diff --git a/agent/conf/log4j-cloud.xml.in b/agent/conf/log4j-cloud.xml.in index 6bc80f084739..093b5dab6b82 100644 --- a/agent/conf/log4j-cloud.xml.in +++ b/agent/conf/log4j-cloud.xml.in @@ -58,7 +58,38 @@ under the License. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/com/cloud/agent/api/Answer.java b/api/src/com/cloud/agent/api/Answer.java index 17bba76e14d0..25a2f5c476b9 100644 --- a/api/src/com/cloud/agent/api/Answer.java +++ b/api/src/com/cloud/agent/api/Answer.java @@ -19,6 +19,7 @@ import com.cloud.utils.exception.ExceptionUtil; public class Answer extends Command { + public AnswerTypeError typeError; protected boolean result; protected String details; @@ -35,6 +36,12 @@ public Answer(final Command command, final boolean success, final String details this.details = details; } + public Answer(final Command command, final boolean success, final String details, final AnswerTypeError type) { + this.typeError = type; + result = success; + this.details = details; + } + public Answer(final Command command, final Exception e) { this(command, false, ExceptionUtil.toString(e)); } @@ -60,6 +67,14 @@ public static UnsupportedAnswer createUnsupportedVersionAnswer(final Command cmd return new UnsupportedAnswer(cmd, "Unsuppored Version."); } + public AnswerTypeError getTypeError() { + return typeError; + } + + public enum AnswerTypeError { + DNS_IO_ERROR + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/api/src/com/cloud/agent/api/to/SrTO.java b/api/src/com/cloud/agent/api/to/SrTO.java new file mode 100644 index 000000000000..7f3ec32f674e --- /dev/null +++ b/api/src/com/cloud/agent/api/to/SrTO.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api.to; + +public class SrTO { + + protected String ref; + + public SrTO(String ref) { + this.ref = ref; + } + + public String getRef() { + return ref; + } + + public void setRef(String ref) { + this.ref = ref; + } +} + diff --git a/api/src/com/cloud/agent/api/to/VirtualNetworkTO.java b/api/src/com/cloud/agent/api/to/VirtualNetworkTO.java new file mode 100644 index 000000000000..1f69599d37a2 --- /dev/null +++ b/api/src/com/cloud/agent/api/to/VirtualNetworkTO.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.agent.api.to; + +public class VirtualNetworkTO { + protected String ref; + + public VirtualNetworkTO(String ref) { + this.ref = ref; + } + + public String getRef() { + return ref; + } + + public void setRef(String ref) { + this.ref = ref; + } +} diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 907b93eca103..0486075ca47d 100644 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -149,7 +149,9 @@ public class EventTypes { // Load Balancers public static final String EVENT_ASSIGN_TO_LOAD_BALANCER_RULE = "LB.ASSIGN.TO.RULE"; + public static final String EVENT_ASSIGN_NETWORK_TO_LOAD_BALANCER_RULE = "LB.ASSIGN.NETWORK.TO.RULE"; public static final String EVENT_REMOVE_FROM_LOAD_BALANCER_RULE = "LB.REMOVE.FROM.RULE"; + public static final String EVENT_REMOVE_NETWORK_FROM_LOAD_BALANCER_RULE = "LB.REMOVE.NETWORK.FROM.RULE"; public static final String EVENT_LOAD_BALANCER_CREATE = "LB.CREATE"; public static final String EVENT_LOAD_BALANCER_DELETE = "LB.DELETE"; public static final String EVENT_LB_STICKINESSPOLICY_CREATE = "LB.STICKINESSPOLICY.CREATE"; @@ -163,6 +165,11 @@ public class EventTypes { 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"; + public static final String EVENT_LB_REGISTER_DNS = "LB.DNS.REGISTRY"; + public static final String EVENT_LB_CREATE_POOL = "LB.POOL.CREATE"; + public static final String EVENT_LB_REMOVE_POOL = "LB.POOL.REMOVE"; + public static final String EVENT_LB_LINK = "LB.LINK"; + public static final String EVENT_LB_UNLINK = "LB.UNLINK"; // Global Load Balancer rules public static final String EVENT_ASSIGN_TO_GLOBAL_LOAD_BALANCER_RULE = "GLOBAL.LB.ASSIGN"; @@ -322,6 +329,9 @@ public class EventTypes { // Host public static final String EVENT_HOST_RECONNECT = "HOST.RECONNECT"; + public static final String EVENT_HOST_CREATE = "HOST.CREATE"; + public static final String EVENT_HOST_DELETE = "HOST.DELETE"; + public static final String EVENT_HOST_UPDATE = "HOST.UPDATE"; // Host Out-of-band management public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_ENABLE = "HOST.OOBM.ENABLE"; @@ -497,6 +507,11 @@ public class EventTypes { public static final String EVENT_AUTOSCALEVMGROUP_ENABLE = "AUTOSCALEVMGROUP.ENABLE"; public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE"; + public static final String EVENT_AUTOSCALEVMGROUP_SCALEUP = "AUTOSCALE.SCALEUP"; + public static final String EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED = "AUTOSCALE.SCALEUP.FAILED"; + public static final String EVENT_AUTOSCALEVMGROUP_SCALEDOWN = "AUTOSCALE.SCALEDOWN"; + public static final String EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED = "AUTOSCALE.SCALEDOWN.FAILED"; + public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD"; public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE"; public static final String EVENT_BAREMETAL_PXE_SERVER_ADD = "PHYSICAL.PXE.ADD"; @@ -796,6 +811,9 @@ public class EventTypes { // Host entityEventDetails.put(EVENT_HOST_RECONNECT, Host.class); + entityEventDetails.put(EVENT_HOST_CREATE, Host.class); + entityEventDetails.put(EVENT_HOST_DELETE, Host.class); + entityEventDetails.put(EVENT_HOST_UPDATE, Host.class); // Host Out-of-band management entityEventDetails.put(EVENT_HOST_OUTOFBAND_MANAGEMENT_ENABLE, Host.class); @@ -947,6 +965,10 @@ public class EventTypes { entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_UPDATE, AutoScaleVmGroup.class); entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_ENABLE, AutoScaleVmGroup.class); entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_DISABLE, AutoScaleVmGroup.class); + entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCALEUP, AutoScaleVmGroup.class); + entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED, AutoScaleVmGroup.class); + entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCALEDOWN, AutoScaleVmGroup.class); + entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED, AutoScaleVmGroup.class); entityEventDetails.put(EVENT_GUEST_VLAN_RANGE_DEDICATE, GuestVlan.class); entityEventDetails.put(EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE, GuestVlan.class); diff --git a/api/src/com/cloud/globodictionary/GloboDictionaryEntity.java b/api/src/com/cloud/globodictionary/GloboDictionaryEntity.java new file mode 100644 index 000000000000..d21e34e00ff1 --- /dev/null +++ b/api/src/com/cloud/globodictionary/GloboDictionaryEntity.java @@ -0,0 +1,34 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.cloud.globodictionary; + +public interface GloboDictionaryEntity extends Comparable { + + String getId(); + + void setId(String id); + + String getName(); + + void setName(String name); + + String getStatus(); + + void setStatus(String status); + + boolean isActive(); +} diff --git a/api/src/com/cloud/globodictionary/GloboDictionaryService.java b/api/src/com/cloud/globodictionary/GloboDictionaryService.java new file mode 100644 index 000000000000..6693cb19d19d --- /dev/null +++ b/api/src/com/cloud/globodictionary/GloboDictionaryService.java @@ -0,0 +1,48 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.cloud.globodictionary; + +import java.util.List; + +public interface GloboDictionaryService { + + enum GloboDictionaryEntityType { + CLIENT("/clientes", "Clients"), BUSINESS_SERVICE("/servicos-de-negocio", "Business Services"), + COMPONENT("/componentes", "Components"), SUB_COMPONENT("/sub-componentes", "Sub-components"), + PRODUCT("/produtos", "Products"); + + private final String uri; + private final String friendlyName; + + GloboDictionaryEntityType(String uri, String friendlyName) { + this.uri = uri; + this.friendlyName = friendlyName; + } + + public String getUri() { + return uri; + } + + public String getFriendlyName() { + return friendlyName; + } + } + + List list(GloboDictionaryEntityType type); + + GloboDictionaryEntity get(GloboDictionaryEntityType type, String id); +} diff --git a/api/src/com/cloud/network/IpAddress.java b/api/src/com/cloud/network/IpAddress.java index 2447809d66f5..3f76c1c8e725 100644 --- a/api/src/com/cloud/network/IpAddress.java +++ b/api/src/com/cloud/network/IpAddress.java @@ -86,6 +86,7 @@ enum Purpose { Long getNetworkId(); + @Override boolean isDisplay(); public Date getRemoved(); diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 75196a469d3e..068f8bdef3f9 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -135,6 +135,10 @@ public static class Provider { // add new Ovs provider public static final Provider Ovs = new Provider("Ovs", false); public static final Provider Opendaylight = new Provider("Opendaylight", false); + + // add GloboNetwork provider + public static final Provider GloboNetwork = new Provider("GloboNetwork", true); + public static final Provider GloboAclApi = new Provider("GloboACLAPI", true); // add Nuage Vsp Providers public static final Provider NuageVsp = new Provider("NuageVsp", false); public static final Provider BrocadeVcs = new Provider("BrocadeVcs", false); diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index d76d65972026..ab68311b75be 100644 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -67,6 +67,7 @@ IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long ne Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; Pair, Integer> searchForNetworks(ListNetworksCmd cmd); + List searchForAllNetworks(ListNetworksCmd cmd); boolean deleteNetwork(long networkId, boolean forced); diff --git a/api/src/com/cloud/network/as/AutoScaleCounter.java b/api/src/com/cloud/network/as/AutoScaleCounter.java index f25d6699a006..0447343f7c43 100644 --- a/api/src/com/cloud/network/as/AutoScaleCounter.java +++ b/api/src/com/cloud/network/as/AutoScaleCounter.java @@ -29,6 +29,9 @@ public static class AutoScaleCounterType { public static final AutoScaleCounterType Netscaler = new AutoScaleCounterType("netscaler"); public static final AutoScaleCounterType Cpu = new AutoScaleCounterType("cpu"); public static final AutoScaleCounterType Memory = new AutoScaleCounterType("memory"); + public static final AutoScaleCounterType ActiveConnections = new AutoScaleCounterType("active_connections"); + public static final AutoScaleCounterType MemoryNoCache = new AutoScaleCounterType("memory_no_cache"); + public AutoScaleCounterType(String name) { _name = name; diff --git a/api/src/com/cloud/network/as/AutoScalePolicy.java b/api/src/com/cloud/network/as/AutoScalePolicy.java index 56ea3f5f7f72..0c3476f63c4f 100644 --- a/api/src/com/cloud/network/as/AutoScalePolicy.java +++ b/api/src/com/cloud/network/as/AutoScalePolicy.java @@ -24,6 +24,10 @@ public interface AutoScalePolicy extends ControlledEntity, InternalIdentity { + enum LogicalOperator{ + OR, AND + } + @Override long getId(); @@ -37,4 +41,8 @@ public interface AutoScalePolicy extends ControlledEntity, InternalIdentity { public String getAction(); + public LogicalOperator getLogicalOperator(); + + public Integer getStep(); + } diff --git a/api/src/com/cloud/network/as/AutoScaleService.java b/api/src/com/cloud/network/as/AutoScaleService.java index 32d693d7b8a4..b98657b6a1c2 100644 --- a/api/src/com/cloud/network/as/AutoScaleService.java +++ b/api/src/com/cloud/network/as/AutoScaleService.java @@ -59,6 +59,8 @@ public interface AutoScaleService { boolean deleteAutoScaleVmGroup(long vmGroupId); + boolean deleteAutoScaleVmGroupWithDependencies(long vmGroupId); + AutoScaleVmGroup updateAutoScaleVmGroup(UpdateAutoScaleVmGroupCmd cmd); AutoScaleVmGroup enableAutoScaleVmGroup(Long id); @@ -78,4 +80,5 @@ public interface AutoScaleService { List listConditions(ListConditionsCmd cmd); boolean deleteCondition(long conditionId) throws ResourceInUseException; + } diff --git a/api/src/com/cloud/network/as/AutoScaleVmGroup.java b/api/src/com/cloud/network/as/AutoScaleVmGroup.java index cf2c15c42166..59c4289ee0c0 100644 --- a/api/src/com/cloud/network/as/AutoScaleVmGroup.java +++ b/api/src/com/cloud/network/as/AutoScaleVmGroup.java @@ -54,7 +54,11 @@ public interface AutoScaleVmGroup extends ControlledEntity, InternalIdentity, Di String getUuid(); + boolean isLocked(); + @Override boolean isDisplay(); + String getVmPrefixName(); + } diff --git a/api/src/com/cloud/network/as/AutoScaleVmProfile.java b/api/src/com/cloud/network/as/AutoScaleVmProfile.java index 495446a6e837..0fb3cc6c2ad3 100644 --- a/api/src/com/cloud/network/as/AutoScaleVmProfile.java +++ b/api/src/com/cloud/network/as/AutoScaleVmProfile.java @@ -49,6 +49,8 @@ public interface AutoScaleVmProfile extends ControlledEntity, InternalIdentity, public long getAutoScaleUserId(); + public String getUserData(); + @Override boolean isDisplay(); diff --git a/api/src/com/cloud/network/as/Counter.java b/api/src/com/cloud/network/as/Counter.java index 286bf517c79d..1a8bbe8a48ec 100644 --- a/api/src/com/cloud/network/as/Counter.java +++ b/api/src/com/cloud/network/as/Counter.java @@ -26,7 +26,11 @@ public static enum Source { netscaler, snmp, cpu, - memory + memory, + cpu_used, + memory_used, + memory_free, + active_connections } String getName(); diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java b/api/src/com/cloud/network/element/UserDataServiceProvider.java index bf6d7e819e91..e3d89b448901 100644 --- a/api/src/com/cloud/network/element/UserDataServiceProvider.java +++ b/api/src/com/cloud/network/element/UserDataServiceProvider.java @@ -34,4 +34,5 @@ boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachinePr boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; boolean saveSSHKey(Network network, NicProfile nic, VirtualMachineProfile vm, String sshPublicKey) throws ResourceUnavailableException; + } diff --git a/api/src/com/cloud/network/lb/LoadBalancingRule.java b/api/src/com/cloud/network/lb/LoadBalancingRule.java index be69332ea780..1d4b83206693 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRule.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRule.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network.lb; +import java.util.ArrayList; import java.util.List; import com.cloud.network.as.AutoScalePolicy; @@ -38,6 +39,16 @@ public class LoadBalancingRule { private List healthCheckPolicies; private LbSslCert sslCert; private String lbProtocol; + private String cache; + private String serviceDownAction; + private String healthCheckDestination; + private String healthCheckType; + private String expectedHealthCheck; + private boolean skipDnsError = false; + private boolean dsr; + + private List additionalNetworks; + private List additionalPortMap; public LoadBalancingRule(LoadBalancer lb, List destinations, List stickinessPolicies, List healthCheckPolicies, Ip sourceIp) { @@ -59,6 +70,13 @@ public LoadBalancingRule(LoadBalancer lb, List destinations, List this.lbProtocol = lbProtocol; } + public List getAllNetworks() { + List ids = new ArrayList<>(additionalNetworks); + ids.add(getNetworkId()); + + return ids; + } + public long getId() { return lb.getId(); } @@ -107,6 +125,18 @@ public String getLbProtocol() { return this.lbProtocol; } + public String getCache() { return this.cache; } + + public void setCache(String cache) { this.cache = cache; } + + public String getServiceDownAction() { + return serviceDownAction; + } + + public void setServiceDownAction(String serviceDownAction) { + this.serviceDownAction = serviceDownAction; + } + public FirewallRule.Purpose getPurpose() { return FirewallRule.Purpose.LoadBalancing; } @@ -143,6 +173,58 @@ public LbSslCert getLbSslCert() { return sslCert; } + public List getAdditionalNetworks() { + return additionalNetworks; + } + + public void setAdditionalNetworks(List additionalNetworks) { + this.additionalNetworks = additionalNetworks; + } + + public List getAdditionalPortMap() { return additionalPortMap; } + + public void setAdditionalPortMap(List additionalPortMap) { this.additionalPortMap = additionalPortMap; } + + public void setHealthCheckDestination(String healthCheckDestination) { + this.healthCheckDestination = healthCheckDestination; + } + + public String getHealthcheckDestination() { + return healthCheckDestination; + } + + public String getHealthCheckType() { + return healthCheckType; + } + + public void setHealthCheckType(String healthCheckType) { + this.healthCheckType = healthCheckType; + } + + public String getExpectedHealthCheck() { + return expectedHealthCheck; + } + + public void setExpectedHealthCheck(String expectedHealthCheck) { + this.expectedHealthCheck = expectedHealthCheck; + } + + public boolean isSkipDnsError() { + return skipDnsError; + } + + public void setSkipDnsError(boolean skipDnsError) { + this.skipDnsError = skipDnsError; + } + + public void setDsr(boolean dsr) { + this.dsr = dsr; + } + + public boolean isDsr() { + return dsr; + } + public interface Destination { String getIpAddress(); @@ -150,7 +232,11 @@ public interface Destination { int getDestinationPortEnd(); + long getNetworkId(); + boolean isRevoked(); + + long getInstanceId(); } public static class LbStickinessPolicy { @@ -244,12 +330,16 @@ public static class LbDestination implements Destination { private int portStart; private int portEnd; private String ip; + private long networkId; + private long instanceId; boolean revoked; - public LbDestination(int portStart, int portEnd, String ip, boolean revoked) { + public LbDestination(int portStart, int portEnd, String ip, long networkId, long instanceId, boolean revoked) { this.portStart = portStart; this.portEnd = portEnd; this.ip = ip; + this.networkId = networkId; + this.instanceId = instanceId; this.revoked = revoked; } @@ -268,6 +358,16 @@ public int getDestinationPortEnd() { return portEnd; } + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getInstanceId() { + return instanceId; + } + @Override public boolean isRevoked() { return revoked; diff --git a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java index 50b39d2f3382..153a70c65505 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network.lb; +import com.cloud.utils.Ternary; import java.util.List; import java.util.Map; @@ -41,18 +42,20 @@ public interface LoadBalancingRulesService { /** * Create a load balancer rule from the given ipAddress/port to the given private port - * @param openFirewall - * TODO - * @param forDisplay TODO * @param cmd * the command specifying the ip address, public port, protocol, private port, and algorithm * + * @param openFirewall + * TODO + * @param forDisplay TODO + * @param skipDnsError * @return the newly created LoadBalancerVO if successful, null otherwise * @throws InsufficientAddressCapacityException */ 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, String lbProtocol, Boolean forDisplay) throws NetworkRuleConflictException, - InsufficientAddressCapacityException; + Long ipAddrId, String protocol, String algorithm, long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol, Boolean forDisplay, List additionalPortMap, String cache, + String serviceDownAction, String healthCheckDestination, String expectedHealthcheck, String healthcheckType, boolean forcedns, boolean dsr) + throws NetworkRuleConflictException, InsufficientAddressCapacityException; LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd); @@ -106,6 +109,13 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri boolean removeCertFromLoadBalancer(long lbRuleId); + + /* + * Assign a network methods + */ + boolean assignNetworksToLoadBalancer(Long loadBalancerId, List networkIds); + boolean removeNetworksFromLoadBalancer(Long loadBalancerId, List networkIds); + /** * List instances that have either been applied to a load balancer or are eligible to be assigned to a load * balancer. @@ -114,7 +124,7 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri * @return list of vm instances that have been or can be applied to a load balancer along with service state, * if the LB has health check policy created on it from cloudstack. */ - Pair, List> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd); + Ternary, List, Integer> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd); /** * List load balancer rules based on the given criteria @@ -148,6 +158,8 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri LoadBalancer findById(long loadBalancer); + LoadBalancer findByUuid(String uuid); + public void updateLBHealthChecks(Scheme scheme) throws ResourceUnavailableException; Map getLbInstances(long lbId); @@ -163,4 +175,17 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri LoadBalancer findLbByStickinessId(long stickinessPolicyId); Long findLBIdByHealtCheckPolicyId(long lbHealthCheckPolicy); + + HealthCheckPolicy validateAndPersistLbHealthcheckPolicy(Long lbRuleId, String pingPath, String description, int timeout, int healthcheckInterval, int healthyThreshold, int unhealthyThreshold, Boolean forDisplay); + + StickinessPolicy validateAndPersistLbStickinessPolicy(Long lbRuleId, String lbStickinessPolicyName, String lbStickinessMethodName, Map paramList, String description, Boolean forDisplay); + + public boolean isLinkedChildLoadBalancer(String lbuuid); + + public boolean isLinkedParentLoadBalancer(String lbuuid); + + public void throwExceptionIfIsChildLoadBalancer(Long id, String operation); + + void throwExceptionIfIsParentLoadBalancer(Long id, String actualCommandName); + void throwExceptionIfIsParentLoadBalancer(List ids, String operation); } diff --git a/api/src/com/cloud/projects/ProjectService.java b/api/src/com/cloud/projects/ProjectService.java index dc882ef11b8d..9fd8530a430d 100644 --- a/api/src/com/cloud/projects/ProjectService.java +++ b/api/src/com/cloud/projects/ProjectService.java @@ -34,10 +34,16 @@ public interface ProjectService { * - account name of the project owner * @param domainId * - domainid of the project owner + * @param businessServiceId + * @param clientId + * @param componentId + * @param subComponentId + * @param productId + * @param detailedUsage * @return the project if created successfully, null otherwise * @throws ResourceAllocationException */ - Project createProject(String name, String displayText, String accountName, Long domainId) throws ResourceAllocationException; + Project createProject(String name, String displayText, String accountName, Long domainId, String businessServiceId, String clientId, String componentId, String subComponentId, String productId, Boolean detailedUsage) throws ResourceAllocationException; /** * Deletes a project @@ -67,7 +73,7 @@ public interface ProjectService { Project findByNameAndDomainId(String name, long domainId); - Project updateProject(long id, String displayText, String newOwnerName) throws ResourceAllocationException; + Project updateProject(long id, String displayText, String newOwnerName, String businessServiceId, String clientId, String componentId, String subComponentId, String productId, Boolean detailedUsage) throws ResourceAllocationException; boolean addAccountToProject(long projectId, String accountName, String email); diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java index 0bd5d734e30e..75e436c7921a 100644 --- a/api/src/com/cloud/server/ResourceTag.java +++ b/api/src/com/cloud/server/ResourceTag.java @@ -55,7 +55,7 @@ public enum ResourceObjectType { User(true, true), DiskOffering(false, true), AutoScaleVmProfile(false, true), - AutoScaleVmGroup(false, true), + AutoScaleVmGroup(true, true), LBStickinessPolicy(false, true), LBHealthCheckPolicy(false, true), SnapshotPolicy(false, true), diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 03ee7fc1b20b..4afd837f7eb3 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -25,6 +25,7 @@ public class ApiConstants { public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALLOCATED_ONLY = "allocatedonly"; + public static final String ADDITIONAL_PORT_MAP = "additionalportmap"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; public static final String ASYNC_BACKUP = "asyncbackup"; @@ -38,6 +39,11 @@ public class ApiConstants { public static final String BIND_PASSWORD = "bindpass"; public static final String BYTES_READ_RATE = "bytesreadrate"; public static final String BYTES_WRITE_RATE = "byteswriterate"; + public static final String CACHE = "cache"; + public static final String SERVICE_DOWN_ACTION = "servicedownactionid"; + public static final String HEALTHCHECK_DESTINATION = "healthcheckdestination"; + public static final String HEALTHCHECK_TYPE = "healthchecktype"; + public static final String EXPECTED_HEALTHCHECK = "expectedhealthcheck"; public static final String BYPASS_VLAN_OVERLAP_CHECK = "bypassvlanoverlapcheck"; public static final String CATEGORY = "category"; public static final String CAN_REVERT = "canrevert"; @@ -178,6 +184,7 @@ public class ApiConstants { public static final String ISO_GUEST_OS_NONE = "None"; public static final String JOB_ID = "jobid"; public static final String JOB_STATUS = "jobstatus"; + public static final String CONTEXT = "context"; public static final String LASTNAME = "lastname"; public static final String LEVEL = "level"; public static final String LENGTH = "length"; @@ -185,6 +192,10 @@ public class ApiConstants { public static final String LOCK = "lock"; public static final String LUN = "lun"; public static final String LBID = "lbruleid"; + public static final String LB_CHILD_LBID = "childlbid"; + public static final String LB_PARENT_LBID = "parentlbid"; + public static final String LB_LINKED_PARENT_LOAD_BALANCER = "linkedparent"; + public static final String LB_LINKED_CHILDREN_LOAD_BALANCER = "linkedchildren"; public static final String MAX = "max"; public static final String MAC_ADDRESS = "macaddress"; public static final String MAX_SNAPS = "maxsnaps"; @@ -203,6 +214,7 @@ public class ApiConstants { public static final String OP = "op"; public static final String OS_CATEGORY_ID = "oscategoryid"; public static final String OS_TYPE_ID = "ostypeid"; + public static final String OS_TYPE_NAME = "ostypename"; public static final String OS_DISPLAY_NAME = "osdisplayname"; public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor"; public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate"; @@ -247,6 +259,7 @@ public class ApiConstants { public static final String RECOVER = "recover"; public static final String REQUIRES_HVM = "requireshvm"; public static final String RESOURCE_TYPE = "resourcetype"; + public static final String RESOURCE_KEY = "resourcekey"; public static final String RESOURCE_TYPE_NAME = "resourcetypename"; public static final String RESPONSE = "response"; public static final String REVERTABLE = "revertable"; @@ -315,6 +328,7 @@ public class ApiConstants { public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap"; + public static final String VIP_ID = "vipid"; public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount"; public static final String USAGE_ID = "usageid"; public static final String USAGE_TYPE = "usagetype"; @@ -595,10 +609,15 @@ public class ApiConstants { public static final String INTERVAL = "interval"; public static final String QUIETTIME = "quiettime"; public static final String ACTION = "action"; + public static final String LOGICAL_OPERATOR = "logicaloperator"; + public static final String STEP = "step"; public static final String CONDITION_ID = "conditionid"; public static final String CONDITION_IDS = "conditionids"; public static final String COUNTERPARAM_LIST = "counterparam"; public static final String AUTOSCALE_USER_ID = "autoscaleuserid"; + public static final String AUTOSCALE_PROFILE_REMOVE_NETWORKS = "removenetworks"; + public static final String AUTOSCALE_GROUP_COUNT_MEMBERS = "autoscalegroupcountmembers"; + public static final String AUTOSCALE_GROUP_VM_PREFIX_NAME = "autoscalegroupvmprefixname"; public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; public static final String BAREMETAL_RCT_URL = "baremetalrcturl"; public static final String UCS_DN = "ucsdn"; @@ -693,6 +712,19 @@ public class ApiConstants { public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans"; public static final String METADATA = "metadata"; public static final String PHYSICAL_SIZE = "physicalsize"; + + public static final String GURU_NAME = "guruname"; + public static final String GLOBO_RESOURCE_CONFIG = "globoresourceconfig"; + public static final String L4_PROTOCOL = "l4protocol"; + public static final String L7_PROTOCOL = "l7protocol"; + + public static final String BUSINESS_SERVICE_ID = "businessserviceid"; + public static final String CLIENT_ID = "clientid"; + public static final String COMPONENT_ID = "componentid"; + public static final String SUB_COMPONENT_ID = "subcomponentid"; + public static final String PRODUCT_ID = "productid"; + public static final String DETAILED_USAGE = "detailedusage"; + public static final String OVM3_POOL = "ovm3pool"; public static final String OVM3_CLUSTER = "ovm3cluster"; public static final String OVM3_VIP = "ovm3vip"; @@ -715,7 +747,6 @@ public class ApiConstants { public static final String LAST_ANNOTATED = "lastannotated"; public static final String LDAP_DOMAIN = "ldapdomain"; - public enum HostDetails { all, capacity, events, stats, min; } diff --git a/api/src/org/apache/cloudstack/api/BaseListCmd.java b/api/src/org/apache/cloudstack/api/BaseListCmd.java index 36fa36fcfc9a..e242aa8a4d85 100644 --- a/api/src/org/apache/cloudstack/api/BaseListCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseListCmd.java @@ -142,4 +142,12 @@ public void validateSpecificParameters(final Map params){ throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified"); } } + + public void setPage(Integer page) { + this.page = page; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } } diff --git a/api/src/org/apache/cloudstack/api/BaseResponse.java b/api/src/org/apache/cloudstack/api/BaseResponse.java index 45016c1a2a26..13d4f01aec24 100644 --- a/api/src/org/apache/cloudstack/api/BaseResponse.java +++ b/api/src/org/apache/cloudstack/api/BaseResponse.java @@ -19,6 +19,7 @@ import com.google.gson.annotations.SerializedName; import com.cloud.serializer.Param; +import org.apache.cloudstack.context.CallContext; public abstract class BaseResponse implements ResponseObject { private transient String responseName; @@ -32,6 +33,10 @@ public abstract class BaseResponse implements ResponseObject { @Param(description = "the current status of the latest async job acting on this object") private Integer jobStatus; + @SerializedName(ApiConstants.CONTEXT) + @Param(description = "context error") + private String context; + public BaseResponse() { } @@ -83,4 +88,20 @@ public Integer getJobStatus() { public void setJobStatus(Integer jobStatus) { this.jobStatus = jobStatus; } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + + public void buildCurrentContext() { + CallContext context = CallContext.current(); + if (context != null) { + setContext(context.getNdcContext()); + } + } } diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 4fb248cd1055..cfaee3e51f2f 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -22,6 +22,8 @@ import java.util.Map; import java.util.Set; +import javax.servlet.http.HttpSession; + import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants.HostDetails; @@ -64,6 +66,7 @@ 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.LoginCmdResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; @@ -461,5 +464,7 @@ List createTemplateResponses(ResponseView view, VirtualMachine ListResponse createUpgradeRouterTemplateResponse(List jobIds); + LoginCmdResponse createLoginResponse(HttpSession session); + SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java b/api/src/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java index 1bece181fb85..f0dc4e40e21b 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java +++ b/api/src/org/apache/cloudstack/api/command/admin/loadbalancer/ListLoadBalancerRuleInstancesCmdByAdmin.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.loadbalancer; +import com.cloud.utils.Ternary; import java.util.ArrayList; import java.util.List; @@ -30,7 +31,7 @@ import org.apache.cloudstack.api.response.UserVmResponse; import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; +//import com.cloud.utils.Pair; @APICommand(name = "listLoadBalancerRuleInstances", description = "List all virtual machine instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Full, requestHasSensitiveInfo = false, @@ -42,10 +43,10 @@ public class ListLoadBalancerRuleInstancesCmdByAdmin extends ListLoadBalancerRul @Override public void execute(){ - Pair, List> vmServiceMap = _lbService.listLoadBalancerInstances(this); + Ternary, List, Integer> vmServiceMap = _lbService.listLoadBalancerInstances(this); List result = vmServiceMap.first(); List serviceStates = vmServiceMap.second(); - + Integer count = vmServiceMap.third(); if (!isListLbVmip()) { // list lb instances @@ -59,7 +60,7 @@ public void execute(){ vmResponses.get(i).setServiceState(serviceStates.get(i)); } } - response.setResponses(vmResponses); + response.setResponses(vmResponses,count); response.setResponseName(getCommandName()); setResponseObject(response); @@ -87,7 +88,7 @@ public void execute(){ } lbRes.setResponseName(getCommandName()); - lbRes.setResponses(listlbVmRes); + lbRes.setResponses(listlbVmRes, count); setResponseObject(lbRes); } } diff --git a/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java index 413818b8242b..9a6b7b59b2aa 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java @@ -17,8 +17,6 @@ package org.apache.cloudstack.api.command.admin.region; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -28,6 +26,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.PortableIpRangeResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.user.Account; diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java index 71d6a661af33..18842dbf4da6 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java @@ -32,7 +32,7 @@ import com.cloud.user.User; @APICommand(name = "createUser", description = "Creates a user for an account that already exists", responseObject = UserResponse.class, - requestHasSensitiveInfo = true, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, entityType = { Account.class }) public class CreateUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(CreateUserCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java index 08ba521ed62c..f90a48043db3 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java @@ -35,7 +35,7 @@ import com.cloud.user.User; @APICommand(name = "deleteUser", description = "Deletes a user for an account", responseObject = SuccessResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, entityType = { Account.class }) public class DeleteUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(DeleteUserCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java index c6e09ef0f9e6..3a9676e24665 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/DisableUserCmd.java @@ -37,7 +37,7 @@ import com.cloud.user.UserAccount; @APICommand(name = "disableUser", description = "Disables a user account", responseObject = UserResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = { Account.class }) public class DisableUserCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(DisableUserCmd.class.getName()); private static final String s_name = "disableuserresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java index d69eccf9342b..e4f4477103cc 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/EnableUserCmd.java @@ -35,7 +35,7 @@ import com.cloud.user.UserAccount; @APICommand(name = "enableUser", description = "Enables a user account", responseObject = UserResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = { Account.class }) public class EnableUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(EnableUserCmd.class.getName()); private static final String s_name = "enableuserresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java index d58f69596328..51fa8eed0a9b 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/GetUserCmd.java @@ -25,10 +25,11 @@ import org.apache.cloudstack.api.response.UserResponse; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; import com.cloud.user.UserAccount; @APICommand(name = "getUser", description = "Find user account by API key", responseObject = UserResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = { Account.class }) public class GetUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(GetUserCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java index c0c2b24814aa..14cbb26602ec 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/ListUsersCmd.java @@ -25,8 +25,10 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserResponse; +import com.cloud.user.Account; + @APICommand(name = "listUsers", description = "Lists user accounts", responseObject = UserResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = { Account.class }) public class ListUsersCmd extends BaseListAccountResourcesCmd { public static final Logger s_logger = Logger.getLogger(ListUsersCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java index e6ac36719e33..ac49c76cbbb3 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java @@ -34,7 +34,7 @@ import com.cloud.user.UserAccount; @APICommand(name = "updateUser", description = "Updates a user account", responseObject = UserResponse.class, - requestHasSensitiveInfo = true, responseHasSensitiveInfo = true) + requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, entityType = { Account.class }) public class UpdateUserCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpdateUserCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java b/api/src/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java index 03bb4c623d0d..fe457038cf0f 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java @@ -61,8 +61,8 @@ public void execute(){ message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); } } - s_logger.info(ex); - s_logger.info(message.toString(), ex); + s_logger.error(ex); + s_logger.error(message.toString(), ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); } } else { diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java index 6a2b491f2f45..9d393dc480cc 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java @@ -63,6 +63,12 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { description = "the duration for which the conditions have to be true before action is taken") private int duration; + @Parameter(name = ApiConstants.STEP, + type = CommandType.INTEGER, + required = false, + description = "the number of vms that will be created/destroyed") + private Integer step = 1; + @Parameter(name = ApiConstants.QUIETTIME, type = CommandType.INTEGER, description = "the cool down period for which the policy should not be evaluated after the action has been taken") @@ -76,6 +82,11 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { description = "the list of IDs of the conditions that are being evaluated on every interval") private List conditionIds; + @Parameter(name = ApiConstants.LOGICAL_OPERATOR, + type = CommandType.STRING, + description = "logical operator to be used between all of the conditions") + private String logicalOperator = AutoScalePolicy.LogicalOperator.AND.name(); + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -95,10 +106,18 @@ public String getAction() { return action; } + public Integer getStep() { + return step; + } + public List getConditionIds() { return conditionIds; } + public String getLogicalOperator() { + return logicalOperator; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java index 0308c1710c33..94dc8a235c1f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java @@ -98,6 +98,13 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { description = "the autoscale profile that contains information about the vms in the vm group.") private long profileId; + + @Parameter(name = ApiConstants.AUTOSCALE_GROUP_VM_PREFIX_NAME, + type = CommandType.STRING, + required = true, + description = "the prefix name of the AutoScale group vms") + private String vmPrefixName; + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the group to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @@ -133,6 +140,10 @@ public long getLbRuleId() { return lbRuleId; } + public String getVmPrefixName() { + return vmPrefixName; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java index 447085e79b5a..4c3a41875715 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java @@ -17,8 +17,10 @@ package org.apache.cloudstack.api.command.user.autoscale; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.RoleType; @@ -34,6 +36,7 @@ import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; @@ -100,9 +103,29 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { description = "the ID of the user used to launch and destroy the VMs") private Long autoscaleUserId; + @Parameter(name = ApiConstants.NETWORK_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = NetworkResponse.class, + required = false, + description = "list of additional networks (besides the LB network) to be added to the VMs") + private List networkIds; + + @Parameter(name = ApiConstants.AUTOSCALE_PROFILE_REMOVE_NETWORKS, type = CommandType.BOOLEAN, description = "an optional field, all additional networks will be removed if true") + private boolean removeNetworks; + + @Parameter(name = ApiConstants.USER_DATA, + type = CommandType.STRING, + description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", + length = 32768) + private String userData; + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "The project on which the profile will be created") + private Long projectId; + private Map otherDeployParamMap; // /////////////////////////////////////////////////// @@ -113,6 +136,10 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { private Long accountId; public Long getDomainId() { + if(projectId != null){ + Long accountId = _accountService.finalyzeAccountId(null, null, projectId, true); + return _accountService.getAccount(accountId).getDomainId(); + } if (domainId == null) { getAccountId(); } @@ -165,6 +192,9 @@ public Integer getDestroyVmGraceperiod() { } public long getAccountId() { + if(projectId != null){ + return _accountService.finalyzeAccountId(null, null, projectId, true); + } if (accountId != null) { return accountId; } @@ -180,6 +210,18 @@ public long getAccountId() { return accountId; } + public List getNetworkIds() { + return networkIds; + } + + public boolean isRemoveNetworks() { + return removeNetworks; + } + + public String getUserData() { + return userData; + } + private void createOtherDeployParamMap() { if (otherDeployParamMap == null) { otherDeployParamMap = new HashMap(); @@ -249,6 +291,10 @@ public ApiCommandJobType getInstanceType() { return ApiCommandJobType.AutoScaleVmProfile; } + public Long getProjectId() { + return projectId; + } + @Override public void execute() { AutoScaleVmProfile result = _entityMgr.findById(AutoScaleVmProfile.class, getEntityId()); diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java index 847866ef3ae5..cfa788864f74 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.autoscale; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -60,6 +61,9 @@ public class CreateConditionCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID of the account.") private Long domainId; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "The project on which the condition will be created") + private Long projectId; + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -138,7 +142,12 @@ public String getEventType() { @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); + Long accountId = null; + if(projectId != null ){ + accountId = _accountService.finalyzeAccountId(null, null, projectId, true); + }else{ + accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); + } if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -146,4 +155,7 @@ public long getEntityOwnerId() { return accountId; } + public Long getProjectId() { + return projectId; + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java index a9d2f4e23299..c6518c2ae467 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java @@ -52,6 +52,9 @@ public class DeleteAutoScaleVmGroupCmd extends BaseAsyncCmd { description = "the ID of the autoscale group") private Long id; + @Parameter(name = "removedependencies", type = CommandType.BOOLEAN, required = false, description = "remove all dependencies related to Autoscale VM Group") + private Boolean removeDependencies; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -60,6 +63,8 @@ public Long getId() { return id; } + public Boolean getRemoveDependencies() { return removeDependencies; } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -93,7 +98,14 @@ public String getEventDescription() { @Override public void execute() { CallContext.current().setEventDetails("AutoScale Vm Group Id: " + getId()); - boolean result = _autoScaleService.deleteAutoScaleVmGroup(id); + boolean result = false; + if (getRemoveDependencies() == null || !getRemoveDependencies()) { + // Remove autoscale VM group, but preserve all dependencies - i.e. profiles, policies, conditions + result = _autoScaleService.deleteAutoScaleVmGroup(id); + } else { + // Remove autoscale VM group and all profiles, policies and conditions associated to it + result = _autoScaleService.deleteAutoScaleVmGroupWithDependencies(id); + } if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java index c2b244f2a7fe..e41cf1d09ae0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScalePoliciesCmd.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -57,6 +58,9 @@ public class ListAutoScalePoliciesCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.VMGROUP_ID, type = CommandType.UUID, entityType = AutoScaleVmGroupResponse.class, description = "the ID of the autoscale vm group") private Long vmGroupId; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the Project ID of the policy") + private Long projectId; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -77,6 +81,13 @@ public Long getVmGroupId() { return vmGroupId; } + public Long getProjectId() { + if(projectId != null){ + return _accountService.finalyzeAccountId(null, null, projectId, true); + } + return null; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java index 37c90b9bfdb3..12d7d5856d57 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmGroupsCmd.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.RoleType; @@ -65,6 +66,9 @@ public class ListAutoScaleVmGroupsCmd extends BaseListProjectAndAccountResources @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the Project ID of the auto scale group") + private Long projectId; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -89,6 +93,13 @@ public Long getZoneId() { return zoneId; } + public Long getProjectId() { + if(projectId != null){ + return _accountService.finalyzeAccountId(null, null, projectId, true); + } + return null; + } + @Override public Boolean getDisplay() { if (display != null) { diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java index 554b48854a49..11c927ad9d92 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListAutoScaleVmProfilesCmd.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.RoleType; @@ -63,6 +64,9 @@ public class ListAutoScaleVmProfilesCmd extends BaseListProjectAndAccountResourc @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the Project ID of the Vm profile") + private Long projectId; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -95,6 +99,13 @@ public Boolean getDisplay() { return super.getDisplay(); } + public Long getProjectId() { + if(projectId != null){ + return _accountService.finalyzeAccountId(null, null, projectId, true); + } + return null; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java index d75d64942f90..8243998d4248 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/ListConditionsCmd.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -56,6 +57,9 @@ public class ListConditionsCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.POLICY_ID, type = CommandType.UUID, entityType = AutoScalePolicyResponse.class, description = "the ID of the policy") private Long policyId; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the Project ID of the condition") + private Long projectId; + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -92,6 +96,13 @@ public Long getPolicyId() { return policyId; } + public Long getProjectId() { + if(projectId != null){ + return _accountService.finalyzeAccountId(null, null, projectId, true); + } + return null; + } + @Override public String getCommandName() { return s_name; diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java index 24858f4656b9..a016e6ec274d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScalePolicyCmd.java @@ -72,6 +72,17 @@ public class UpdateAutoScalePolicyCmd extends BaseAsyncCmd { description = "the ID of the autoscale policy") private Long id; + @Parameter(name = ApiConstants.STEP, + type = CommandType.INTEGER, + required = false, + description = "the number of vms that will be created/destroyed") + private Integer step = 1; + + @Parameter(name = ApiConstants.LOGICAL_OPERATOR, + type = CommandType.STRING, + description = "logical operator to be used between all of the conditions") + private String logicalOperator; + @Override public void execute() { CallContext.current().setEventDetails("AutoScale Policy Id: " + getId()); @@ -105,6 +116,14 @@ public List getConditionIds() { return conditionIds; } + public Integer getStep() { + return step; + } + + public String getLogicalOperator() { + return logicalOperator; + } + @Override public String getCommandName() { return s_name; diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java index deebe2ecc9a4..b73604e606ea 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java @@ -17,8 +17,10 @@ package org.apache.cloudstack.api.command.user.autoscale; +import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.RoleType; @@ -81,6 +83,23 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd { description = "the ID of the user used to launch and destroy the VMs") private Long autoscaleUserId; + @Parameter(name = ApiConstants.NETWORK_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = NetworkResponse.class, + required = false, + description = "list of additional networks (besides the LB network) to be added to the VMs") + private List networkIds; + + @Parameter(name = ApiConstants.AUTOSCALE_PROFILE_REMOVE_NETWORKS, type = CommandType.BOOLEAN, description = "an optional field, all additional networks will be removed if true") + private boolean removeNetworks; + + @Parameter(name = ApiConstants.USER_DATA, + type = CommandType.STRING, + description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", + length = 32768) + private String userData; + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the profile to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @@ -129,6 +148,18 @@ public Boolean getDisplay() { return display; } + public List getNetworkIds() { + return networkIds; + } + + public boolean isRemoveNetworks() { + return removeNetworks; + } + + public String getUserData() { + return userData; + } + @Override public String getEventType() { return EventTypes.EVENT_AUTOSCALEVMPROFILE_UPDATE; diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java index fab7d9eef29a..3d655e671a85 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java @@ -55,16 +55,16 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal // /////////////////////////////////////////////////// @Parameter(name = ApiConstants.IP_ADDRESS_ID, - type = CommandType.UUID, - entityType = IPAddressResponse.class, - required = true, - description = "the IP address id of the port forwarding rule") + type = CommandType.UUID, + entityType = IPAddressResponse.class, + required = true, + description = "the IP address id of the port forwarding rule") private Long ipAddressId; @Parameter(name = ApiConstants.PROTOCOL, - type = CommandType.STRING, - required = true, - description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + type = CommandType.STRING, + required = true, + description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") private String protocol; @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") @@ -310,7 +310,7 @@ public Integer getIcmpType() { if (icmpType != null) { return icmpType; } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { - return -1; + return -1; } return null; diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmd.java new file mode 100644 index 000000000000..0c848fbd8729 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmd.java @@ -0,0 +1,100 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseDictionaryCmd extends BaseCmd { + + @Inject + protected GloboDictionaryService globoDictionaryService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.STRING, description = "the ID of the object being listed") + private String id; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + ListResponse response = new ListResponse<>(); + List globoDictionaryResponses = new ArrayList<>(); + + if(id != null){ + GloboDictionaryEntity component = globoDictionaryService.get(this.getEntity(), id); + if(component != null){ + globoDictionaryResponses.add(createResponse(component)); + } + }else { + List components = globoDictionaryService.list(this.getEntity()); + for (GloboDictionaryEntity component : components) { + globoDictionaryResponses.add(createResponse(component)); + } + } + + response.setResponses(globoDictionaryResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String getCommandName() { + return null; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + private GloboDictionaryResponse createResponse(GloboDictionaryEntity component) { + GloboDictionaryResponse globoDictionaryResponse = new GloboDictionaryResponse(component.getId(), component.getName()); + globoDictionaryResponse.setObjectName(this.getResponseName()); + return globoDictionaryResponse; + } + + abstract GloboDictionaryService.GloboDictionaryEntityType getEntity(); + + abstract String getResponseName(); + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListBusinessServicesCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListBusinessServicesCmd.java new file mode 100644 index 000000000000..4e2a90f2334c --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListBusinessServicesCmd.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; + +@APICommand(name = "listBusinessServices", description = "Lists business services", responseObject = GloboDictionaryResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListBusinessServicesCmd extends BaseDictionaryCmd { + + private static final String s_name = "listbusinessservicesresponse"; + private static final String response_name = "businessservice"; + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + String getResponseName() { + return response_name; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListClientsCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListClientsCmd.java new file mode 100644 index 000000000000..9be5927e5c21 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListClientsCmd.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; + +@APICommand(name = "listClients", description = "Lists clients", responseObject = GloboDictionaryResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListClientsCmd extends BaseDictionaryCmd { + + private static final String s_name = "listclientsresponse"; + private static final String response_name = "client"; + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.CLIENT; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + String getResponseName() { + return response_name; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListComponentsCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListComponentsCmd.java new file mode 100644 index 000000000000..4775df4e6400 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListComponentsCmd.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; + +@APICommand(name = "listComponents", description = "Lists components", responseObject = GloboDictionaryResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListComponentsCmd extends BaseDictionaryCmd { + + private static final String s_name = "listcomponentsresponse"; + private static final String response_name = "component"; + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.COMPONENT; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + String getResponseName() { + return response_name; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListProductsCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListProductsCmd.java new file mode 100644 index 000000000000..7ac2ca169f8b --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListProductsCmd.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; + +@APICommand(name = "listProducts", description = "Lists products", responseObject = GloboDictionaryResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListProductsCmd extends BaseDictionaryCmd { + + private static final String s_name = "listproductsresponse"; + private static final String response_name = "product"; + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.PRODUCT; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + String getResponseName() { + return response_name; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListSubComponentsCmd.java b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListSubComponentsCmd.java new file mode 100644 index 000000000000..ba8f11166cb9 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/globodictionary/ListSubComponentsCmd.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; + +@APICommand(name = "listSubComponents", description = "Lists sub-components", responseObject = GloboDictionaryResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListSubComponentsCmd extends BaseDictionaryCmd { + + private static final String s_name = "listsubcomponentsresponse"; + private static final String response_name = "subcomponent"; + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.SUB_COMPONENT; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + String getResponseName() { + return response_name; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignNetworksToLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignNetworksToLoadBalancerRuleCmd.java new file mode 100644 index 000000000000..108df11a5177 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignNetworksToLoadBalancerRuleCmd.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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 java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import com.cloud.utils.StringUtils; + +@APICommand(name = "assignNetworksToLoadBalancerRule", description = "Assigns network or a list of networks to a load balancer rule.", responseObject = SuccessResponse.class) +public class AssignNetworksToLoadBalancerRuleCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AssignNetworksToLoadBalancerRuleCmd.class.getName()); + + private static final String s_name = "assignnetworkstoloadbalancerruleresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the load balancer rule") + private Long id; + + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the list of IDs of the networks that are being assigned to the load balancer rule") + private List networkIds; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getLoadBalancerId() { + return id; + } + + public List getNetworkIds() { + return networkIds; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLoadBalancerId()); + if (lb == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return lb.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ASSIGN_NETWORK_TO_LOAD_BALANCER_RULE; + } + + @Override + public String getEventDescription() { + return "applying networks for load balancer: " + getLoadBalancerId() + " (ids: " + StringUtils.join(getNetworkIds(), ",") + ")"; + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(id, getActualCommandName()); + + CallContext.current().setEventDetails("Load balancer Id: " + getLoadBalancerId() + " Network ids: " + StringUtils.join(getNetworkIds(), ",")); + boolean result = _lbService.assignNetworksToLoadBalancer(getLoadBalancerId(), getNetworkIds()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign load balancer rule"); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _lbService.findById(id); + if (lb == null) { + throw new InvalidParameterValueException("Unable to find load balancer rule: " + id); + } + return lb.getNetworkId(); + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java index 1e7303708014..dd8c96239324 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; @@ -163,17 +162,11 @@ public Map> getVmIdIpListMap() { @Override public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(id, getActualCommandName()); CallContext.current().setEventDetails("Load balancer Id: " + getLoadBalancerId() + " VmIds: " + StringUtils.join(getVirtualMachineIds(), ",")); Map> vmIdIpsMap = getVmIdIpListMap(); - boolean result = false; - - try { - result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds, vmIdIpsMap); - }catch (CloudRuntimeException ex) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign load balancer rule"); - } - + boolean result = _lbService.assignToLoadBalancer(getLoadBalancerId(), virtualMachineIds, vmIdIpsMap); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); @@ -195,4 +188,9 @@ public Long getSyncObjId() { } return lb.getNetworkId(); } + + + public void setVirtualMachineIds(List virtualMachineIds) { + this.virtualMachineIds = virtualMachineIds; + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java index 94c5324c23e2..8a790307b981 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBHealthCheckPolicyCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.loadbalancer; +import java.util.Objects; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -159,24 +160,18 @@ public int getUnhealthyThreshold() { @Override public void execute() throws ResourceAllocationException, ResourceUnavailableException { - HealthCheckPolicy policy = null; - boolean success = false; - - try { - CallContext.current().setEventDetails("Load balancer health check policy ID : " + getEntityId()); - success = _lbService.applyLBHealthCheckPolicy(this); - if (success) { - // State might be different after the rule is applied, so get new object here - policy = _entityMgr.findById(HealthCheckPolicy.class, getEntityId()); - LoadBalancer lb = _lbService.findById(policy.getLoadBalancerId()); - LBHealthCheckResponse hcResponse = _responseGenerator.createLBHealthCheckPolicyResponse(policy, lb); - setResponseObject(hcResponse); - hcResponse.setResponseName(getCommandName()); - } - } finally { - if (!success || (policy == null)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create health check policy"); - } + HealthCheckPolicy policy; + boolean success; + + CallContext.current().setEventDetails("Load balancer healthcheck policy Id : " + getEntityId()); + success = _lbService.applyLBHealthCheckPolicy(this); + if (success) { + // State might be different after the rule is applied, so get new object here + policy = _entityMgr.findById(HealthCheckPolicy.class, getEntityId()); + LoadBalancer lb = _lbService.findById(policy.getLoadBalancerId()); + LBHealthCheckResponse hcResponse = _responseGenerator.createLBHealthCheckPolicyResponse(policy, lb); + setResponseObject(hcResponse); + hcResponse.setResponseName(getCommandName()); } } @@ -201,4 +196,34 @@ public String getEventType() { public String getEventDescription() { return "Create load balancer health check policy"; } + + + public void setPingPath(String pingPath) { + this.pingPath = pingPath; + } + + public void setLbRuleId(Long lbRuleId) { + this.lbRuleId = lbRuleId; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CreateLBHealthCheckPolicyCmd that = (CreateLBHealthCheckPolicyCmd) o; + return Objects.equals(responsTimeOut, that.responsTimeOut) && + Objects.equals(healthCheckInterval, that.healthCheckInterval) && + Objects.equals(healthyThreshold, that.healthyThreshold) && + Objects.equals(unhealthyThreshold, that.unhealthyThreshold) && + Objects.equals(lbRuleId, that.lbRuleId) && + Objects.equals(description, that.description) && + Objects.equals(pingPath, that.pingPath) && + Objects.equals(display, that.display); + } + + @Override + public int hashCode() { + return Objects.hash(lbRuleId, description, pingPath, responsTimeOut, healthCheckInterval, healthyThreshold, unhealthyThreshold, display); + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java index 45e6f81a0aa3..1e94845f22a3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java @@ -18,6 +18,7 @@ import java.util.Map; +import java.util.Objects; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -192,4 +193,34 @@ public Long getSyncObjId() { } return lb.getNetworkId(); } + + public void setLbRuleId(Long lbRuleId) { + this.lbRuleId = lbRuleId; + } + + public void setStickinessMethodName(String stickinessMethodName) { + this.stickinessMethodName = stickinessMethodName; + } + + public void setLbStickinessPolicyName(String lbStickinessPolicyName) { + this.lbStickinessPolicyName = lbStickinessPolicyName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CreateLBStickinessPolicyCmd that = (CreateLBStickinessPolicyCmd) o; + return Objects.equals(lbRuleId, that.lbRuleId) && + Objects.equals(description, that.description) && + Objects.equals(lbStickinessPolicyName, that.lbStickinessPolicyName) && + Objects.equals(stickinessMethodName, that.stickinessMethodName) && + Objects.equals(paramList, that.paramList) && + Objects.equals(display, that.display); + } + + @Override + public int hashCode() { + return Objects.hash(lbRuleId, description, lbStickinessPolicyName, stickinessMethodName, paramList, display); + } } 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 f9367652bdbd..ad50614c415b 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 @@ -120,6 +120,30 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.ADDITIONAL_PORT_MAP, type = CommandType.LIST, collectionType = CommandType.STRING, description = "additional port mappings for load balancing rule", since = "4.4") + private List additionalPortMap; + + @Parameter(name = ApiConstants.CACHE, type = CommandType.STRING, description = "Cache group associated to Load Balancer") + private String cache; + + @Parameter(name = ApiConstants.SERVICE_DOWN_ACTION, type = CommandType.STRING, description = "ID of the action to be executed when service is down") + private String serviceDownAction; + + @Parameter(name = ApiConstants.HEALTHCHECK_DESTINATION, type = CommandType.STRING, description = "Port to be used as health check alternative to the service port (optional)") + private String healthCheckDestination; + + @Parameter(name = ApiConstants.HEALTHCHECK_TYPE, type = CommandType.STRING, description = "Healthcheck type (TCP, UDP, HTTP or HTTPS)") + protected String healthCheckType; + + @Parameter(name = ApiConstants.EXPECTED_HEALTHCHECK, type = CommandType.STRING, description = "Expected healthcheck string to check if is in service.") + private String expectedHealthCheck; + + @Parameter(name = "skipdnserror", type = CommandType.BOOLEAN, description = "when false if dns integration failed, the loadbalancer will not be created and throw an exception, else lb will be created and dns it could be register later") + private boolean skipDnsError = false ; + + @Parameter(name = "dsr", type = CommandType.BOOLEAN, description = "when true creates the load balancing using DSR (Direct server return)") + private boolean dsr = false ; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -223,6 +247,16 @@ public long getNetworkId() { } } + public String getCache() { return cache; } + + public String getServiceDownAction() { + return serviceDownAction; + } + + public String getHealthCheckDestination() { + return healthCheckDestination; + } + public Integer getPublicPort() { return publicPort; } @@ -231,6 +265,8 @@ public String getName() { return loadBalancerRuleName; } + public List getAdditionalPortMap() { return additionalPortMap; } + public Boolean getOpenFirewall() { boolean isVpc = getVpcId() == null ? false : true; if (openFirewall != null) { @@ -278,18 +314,26 @@ public void execute() throws ResourceAllocationException, ResourceUnavailableExc if (getOpenFirewall()) { success = success && _firewallService.applyIngressFirewallRules(getSourceIpAddressId(), callerContext.getCallingAccount()); + if(!success) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to apply firewall rules."); + } } // State might be different after the rule is applied, so get new object here rule = _entityMgr.findById(LoadBalancer.class, getEntityId()); LoadBalancerResponse lbResponse = new LoadBalancerResponse(); - if (rule != null) { - lbResponse = _responseGenerator.createLoadBalancerResponse(rule); - setResponseObject(lbResponse); + if (rule == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to apply firewall rules."); } + lbResponse = _responseGenerator.createLoadBalancerResponse(rule); lbResponse.setResponseName(getCommandName()); + + setResponseObject(lbResponse); + } catch (Exception ex) { s_logger.warn("Failed to create LB rule due to exception ", ex); + String msg = ex.getMessage() != null ? ex.getMessage() : ""; + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create load balancer rule. " + msg); } finally { if (!success || rule == null) { @@ -298,8 +342,6 @@ public void execute() throws ResourceAllocationException, ResourceUnavailableExc } // no need to apply the rule on the backend as it exists in the db only _lbService.deleteLoadBalancerRule(getEntityId(), false); - - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create load balancer rule"); } } } @@ -314,7 +356,8 @@ public void create() { try { LoadBalancer result = _lbService.createPublicLoadBalancerRule(getXid(), getName(), getDescription(), getSourcePortStart(), getSourcePortEnd(), getDefaultPortStart(), - getDefaultPortEnd(), getSourceIpAddressId(), getProtocol(), getAlgorithm(), getNetworkId(), getEntityOwnerId(), getOpenFirewall(), getLbProtocol(), isDisplay()); + getDefaultPortEnd(), getSourceIpAddressId(), getProtocol(), getAlgorithm(), getNetworkId(), getEntityOwnerId(), getOpenFirewall(), getLbProtocol(), isDisplay(), + getAdditionalPortMap(), getCache(), getServiceDownAction(), getHealthCheckDestination(), getExpectedHealthCheck(), healthCheckType, skipDnsError, dsr); this.setEntityId(result.getId()); this.setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException e) { @@ -425,4 +468,28 @@ public String getSyncObjType() { public Long getSyncObjId() { return getNetworkId(); } + + public String getHealthCheckType() { + return healthCheckType; + } + + public String getExpectedHealthCheck() { + return expectedHealthCheck; + } + + public boolean isSkipDnsError() { + return skipDnsError; + } + + public void setSkipDnsError(boolean skipDnsError) { + this.skipDnsError = skipDnsError; + } + + public boolean isDsr() { + return dsr; + } + + public void setDsr(boolean dsr) { + this.dsr = dsr; + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java index e3cde0ba640f..5529031037eb 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerRuleInstancesCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.loadbalancer; +import com.cloud.utils.Ternary; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ import org.apache.cloudstack.api.response.UserVmResponse; import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; +//import com.cloud.utils.Pair; @APICommand(name = "listLoadBalancerRuleInstances", description = "List all virtual machine instances that are assigned to a load balancer rule.", responseObject = LoadBalancerRuleVmMapResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, @@ -93,9 +94,10 @@ public String getCommandName() { @Override public void execute() { - Pair, List> vmServiceMap = _lbService.listLoadBalancerInstances(this); + Ternary, List,Integer> vmServiceMap = _lbService.listLoadBalancerInstances(this); List result = vmServiceMap.first(); List serviceStates = vmServiceMap.second(); + Integer count = vmServiceMap.third(); if (!isListLbVmip()) { // list lb instances @@ -109,11 +111,10 @@ public void execute() { vmResponses.get(i).setServiceState(serviceStates.get(i)); } } - response.setResponses(vmResponses); + response.setResponses(vmResponses, count); response.setResponseName(getCommandName()); - setResponseObject(response); - + setResponseObject(response); } else { ListResponse lbRes = new ListResponse(); @@ -139,7 +140,7 @@ public void execute() { } lbRes.setResponseName(getCommandName()); - lbRes.setResponses(listlbVmRes); + lbRes.setResponses(listlbVmRes, count); setResponseObject(lbRes); } } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java index 13765563fd9d..320596dccdc7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveFromLoadBalancerRuleCmd.java @@ -156,6 +156,7 @@ public String getEventDescription() { @Override public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(id, getActualCommandName()); CallContext.current().setEventDetails("Load balancer Id: " + getId() + " VmIds: " + StringUtils.join(getVirtualMachineIds(), ",")); Map> vmIdIpsMap = getVmIdIpListMap(); try { diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveNetworksFromLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveNetworksFromLoadBalancerRuleCmd.java new file mode 100644 index 000000000000..b5ea5a8253fc --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveNetworksFromLoadBalancerRuleCmd.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 org.apache.cloudstack.api.command.user.loadbalancer; + +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import com.cloud.utils.StringUtils; + +@APICommand(name = "removeNetworksFromLoadBalancerRule", description = "Removes a list of networks from a load balancer rule.", responseObject = SuccessResponse.class) +public class RemoveNetworksFromLoadBalancerRuleCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveNetworksFromLoadBalancerRuleCmd.class.getName()); + + private static final String s_name = "removenetworksfromloadbalancerruleresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the load balancer rule") + private Long id; + + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the list of IDs of the networks that are being removed from the load balancer rule") + private List networkIds; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getLoadBalancerId() { + return id; + } + + public List getNetworkIds() { + return networkIds; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLoadBalancerId()); + if (lb == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return lb.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_REMOVE_NETWORK_FROM_LOAD_BALANCER_RULE; + } + + @Override + public String getEventDescription() { + return "removing networks from load balancer: " + getLoadBalancerId() + " (ids: " + StringUtils.join(getNetworkIds(), ",") + ")"; + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(id, getActualCommandName()); + CallContext.current().setEventDetails("Load balancer Id: " + getLoadBalancerId() + " NetworkIds: " + StringUtils.join(getNetworkIds(), ",")); + boolean result = _lbService.removeNetworksFromLoadBalancer(id, getNetworkIds()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove networks from load balancer rule"); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _lbService.findById(id); + if (lb == null) { + throw new InvalidParameterValueException("Unable to find load balancer rule: " + id); + } + return lb.getNetworkId(); + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java index 43b6633bde41..373744f9df33 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.network; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.log4j.Logger; @@ -76,13 +77,19 @@ public String getCommandName() { @Override public void execute() { - CallContext.current().setEventDetails("Network Id: " + id); - boolean result = _networkService.deleteNetwork(id, isForced()); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete network"); + CallContext.current().setEventDetails("Network Id: " + getId()); + try { + boolean result = _networkService.deleteNetwork(getId(), isForced()); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete network"); + } + + }catch (CloudRuntimeException e ) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage(), e); } } diff --git a/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java b/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java index e8a045ca1ccf..f4168e1b6fb0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java @@ -57,6 +57,24 @@ public class CreateProjectCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "display text of the project") private String displayText; + @Parameter(name = ApiConstants.BUSINESS_SERVICE_ID, type = CommandType.STRING, required = false, description = "business service id") + private String businessServiceId; + + @Parameter(name = ApiConstants.CLIENT_ID, type = CommandType.STRING, required = false, description = "client id") + private String clientId; + + @Parameter(name = ApiConstants.COMPONENT_ID, type = CommandType.STRING, required = false, description = "component id") + private String componentId; + + @Parameter(name = ApiConstants.SUB_COMPONENT_ID, type = CommandType.STRING, required = false, description = "sub-component id") + private String subComponentId; + + @Parameter(name = ApiConstants.PRODUCT_ID, type = CommandType.STRING, required = false, description = "product id") + private String productId; + + @Parameter(name = ApiConstants.DETAILED_USAGE, type = CommandType.BOOLEAN, required = false, description = "true if project has detailed usage by third party app") + private Boolean detailedUsage = false; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -86,6 +104,30 @@ public String getDisplayText() { return displayText; } + public String getBusinessServiceId() { + return businessServiceId; + } + + public String getClientId() { + return clientId; + } + + public String getComponentId() { + return componentId; + } + + public String getSubComponentId() { + return subComponentId; + } + + public String getProductId() { + return productId; + } + + public Boolean isDetailedUsage() { + return detailedUsage; + } + @Override public String getCommandName() { return s_name; @@ -125,7 +167,8 @@ public void execute() { @Override public void create() throws ResourceAllocationException { CallContext.current().setEventDetails("Project Name: " + getName()); - Project project = _projectService.createProject(getName(), getDisplayText(), getAccountName(), getDomainId()); + Project project = _projectService.createProject(getName(), getDisplayText(), getAccountName(), getDomainId(), + getBusinessServiceId(), getClientId(), getComponentId(), getSubComponentId(), getProductId(), isDetailedUsage()); if (project != null) { this.setEntityId(project.getId()); this.setEntityUuid(project.getUuid()); diff --git a/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java b/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java index 7b479b1a9ffd..7a25f66ea32b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/project/ListProjectsCmd.java @@ -61,6 +61,9 @@ public class ListProjectsCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "List projects by tags (key/value pairs)") private Map tags; + @Parameter(name = "simple", type = CommandType.BOOLEAN, description = "List only basic projec data like id, name, domain") + private boolean simple; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -81,6 +84,10 @@ public String getState() { return state; } + public boolean isSimple() { + return simple; + } + @Override public String getCommandName() { return s_name; diff --git a/api/src/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java b/api/src/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java index 11e5e35bac3d..cd584e769a3f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java @@ -52,6 +52,24 @@ public class UpdateProjectCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "display text of the project") private String displayText; + @Parameter(name = ApiConstants.BUSINESS_SERVICE_ID, type = CommandType.STRING, required = false, description = "business service id") + private String businessServiceId; + + @Parameter(name = ApiConstants.CLIENT_ID, type = CommandType.STRING, required = false, description = "client id") + private String clientId; + + @Parameter(name = ApiConstants.COMPONENT_ID, type = CommandType.STRING, required = false, description = "component id") + private String componentId; + + @Parameter(name = ApiConstants.SUB_COMPONENT_ID, type = CommandType.STRING, required = false, description = "sub-component id") + private String subComponentId; + + @Parameter(name = ApiConstants.PRODUCT_ID, type = CommandType.STRING, required = false, description = "product id") + private String productId; + + @Parameter(name = ApiConstants.DETAILED_USAGE, type = CommandType.BOOLEAN, required = false, description = "true if project has detailed usage by third party app") + private Boolean detailedUsage; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +86,30 @@ public String getDisplayText() { return displayText; } + public String getBusinessServiceId() { + return businessServiceId; + } + + public String getClientId() { + return clientId; + } + + public String getComponentId() { + return componentId; + } + + public String getSubComponentId() { + return subComponentId; + } + + public String getProductId() { + return productId; + } + + public Boolean isDetailedUsage() { + return detailedUsage; + } + @Override public String getCommandName() { return s_name; @@ -91,7 +133,8 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceAllocationException { CallContext.current().setEventDetails("Project id: " + getId()); - Project project = _projectService.updateProject(getId(), getDisplayText(), getAccountName()); + Project project = _projectService.updateProject(getId(), getDisplayText(), getAccountName(), getBusinessServiceId(), + getClientId(), getComponentId(), getSubComponentId(), getProductId(), isDetailedUsage()); if (project != null) { ProjectResponse response = _responseGenerator.createProjectResponse(project); response.setResponseName(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java index c514984d2171..f52e3748f74c 100644 --- a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/RemoveFromGlobalLoadBalancerRuleCmd.java @@ -117,6 +117,9 @@ public String getEventDescription() { public void execute() { CallContext.current().setEventDetails( "Global Load balancer rule Id: " + getGlobalLoadBalancerRuleId() + " VmIds: " + StringUtils.join(getLoadBalancerRulesIds(), ",")); + + _lbService.throwExceptionIfIsParentLoadBalancer(getLoadBalancerRulesIds(), getActualCommandName()); + boolean result = _gslbService.removeFromGlobalLoadBalancerRule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java index d16b87cd95e8..afa07cca3b9d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java @@ -78,6 +78,9 @@ public class CopyTemplateCmd extends BaseAsyncCmd { "Do not specify destzoneid and destzoneids together, however one of them is required.") protected List destZoneIds; + @Parameter(name = "force", type = CommandType.BOOLEAN, description = "Force copy template when template is cross zones") + private boolean force = false; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -179,4 +182,8 @@ public void execute() throws ResourceAllocationException { throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); } } + + public boolean isForce() { + return force; + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListGloboVirtualMachinesCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListGloboVirtualMachinesCmd.java new file mode 100644 index 000000000000..bd9a670f4dba --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListGloboVirtualMachinesCmd.java @@ -0,0 +1,57 @@ +package org.apache.cloudstack.api.command.user.vm; + +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; + +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.response.ListResponse; + +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.query.QueryService; + +@APICommand(name = "listGloboVirtualMachines", description = "List the virtual machines by usage.", responseObject = UserVmResponse.class, responseView = ResponseObject.ResponseView.Restricted, entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) +public class ListGloboVirtualMachinesCmd extends BaseListTaggedResourcesCmd { + + private static final String s_name = "listglobovirtualmachinesresponse"; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the virtual machine") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the virtual machine (a substring match is made against the parameter value, data for all matching VMs will be returned)") + private String name; + + @Inject + public QueryService _queryService; + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return s_name; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.VirtualMachine; + } + + @Override + public void execute() { + ListResponse response = new ListResponse<>(); + Pair, Integer> result = _queryService.listGloboVm(id, name, getProjectId(), getTags()); + + response.setResponses(result.first(), result.second()); + response.setResponseName(getCommandName()); + response.setObjectName("virtualmachine"); + setResponseObject(response); + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index ff6acde755f8..e423923c61c1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -130,6 +130,9 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = "simple", type = CommandType.BOOLEAN, description = "List only basic VM data") + private boolean simple; + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "the user ID that created the VM and is under the account that owns the VM") private Long userId; @@ -195,6 +198,10 @@ public Long getAffinityGroupId() { return affinityGroupId; } + public boolean isSimple() { + return simple; + } + public String getKeyPairName() { return keypair; } diff --git a/api/src/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java b/api/src/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java index bf85d8b227f2..e797fbe11c3c 100644 --- a/api/src/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AutoScalePolicyResponse.java @@ -38,6 +38,10 @@ public class AutoScalePolicyResponse extends BaseResponse implements ControlledE @Param(description = "the action to be executed if all the conditions evaluate to true for the specified duration.") private String action; + @SerializedName(ApiConstants.STEP) + @Param(description = "the number of vms that will be created/destroyed") + private Integer step; + @SerializedName(ApiConstants.DURATION) @Param(description = "the duration for which the conditions have to be true before action is taken") private Integer duration; @@ -50,6 +54,10 @@ public class AutoScalePolicyResponse extends BaseResponse implements ControlledE @Param(description = "the list of IDs of the conditions that are being evaluated on every interval") private List conditions; + @SerializedName(ApiConstants.LOGICAL_OPERATOR) + @Param(description = "logical operator to be used between all of the conditions") + private String logicalOperator; + @SerializedName(ApiConstants.ACCOUNT) @Param(description = "the account owning the autoscale policy") private String accountName; @@ -91,10 +99,18 @@ public void setAction(String action) { this.action = action; } + public void setStep(Integer step) { + this.step = step; + } + public void setConditions(List conditions) { this.conditions = conditions; } + public void setLogicalOperator(String logicalOperator) { + this.logicalOperator = logicalOperator; + } + @Override public void setAccountName(String accountName) { this.accountName = accountName; diff --git a/api/src/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java b/api/src/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java index 572d4626fd9e..a5da2d2ffcb8 100644 --- a/api/src/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AutoScaleVmGroupResponse.java @@ -38,6 +38,10 @@ public class AutoScaleVmGroupResponse extends BaseResponse implements Controlled @Param(description = "the load balancer rule ID") private String loadBalancerId; + @SerializedName(ApiConstants.AUTOSCALE_GROUP_VM_PREFIX_NAME) + @Param(description = "the autoscale vm group name") + private String autoScaleGroupName; + @SerializedName(ApiConstants.VMPROFILE_ID) @Param(description = "the autoscale profile that contains information about the vms in the vm group.") private String profileId; @@ -46,6 +50,10 @@ public class AutoScaleVmGroupResponse extends BaseResponse implements Controlled @Param(description = "the minimum number of members in the vmgroup, the number of instances in the vm group will be equal to or more than this number.") private int minMembers; + @SerializedName(ApiConstants.AUTOSCALE_GROUP_COUNT_MEMBERS) + @Param(description = "the number of instances in the vm autoscale group.") + private int autoScaleGroupCountMembers; + @SerializedName(ApiConstants.MAX_MEMBERS) @Param(description = "the maximum number of members in the vmgroup, The number of instances in the vm group will be equal to or less than this number.") private int maxMembers; @@ -86,6 +94,13 @@ public class AutoScaleVmGroupResponse extends BaseResponse implements Controlled @Param(description = "the domain name of the vm profile") private String domainName; + @SerializedName("tags") + @Param( + description = "the list of resource tags associated with load balancer", + responseObject = ResourceTagResponse.class + ) + private List tags; + @SerializedName(ApiConstants.FOR_DISPLAY) @Param(description = "is group for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; @@ -107,6 +122,10 @@ public void setLoadBalancerId(String loadBalancerId) { this.loadBalancerId = loadBalancerId; } + public void setAutoScaleGroupName(String autoScaleGroupName) { + this.autoScaleGroupName = autoScaleGroupName; + } + public void setProfileId(String profileId) { this.profileId = profileId; } @@ -115,6 +134,10 @@ public void setMinMembers(int minMembers) { this.minMembers = minMembers; } + public void setAutoScaleGroupCountMembers(int autoScaleGroupCountMembers) { + this.autoScaleGroupCountMembers = autoScaleGroupCountMembers; + } + public void setMaxMembers(int maxMembers) { this.maxMembers = maxMembers; } @@ -160,6 +183,10 @@ public void setProjectName(String projectName) { this.projectName = projectName; } + public void setTags(List tags) { + this.tags = tags; + } + public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } diff --git a/api/src/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java b/api/src/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java index 412af1c27362..561bebd8bd3e 100644 --- a/api/src/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AutoScaleVmProfileResponse.java @@ -102,6 +102,14 @@ public class AutoScaleVmProfileResponse extends BaseResponse implements Controll @Param(description = "is profile for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; + @SerializedName(ApiConstants.NETWORK_IDS) + @Param(description = "list of additional networks (besides the LB network) to be added to the VMs") + private List networkIds; + + @SerializedName(ApiConstants.USER_DATA) + @Param(description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded.") + private String userData; + public AutoScaleVmProfileResponse() { } @@ -179,4 +187,12 @@ public void setCsUrl(String csUrl) { public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } + + public void setNetworkIds(List networkIds) { + this.networkIds = networkIds; + } + + public void setUserData(String userData) { + this.userData = userData; + } } diff --git a/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java b/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java index 65d4ac333711..186483f84a56 100644 --- a/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ExceptionResponse.java @@ -46,6 +46,7 @@ public class ExceptionResponse extends BaseResponse { public ExceptionResponse() { idList = new ArrayList(); + buildCurrentContext(); } public Integer getErrorCode() { diff --git a/api/src/org/apache/cloudstack/api/response/ExpectedHealthcheckResponse.java b/api/src/org/apache/cloudstack/api/response/ExpectedHealthcheckResponse.java new file mode 100644 index 000000000000..42275de93044 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/ExpectedHealthcheckResponse.java @@ -0,0 +1,37 @@ +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class ExpectedHealthcheckResponse extends BaseResponse implements Comparable { + @SerializedName(ApiConstants.ID) + @Param(description = "the Pool ID") + private Long id; + + @SerializedName("expected") + @Param(description = "Expected healthcheck") + private String expected; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getExpected() { + return expected; + } + + public void setExpected(String expected) { + this.expected = expected; + } + + @Override + public int compareTo(ExpectedHealthcheckResponse o) { + return this.getExpected().compareToIgnoreCase(o.getExpected()); + } +} diff --git a/api/src/org/apache/cloudstack/api/response/GloboDictionaryResponse.java b/api/src/org/apache/cloudstack/api/response/GloboDictionaryResponse.java new file mode 100644 index 000000000000..3a47e0a33ca2 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/GloboDictionaryResponse.java @@ -0,0 +1,54 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class GloboDictionaryResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the globo dictionary object id") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the globo dictionary object name") + private String name; + + public GloboDictionaryResponse(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/api/src/org/apache/cloudstack/api/response/HostResponse.java b/api/src/org/apache/cloudstack/api/response/HostResponse.java index b9667eca2411..8046789277ad 100644 --- a/api/src/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/org/apache/cloudstack/api/response/HostResponse.java @@ -496,7 +496,6 @@ public void setDetails(Map details) { detailsCopy.remove("password"); this.details = detailsCopy; - } public void setMemoryTotal(Long memoryTotal) { diff --git a/api/src/org/apache/cloudstack/api/response/ListResponse.java b/api/src/org/apache/cloudstack/api/response/ListResponse.java index 52abe246a49c..ecbee3bff930 100644 --- a/api/src/org/apache/cloudstack/api/response/ListResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ListResponse.java @@ -25,6 +25,10 @@ public class ListResponse extends BaseResponse { List responses; private transient Integer count; + public ListResponse(){ + buildCurrentContext(); + } + public List getResponses() { return responses; } diff --git a/api/src/org/apache/cloudstack/api/response/LoadBalancerResponse.java b/api/src/org/apache/cloudstack/api/response/LoadBalancerResponse.java index 1eb8fca5087b..2ae54479f284 100644 --- a/api/src/org/apache/cloudstack/api/response/LoadBalancerResponse.java +++ b/api/src/org/apache/cloudstack/api/response/LoadBalancerResponse.java @@ -16,14 +16,18 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.ArrayList; import java.util.List; +import com.cloud.network.rules.LoadBalancer; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; @SuppressWarnings("unused") public class LoadBalancerResponse extends BaseResponse implements ControlledEntityResponse { @@ -103,6 +107,26 @@ public class LoadBalancerResponse extends BaseResponse implements ControlledEnti @Param(description = "the protocol of the loadbalanacer rule") private String lbProtocol; + @SerializedName("additionalnetworkids") + @Param(description = "the additional networks that are associated with this load balancer") + private List additionalNetworks; + + @SerializedName(ApiConstants.ADDITIONAL_PORT_MAP) + @Param(description = "additional port map associated with load balancing rule") + private List additionalPortMap; + + @SerializedName(ApiConstants.CACHE) + @Param(description = "cache group associated with load balancing rule") + private String cache; + + @SerializedName(ApiConstants.HEALTHCHECK_DESTINATION) + @Param(description = "Port to be used as health check alternative to the service port (optional)") + private String healthCheckDestination; + + @SerializedName(ApiConstants.SERVICE_DOWN_ACTION) + @Param(description = "ID of the action to be executed when service is down") + private String serviceDownAction; + @SerializedName(ApiConstants.TAGS) @Param(description = "the list of resource tags associated with load balancer", responseObject = ResourceTagResponse.class) private List tags; @@ -111,6 +135,20 @@ public class LoadBalancerResponse extends BaseResponse implements ControlledEnti @Param(description = "is rule for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; + @SerializedName(ApiConstants.GLOBO_RESOURCE_CONFIG) + @Param(description = "list with globo resource configuration") + private List globoResourceConfigs; + + + @SerializedName(ApiConstants.LB_LINKED_PARENT_LOAD_BALANCER) + @Param(description = "load balancer that this is linked") + private LinkedLoadBalancer linkedparent; + + + @SerializedName(ApiConstants.LB_LINKED_CHILDREN_LOAD_BALANCER) + @Param(description = "load balancer linked children") + private List linkedchildren; + public void setId(String id) { this.id = id; } @@ -196,7 +234,130 @@ public void setLbProtocol(String lbProtocol) { this.lbProtocol = lbProtocol; } + public void setAdditionalNetworks(List additionalNetworks) { + this.additionalNetworks = additionalNetworks; + } + + public void setAdditionalPortMap(List additionalPortMap) { this.additionalPortMap = additionalPortMap; } + + public void setCache(String cache) { this.cache = cache; } + + public void setHealthCheckDestination(String healthCheckDestination) { + this.healthCheckDestination = healthCheckDestination; + } + + public void setGloboResourceConfigs(List configs) { + this.globoResourceConfigs = configs; + } + + public void setServiceDownAction(String serviceDownAction) { + this.serviceDownAction = serviceDownAction; + } + public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } + + public String getName() { + return name; + } + + public LinkedLoadBalancer getLinkedparent() { + return linkedparent; + } + + public void setLinkedParentLoadBalancer(LoadBalancer parentLb, GloboResourceConfiguration linkedConfig) { + this.linkedparent = new LinkedLoadBalancer(); + linkedparent.setName(parentLb.getName()); + linkedparent.setUuid(parentLb.getUuid()); + linkedparent.setConfigId(linkedConfig.getId()); + + } + + public void addLinkChild(LoadBalancer childlb, GloboResourceConfiguration linkedConfig) { + if (linkedchildren == null) { + linkedchildren = new ArrayList<>(); + } + + LinkedLoadBalancer child = new LinkedLoadBalancer(); + child.setName(childlb.getName()); + child.setUuid(childlb.getUuid()); + child.setConfigId(linkedConfig.getId()); + linkedchildren.add(child); + + } + + @EntityReference(value = LoadBalancer.class) + public static class LinkedLoadBalancer { + private String name; + private String uuid; + private Long configid; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Long getConfigId() { + return configid; + } + + public void setConfigId(Long configId) { + this.configid = configId; + } + } + + @EntityReference(value = GloboResourceConfiguration.class) + public static class GloboResourceConfigurationResponse { + @SerializedName("resourcetype") + @Param(description = "the resourcetype of the resource") + private String resourceType; + + @SerializedName("configurationkey") + @Param(description = "the configuration key") + private String configurationKey; + + @SerializedName("configurationvalue") + @Param(description = "the configuration value") + private String configurationValue; + + + public GloboResourceConfigurationResponse() { + } + + public String getResourceType() { + return this.resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public void setConfigurationKey(String configurationKey){ + this.configurationKey = configurationKey; + } + + public void setConfigurationValue(String configurationValue){ + this.configurationValue = configurationValue; + } + + public String getConfigurationKey() { + return configurationKey; + } + + public String getConfigurationValue() { + return configurationValue; + } + } } diff --git a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java index 8d0f725500bb..9edcc15fb168 100644 --- a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java @@ -225,6 +225,10 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes @Param(description = "If a network is enabled for 'streched l2 subnet' then represents zones on which network currently spans", since = "4.4") private Set networkSpannedZones; + @SerializedName(ApiConstants.GURU_NAME) + @Param(description = "The name of the network guru that implements this network", since = "4.6") + private String guruName; + @SerializedName(ApiConstants.EXTERNAL_ID) @Param(description = "The external id of the network", since = "4.11") private String externalId; @@ -438,6 +442,10 @@ public void setNetworkSpannedZones(Set networkSpannedZones) { this.networkSpannedZones = networkSpannedZones; } + public void setGuruName(String guruName) { + this.guruName = guruName; + } + public void setExternalId(String externalId) { this.externalId = externalId; } diff --git a/api/src/org/apache/cloudstack/api/response/PoolResponse.java b/api/src/org/apache/cloudstack/api/response/PoolResponse.java new file mode 100644 index 000000000000..ee01d224436e --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/PoolResponse.java @@ -0,0 +1,140 @@ +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class PoolResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the Pool ID") + private Long id; + + @SerializedName("name") + @Param(description = "the Pool Load name") + private String name; + + @SerializedName("lbmethod") + @Param(description = "the Pool Load balancer Method") + private String lbMethod; + + + @SerializedName("port") + @Param(description = "the Pool Port") + private Integer port; + + @SerializedName("vipport") + @Param(description = "the Vip Port") + private Integer vipPort; + + @SerializedName("maxconn") + @Param(description = "the max connections") + private Integer maxconn; + + + @SerializedName("healthchecktype") + @Param(description = "Healthcheck type") + private String healthcheckType; + + @SerializedName("healthcheck") + @Param(description = "Healthcheck") + private String healthcheck; + + @SerializedName("healthcheckexpect") + @Param(description = "Expected healthcheck") + private String expectedHealthcheck; + + @SerializedName("l4protocol") + private String l4Protocol; + + @SerializedName("l7protocol") + private String l7Protocol; + + public String getHealthcheckType() { + return healthcheckType; + } + + public Integer getMaxconn() { + return maxconn; + } + + public void setMaxconn(Integer maxconn) { + this.maxconn = maxconn; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public String getExpectedHealthcheck() { + return expectedHealthcheck; + } + + public void setExpectedHealthcheck(String expectedHealthcheck) { + this.expectedHealthcheck = expectedHealthcheck; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLbMethod() { + return lbMethod; + } + + public void setLbMethod(String lbMethod) { + this.lbMethod = lbMethod; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getVipPort() { + return vipPort; + } + + public void setVipPort(Integer vipPort) { + this.vipPort = vipPort; + } + + public void setL4Protocol(String l4Protocol) { + this.l4Protocol = l4Protocol; + } + + public void setL7Protocol(String l7Protocol) { + this.l7Protocol = l7Protocol; + } + + public String getL4Protocol() { + return l4Protocol; + } + + public String getL7Protocol() { + return l7Protocol; + } +} diff --git a/api/src/org/apache/cloudstack/api/response/ProjectResponse.java b/api/src/org/apache/cloudstack/api/response/ProjectResponse.java index 8bfa6d94b63c..46635c7965df 100644 --- a/api/src/org/apache/cloudstack/api/response/ProjectResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ProjectResponse.java @@ -51,6 +51,30 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the domain name where the project belongs to") private String domain; + @SerializedName(ApiConstants.BUSINESS_SERVICE_ID) + @Param(description = "the business service id") + private String businessServiceId; + + @SerializedName(ApiConstants.CLIENT_ID) + @Param(description = "the client id") + private String clientId; + + @SerializedName(ApiConstants.COMPONENT_ID) + @Param(description = "the component id") + private String componentId; + + @SerializedName(ApiConstants.SUB_COMPONENT_ID) + @Param(description = "the sub-component id") + private String subComponentId; + + @SerializedName(ApiConstants.PRODUCT_ID) + @Param(description = "the product id") + private String productId; + + @SerializedName(ApiConstants.DETAILED_USAGE) + @Param(description = "true if project has detailed usage by third party app") + private Boolean detailedUsage; + @SerializedName(ApiConstants.ACCOUNT) @Param(description = "the account name of the project's owner") private String ownerName; @@ -422,4 +446,27 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) { this.secondaryStorageAvailable = secondaryStorageAvailable; } + public void setBusinessServiceId(String businessServiceId) { + this.businessServiceId = businessServiceId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public void setComponentId(String componentId) { + this.componentId = componentId; + } + + public void setSubComponentId(String subComponentId) { + this.subComponentId = subComponentId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public void setDetailedUsage(Boolean detailedUsage) { + this.detailedUsage = detailedUsage; + } } diff --git a/api/src/org/apache/cloudstack/api/response/RegisterDnsForLoadBalancerResponse.java b/api/src/org/apache/cloudstack/api/response/RegisterDnsForLoadBalancerResponse.java new file mode 100644 index 000000000000..05dc3fca93fa --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/RegisterDnsForLoadBalancerResponse.java @@ -0,0 +1,47 @@ +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.BaseResponse; + +/** + * Created by sinval.neto on 7/20/16. + */ +public class RegisterDnsForLoadBalancerResponse extends BaseResponse { + + @SerializedName("result") + @Param(description = "the result of the operation") + private String result; + + @SerializedName("id") + @Param(description = "the id of the resource") + private String id; + + @SerializedName("resourcetype") + @Param(description = "the resourcetype of the resource") + private String resourceType; + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getResourceType() { + return this.resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } +} diff --git a/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java b/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java index bb2ff7f6d0e9..ed5b07dd620c 100644 --- a/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java +++ b/api/src/org/apache/cloudstack/api/response/SnapshotResponse.java @@ -68,6 +68,10 @@ public class SnapshotResponse extends BaseResponse implements ControlledEntityRe @Param(description = "type of the disk volume") private String volumeType; + @SerializedName("volumesize") + @Param(description = "size of the disk volume") + private Long volumeSize; + @SerializedName(ApiConstants.CREATED) @Param(description = " the date the snapshot was created") private Date created; @@ -96,6 +100,10 @@ public class SnapshotResponse extends BaseResponse implements ControlledEntityRe @Param(description = "id of the availability zone") private String zoneId; + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "name of the availability zone") + private String zoneName; + @SerializedName(ApiConstants.TAGS) @Param(description = "the list of resource tags associated with snapshot", responseObject = ResourceTagResponse.class) private List tags; @@ -168,6 +176,10 @@ public void setVolumeType(String volumeType) { this.volumeType = volumeType; } + public void setVolumeSize(Long volumeSize) { + this.volumeSize = volumeSize; + } + public void setCreated(Date created) { this.created = created; } @@ -206,6 +218,10 @@ public void setZoneId(String zoneId) { this.zoneId = zoneId; } + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + public void setTags(List tags) { this.tags = tags; } diff --git a/api/src/org/apache/cloudstack/api/response/SuccessResponse.java b/api/src/org/apache/cloudstack/api/response/SuccessResponse.java index 0dde6d0e4230..87857b07f054 100644 --- a/api/src/org/apache/cloudstack/api/response/SuccessResponse.java +++ b/api/src/org/apache/cloudstack/api/response/SuccessResponse.java @@ -48,6 +48,7 @@ public void setDisplayText(String displayText) { } public SuccessResponse() { + buildCurrentContext(); } public SuccessResponse(String responseName) { diff --git a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java index 2ff1eaa717b7..9abb6cb10207 100644 --- a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java @@ -286,6 +286,11 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "OS type id of the vm", since = "4.4") private Long osTypeId; + + @SerializedName(ApiConstants.OS_TYPE_NAME) + @Param(description = "OS type name of the vm", since = "4.4") + private String osTypeName; + public UserVmResponse() { securityGroupList = new LinkedHashSet(); nics = new LinkedHashSet(); @@ -837,4 +842,9 @@ public void setDynamicallyScalable(Boolean dynamicallyScalable) { public Long getOsTypeId() { return osTypeId; } + + public void setOsTypeName(String osTypeName) { + this.osTypeName = osTypeName; + } + } diff --git a/api/src/org/apache/cloudstack/context/CallContext.java b/api/src/org/apache/cloudstack/context/CallContext.java index fb83f8689143..521e337aa303 100644 --- a/api/src/org/apache/cloudstack/context/CallContext.java +++ b/api/src/org/apache/cloudstack/context/CallContext.java @@ -62,6 +62,8 @@ protected Stack initialValue() { private final Map context = new HashMap(); private Project project; + private String ndcContext; + static EntityManager s_entityMgr; public static void init(EntityManager entityMgr) { @@ -171,7 +173,9 @@ protected static CallContext register(User callingUser, Account callingAccount, callingContext = new CallContext(userId, accountId, contextId); } s_currentContext.set(callingContext); - NDC.push("ctx-" + UuidUtils.first(contextId)); + String ndc = "ctx-" + UuidUtils.first(contextId); + NDC.push(ndc); + callingContext.setNdcContenxt(ndc); if (s_logger.isTraceEnabled()) { s_logger.trace("Registered: " + callingContext); } @@ -376,4 +380,12 @@ public String toString() { .append("]") .toString(); } + + public void setNdcContenxt(String ndcContext) { + this.ndcContext = ndcContext; + } + + public String getNdcContext() { + return this.ndcContext; + } } diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java index b34935611025..3893e5500e5a 100644 --- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java +++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java @@ -48,7 +48,8 @@ public static class NetworkDevice { public static final NetworkDevice OpenDaylightController = new NetworkDevice("OpenDaylightController", Network.Provider.Opendaylight.getName()); public static final NetworkDevice BrocadeVcs = new NetworkDevice("BrocadeVcs", Network.Provider.BrocadeVcs.getName()); public static final NetworkDevice GloboDns = new NetworkDevice("GloboDns", Network.Provider.GloboDns.getName()); - + public static final NetworkDevice GloboNetwork = new NetworkDevice("GloboNetwork", Network.Provider.GloboNetwork.getName()); + public static final NetworkDevice GloboAclAPI = new NetworkDevice("GloboACLAPI", Network.Provider.GloboAclApi.getName()); public NetworkDevice(String deviceName, String ntwkServiceprovider) { _name = deviceName; _provider = ntwkServiceprovider; diff --git a/api/src/org/apache/cloudstack/query/QueryService.java b/api/src/org/apache/cloudstack/query/QueryService.java index de9fbb55a26c..fa48c96a781b 100644 --- a/api/src/org/apache/cloudstack/query/QueryService.java +++ b/api/src/org/apache/cloudstack/query/QueryService.java @@ -16,8 +16,10 @@ // under the License. package org.apache.cloudstack.query; +import com.cloud.utils.Pair; import java.util.List; +import java.util.Map; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; @@ -141,4 +143,6 @@ public interface QueryService { ListResponse searchForStorageTags(ListStorageTagsCmd cmd); ListResponse searchForHostTags(ListHostTagsCmd cmd); + + Pair, Integer> listGloboVm(Long id, String name, Long projectId, Map tags); } diff --git a/api/test/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmdTest.java b/api/test/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmdTest.java new file mode 100644 index 000000000000..2579b8ded717 --- /dev/null +++ b/api/test/org/apache/cloudstack/api/command/user/globodictionary/BaseDictionaryCmdTest.java @@ -0,0 +1,142 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.cloudstack.api.command.user.globodictionary; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; +import org.apache.cloudstack.api.response.GloboDictionaryResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +public class BaseDictionaryCmdTest { + + public static final String BUSINESS_SERVICE_ID = "31cf5f75db9f43409fc15458dc96198b"; + public static final String BUSINESS_SERVICE_NAME = "Assinatura - Vendas"; + + @Mock + GloboDictionaryService globoDictionaryService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testList() throws Exception { + GloboDictionaryEntity mockEntity = mock(GloboDictionaryEntity.class); + when(mockEntity.getId()).thenReturn(BUSINESS_SERVICE_ID); + when(mockEntity.getName()).thenReturn(BUSINESS_SERVICE_NAME); + when(globoDictionaryService.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE)).thenReturn(Collections.singletonList(mockEntity)); + + DummyBaseDictionaryCmd cmd = new DummyBaseDictionaryCmd(globoDictionaryService); + cmd.execute(); + ListResponse responses = (ListResponse) cmd.getResponseObject(); + GloboDictionaryResponse response = ((GloboDictionaryResponse) responses.getResponses().get(0)); + + assertEquals(new Integer(1), responses.getCount()); + assertEquals("listdummyresponse", responses.getResponseName()); + assertEquals(BUSINESS_SERVICE_ID, response.getId()); + assertEquals(BUSINESS_SERVICE_NAME, response.getName()); + assertEquals("response", response.getObjectName()); + + verify(globoDictionaryService).list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + @Test + public void testListGivenEmptyResponse() throws Exception { + when(globoDictionaryService.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE)).thenReturn(new ArrayList()); + + DummyBaseDictionaryCmd cmd = new DummyBaseDictionaryCmd(globoDictionaryService); + cmd.execute(); + ListResponse responses = (ListResponse) cmd.getResponseObject(); + + assertEquals(new Integer(0), responses.getCount()); + assertEquals("listdummyresponse", responses.getResponseName()); + + verify(globoDictionaryService).list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + @Test + public void testGet() throws Exception { + GloboDictionaryEntity mockEntity = mock(GloboDictionaryEntity.class); + when(mockEntity.getId()).thenReturn(BUSINESS_SERVICE_ID); + when(mockEntity.getName()).thenReturn(BUSINESS_SERVICE_NAME); + when(globoDictionaryService.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID)).thenReturn(mockEntity); + + DummyBaseDictionaryCmd cmd = new DummyBaseDictionaryCmd(globoDictionaryService); + cmd.setId(BUSINESS_SERVICE_ID); + cmd.execute(); + ListResponse responses = (ListResponse) cmd.getResponseObject(); + GloboDictionaryResponse response = ((GloboDictionaryResponse) responses.getResponses().get(0)); + + assertEquals(new Integer(1), responses.getCount()); + assertEquals("listdummyresponse", responses.getResponseName()); + assertEquals(BUSINESS_SERVICE_ID, response.getId()); + assertEquals(BUSINESS_SERVICE_NAME, response.getName()); + assertEquals("response", response.getObjectName()); + + verify(globoDictionaryService).get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test + public void testGetGivenObjectNotFound() throws Exception { + when(globoDictionaryService.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID)).thenReturn(null); + + DummyBaseDictionaryCmd cmd = new DummyBaseDictionaryCmd(globoDictionaryService); + cmd.setId(BUSINESS_SERVICE_ID); + cmd.execute(); + ListResponse responses = (ListResponse) cmd.getResponseObject(); + assertEquals(new Integer(0), responses.getCount()); + assertEquals("listdummyresponse", responses.getResponseName()); + + verify(globoDictionaryService).get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } +} + +class DummyBaseDictionaryCmd extends BaseDictionaryCmd { + + public DummyBaseDictionaryCmd(GloboDictionaryService globoDictionaryService) { + this.globoDictionaryService = globoDictionaryService; + } + + @Override + GloboDictionaryService.GloboDictionaryEntityType getEntity() { + return GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE; + } + + @Override + public String getCommandName() { + return "listdummyresponse"; + } + + @Override + String getResponseName() { + return "response"; + } +} \ No newline at end of file diff --git a/api/test/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmdTest.java b/api/test/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmdTest.java new file mode 100644 index 000000000000..048222826142 --- /dev/null +++ b/api/test/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmdTest.java @@ -0,0 +1,260 @@ +package org.apache.cloudstack.api.command.user.loadbalancer; + +import com.cloud.network.lb.LoadBalancingRulesService; + +import com.cloud.network.rules.LoadBalancer; + +import com.cloud.utils.db.EntityManager; +import junit.framework.TestCase; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.log4j.Logger; + +import org.junit.Test; + +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.util.List; + +public class CreateLoadBalancerRuleCmdTest extends TestCase{ + + private static final Logger s_logger = Logger.getLogger(CreateLoadBalancerRuleCmdTest.class); + + @Test + public void testExecute() throws Exception{ + + CreateLoadBalancerRuleCmd cmd = new CreateLoadBalancerRuleCmd() { + public Boolean getOpenFirewall() { return false; } + public Long getEntityId() { return 1l; } + }; + + //mocks + LoadBalancer lbMock = getLBInstance(1l); + + EntityManager em = Mockito.mock(EntityManager.class); + Mockito.when(em.findById(LoadBalancer.class, 1l)).thenReturn(lbMock); + cmd._entityMgr = em; + + LoadBalancerResponse lbResponse = new LoadBalancerResponse(); + lbResponse.setId("1"); + + ResponseGenerator responseGenerator = Mockito.mock(ResponseGenerator.class); + Mockito.when(responseGenerator.createLoadBalancerResponse(lbMock)).thenReturn(lbResponse); + + cmd._responseGenerator = responseGenerator; + + + //action + cmd.execute(); + + + //check result + LoadBalancerResponse lbResponseCmd = (LoadBalancerResponse) cmd.getResponseObject(); + assertEquals("createloadbalancerruleresponse", lbResponseCmd.getResponseName()); + + assertEquals("1", (String)getPrivateFieldValue(lbResponseCmd, "id")); + + Mockito.verify(em, Mockito.times(1)).findById(LoadBalancer.class, 1l); + Mockito.verify(responseGenerator, Mockito.times(1)).createLoadBalancerResponse(lbMock); + + } + + @Test + public void testExecute_rule_not_created() throws Exception{ + //mocks + CreateLoadBalancerRuleCmd cmd = new CreateLoadBalancerRuleCmd() { + public Boolean getOpenFirewall() { return false; } + public Long getEntityId() { return 23l; } + }; + + EntityManager em = Mockito.mock(EntityManager.class); + Mockito.when(em.findById(LoadBalancer.class, 23l)).thenReturn(null); + cmd._entityMgr = em; + + LoadBalancingRulesService lbRService = Mockito.mock(LoadBalancingRulesService.class); + Mockito.when(lbRService.deleteLoadBalancerRule(23l, false)).thenReturn(true); + cmd._lbService = lbRService; + + try { + //action + cmd.execute(); + + } catch (ServerApiException e ) { + LoadBalancerResponse lbResponseCmd = (LoadBalancerResponse) cmd.getResponseObject(); + assertNull(lbResponseCmd); + + Mockito.verify(em, Mockito.times(1)).findById(LoadBalancer.class, 23l); + Mockito.verify(lbRService, Mockito.times(1)).deleteLoadBalancerRule(23l, false); + } catch (Exception e ) { + e.printStackTrace(); + fail("shoud be ServerApiException"); + } + } + + + public Object getPrivateFieldValue(Object instance, String value) { + try { + for (Field field : instance.getClass().getDeclaredFields()) { + if (value.equals(field.getName())) { + field.setAccessible(true); + return field.get(instance); + } + } + }catch (Exception e) { + e.printStackTrace(); + return null; + } + + return null; + } + + public LoadBalancer getLBInstance(final Long id ) { + LoadBalancer x = new LoadBalancer() { + + @Override + public long getId() { + return id; + } + + @Override + public int getDefaultPortStart() { + return 0; + } + + @Override + public int getDefaultPortEnd() { + return 0; + } + + @Override + public String getXid() { + return null; + } + + @Override + public Integer getSourcePortStart() { + return null; + } + + @Override + public Integer getSourcePortEnd() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public Purpose getPurpose() { + return null; + } + + @Override + public State getState() { + return null; + } + + @Override + public long getNetworkId() { + return 0; + } + + @Override + public Long getSourceIpAddressId() { + return null; + } + + @Override + public Integer getIcmpCode() { + return null; + } + + @Override + public Integer getIcmpType() { + return null; + } + + @Override + public List getSourceCidrList() { + return null; + } + + @Override + public List getDestinationCidrList() { + return null; + } + + @Override + public Long getRelated() { + return null; + } + + @Override + public FirewallRuleType getType() { + return null; + } + + @Override + public TrafficType getTrafficType() { + return null; + } + + @Override + public boolean isDisplay() { + return false; + } + + @Override + public Class getEntityType() { + return null; + } + + @Override + public String getUuid() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public String getAlgorithm() { + return null; + } + + @Override + public String getLbProtocol() { + return null; + } + + @Override + public Scheme getScheme() { + return null; + } + + @Override + public long getAccountId() { + return 0; + } + + @Override + public long getDomainId() { + return 0; + } + }; + + + return x; + } +} diff --git a/api/test/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmdTest.java b/api/test/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmdTest.java new file mode 100644 index 000000000000..a8160e10ee79 --- /dev/null +++ b/api/test/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmdTest.java @@ -0,0 +1,79 @@ +package org.apache.cloudstack.api.command.user.network; + +import com.cloud.network.NetworkService; +import com.cloud.utils.exception.CloudRuntimeException; +import junit.framework.TestCase; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; + +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class DeleteNetworkCmdTest extends TestCase { + + private static final Logger s_logger = Logger.getLogger(DeleteNetworkCmdTest.class); + + private DeleteNetworkCmd cmd; + + @Before + public void setUp() { + cmd = new DeleteNetworkCmd() { + public Long getId() { + return 1l; + } + public boolean isForced() { + return false; + } + }; + } + + @Test + public void testExecute() { + NetworkService netService = Mockito.mock(NetworkService.class); + Mockito.when(netService.deleteNetwork(1l, false)).thenReturn(true); + cmd._networkService = netService; + + cmd.execute(); + + SuccessResponse response = (SuccessResponse) cmd.getResponseObject(); + assertNotNull(response); + assertEquals("deletenetworkresponse", response.getResponseName()); + } + + @Test + public void testExecute_not_created() { + NetworkService netService = Mockito.mock(NetworkService.class); + Mockito.when(netService.deleteNetwork(1l, false)).thenReturn(false); + cmd._networkService = netService; + + try { + cmd.execute(); + fail("expected exception "); + }catch (ServerApiException e) { + assertEquals("Failed to delete network", e.getMessage()); + }catch (Exception e) { + fail("expected ServerApiException " + e.getClass() + " " + e.getMessage()); + } finally { + Mockito.verify(netService, Mockito.times(1)).deleteNetwork(1l, false); + } + } + + @Test + public void testExecute_fail() { + NetworkService netService = Mockito.mock(NetworkService.class); + Mockito.when(netService.deleteNetwork(1l, false)).thenThrow(new CloudRuntimeException("not deleted")); + cmd._networkService = netService; + + try { + cmd.execute(); + }catch (ServerApiException e) { + assertEquals("not deleted", e.getMessage()); + }catch (Exception e) { + fail("should be ServerApiException " + e.getClass() + " " + e.getMessage()); + }finally { + Mockito.verify(netService, Mockito.times(1)).deleteNetwork(1l, false); + } + } +} \ No newline at end of file diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties new file mode 100644 index 000000000000..2f58d7e03541 --- /dev/null +++ b/client/WEB-INF/classes/resources/messages.properties @@ -0,0 +1,2101 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +label.vm.ip=VM IP Address +message.listView.subselect.multi=(Ctrl/Cmd-click) +label.use.vm.ips=Use VM IPs +label.reinstall.vm=Reinstall VM +message.reinstall.vm=NOTE: Proceed with caution. This will cause the VM to be reinstalled from the template; data on the root disk will be lost. Extra data volumes, if any, will not be touched. +label.recover.vm=Recover VM +message.recover.vm=Please confirm that you would like to recover this VM. +label.port=Port +label.remove.ldap=Remove LDAP +label.configure.ldap=Configure LDAP +label.ldap.configuration=LDAP Configuration +label.ldap.port=LDAP port +label.create.nfs.secondary.staging.store=Create NFS secondary staging store +label.volatile=Volatile +label.planner.mode=Planner mode +label.deployment.planner=Deployment planner +label.quiesce.vm=Quiesce VM +label.smb.username=SMB Username +label.smb.password=SMB Password +label.smb.domain=SMB Domain +label.hypervisors=Hypervisors +label.home=Home +label.sockets=CPU Sockets +label.root.disk.size=Root disk size +label.s3.nfs.server=S3 NFS Server +label.s3.nfs.path=S3 NFS Path +label.delete.events=Delete events +label.delete.alerts=Delete alerts +label.archive.alerts=Archive alerts +label.archive.events=Archive events +label.by.alert.type=By alert type +label.by.event.type=By event type +label.by.date.start=By date (start) +label.by.date.end=By date (end) +label.switch.type=Switch Type +label.service.state=Service State +label.egress.default.policy=Egress Default Policy +label.routing=Routing +label.hvm=HVM +label.about=About +label.app.name=CloudStack +label.about.app=About CloudStack +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. +message.select.affinity.groups=Please select any affinity groups you want this VM to belong to: +message.no.affinity.groups=You do not have any affinity groups. Please continue to the next step. +label.action.delete.nic=Remove NIC +message.action.delete.nic=Please confirm that want to remove this NIC, which will also remove the associated network from the VM. +changed.item.properties=Changed item properties +confirm.enable.s3=Please fill in the following information to enable support for S3-backed Secondary Storage +confirm.enable.swift=Please fill in the following information to enable support for Swift +error.could.not.change.your.password.because.ldap.is.enabled=Error could not change your password because LDAP is enabled. +error.could.not.enable.zone=Could not enable zone +error.installWizard.message=Something went wrong; you may go back and correct any errors +error.invalid.username.password=Invalid username or password +error.login=Your username/password does not match our records. +error.menu.select=Unable to perform action due to no items being selected. +error.mgmt.server.inaccessible=The Management Server is unaccessible. Please try again later. +error.password.not.match=The password fields do not match +error.please.specify.physical.network.tags=Network offerings is not available until you specify tags for this physical network. +error.session.expired=Your session has expired. +error.something.went.wrong.please.correct.the.following=Something went wrong; please correct the following +error.unable.to.reach.management.server=Unable to reach Management Server +error.unresolved.internet.name=Your internet name cannot be resolved. +label.extractable=Extractable +force.delete.domain.warning=Warning\: Choosing this option will cause the deletion of all child domains and all associated accounts and their resources. +force.delete=Force Delete +force.remove.host.warning=Warning\: Choosing this option will cause CloudStack to forcefully stop all running virtual machines before removing this host from the cluster. +force.remove=Force Remove +force.stop.instance.warning=Warning\: Forcing a stop on this instance should be your last option. It can lead to data loss as well as inconsistent behavior of the virtual machine state. +force.stop=Force Stop +ICMP.code=ICMP Code +ICMP.type=ICMP Type +image.directory=Image Directory +inline=Inline +instances.actions.reboot.label=Reboot instance +label.accept.project.invitation=Accept project invitation +label.account.and.security.group=Account, Security group +label.account.id=Account ID +label.account.name=Account Name +label.account.specific=Account-Specific +label.account=Account +label.accounts=Accounts +label.acquire.new.ip=Acquire New IP +label.acquire.new.secondary.ip=Acquire new secondary IP +label.action.attach.disk.processing=Attaching Disk.... +label.action.attach.disk=Attach Disk +label.action.attach.iso.processing=Attaching ISO.... +label.action.attach.iso=Attach ISO +label.action.cancel.maintenance.mode.processing=Cancelling Maintenance Mode.... +label.action.cancel.maintenance.mode=Cancel Maintenance Mode +label.action.change.password=Change Password +label.action.configure.samlauthorization=Configure SAML SSO Authorization +label.action.change.service.processing=Changing Service.... +label.action.change.service=Change Service +label.action.copy.ISO.processing=Copying ISO.... +label.action.copy.ISO=Copy ISO +label.action.copy.template.processing=Copying Template.... +label.action.copy.template=Copy Template +label.action.create.template.from.vm=Create Template from VM +label.action.create.template.from.volume=Create Template from Volume +label.action.create.template.processing=Creating Template.... +label.action.create.template=Create Template +label.action.create.vm.processing=Creating VM.... +label.action.create.vm=Create VM +label.action.create.volume.processing=Creating Volume.... +label.action.create.volume=Create Volume +label.action.delete.account.processing=Deleting account.... +label.action.delete.account=Delete account +label.action.delete.cluster.processing=Deleting Cluster.... +label.action.delete.cluster=Delete Cluster +label.action.delete.disk.offering.processing=Deleting Disk Offering.... +label.action.delete.disk.offering=Delete Disk Offering +label.action.delete.domain.processing=Deleting Domain.... +label.action.delete.domain=Delete Domain +label.action.delete.firewall.processing=Deleting Firewall.... +label.action.delete.firewall=Delete firewall rule +label.action.delete.ingress.rule.processing=Deleting Ingress Rule.... +label.action.delete.ingress.rule=Delete Ingress Rule +label.action.delete.IP.range.processing=Deleting IP Range.... +label.action.delete.IP.range=Delete IP Range +label.action.delete.ISO.processing=Deleting ISO.... +label.action.delete.ISO=Delete ISO +label.action.delete.load.balancer.processing=Deleting Load Balancer.... +label.action.delete.load.balancer=Delete load balancer rule +label.action.delete.network.processing=Deleting Network.... +label.action.delete.network=Delete Network +label.action.delete.nexusVswitch=Delete Nexus 1000v +label.action.delete.physical.network=Delete physical network +label.action.delete.pod.processing=Deleting Pod.... +label.action.delete.pod=Delete Pod +label.action.delete.primary.storage.processing=Deleting Primary Storage.... +label.action.delete.primary.storage=Delete Primary Storage +label.action.delete.secondary.storage.processing=Deleting Secondary Storage.... +label.action.delete.secondary.storage=Delete Secondary Storage +label.action.delete.security.group.processing=Deleting Security Group.... +label.action.delete.security.group=Delete Security Group +label.action.delete.service.offering.processing=Deleting Service Offering.... +label.action.delete.service.offering=Delete Service Offering +label.action.delete.snapshot.processing=Deleting Snapshot.... +label.action.delete.snapshot=Delete Snapshot +label.action.delete.system.service.offering=Delete System Service Offering +label.action.delete.template.processing=Deleting Template.... +label.action.delete.template=Delete Template +label.action.delete.user.processing=Deleting User.... +label.action.delete.user=Delete User +label.action.delete.volume.processing=Deleting Volume.... +label.action.delete.volume=Delete Volume +label.action.delete.zone.processing=Deleting Zone.... +label.action.delete.zone=Delete Zone +label.action.destroy.instance.processing=Destroying Instance.... +label.action.destroy.instance=Destroy Instance +label.action.destroy.systemvm.processing=Destroying System VM.... +label.action.destroy.systemvm=Destroy System VM +label.action.detach.disk.processing=Detaching Disk.... +label.action.detach.disk=Detach Disk +label.action.detach.iso.processing=Detaching ISO.... +label.action.detach.iso=Detach ISO +label.action.disable.account.processing=Disabling account.... +label.action.disable.account=Disable account +label.action.disable.cluster.processing=Disabling Cluster.... +label.action.disable.cluster=Disable Cluster +label.action.disable.nexusVswitch=Disable Nexus 1000v +label.action.disable.physical.network=Disable physical network +label.action.disable.pod.processing=Disabling Pod.... +label.action.disable.pod=Disable Pod +label.action.disable.static.NAT.processing=Disabling Static NAT.... +label.action.disable.static.NAT=Disable Static NAT +label.action.disable.user.processing=Disabling User.... +label.action.disable.user=Disable User +label.action.disable.zone.processing=Disabling Zone.... +label.action.disable.zone=Disable Zone +label.action.download.ISO=Download ISO +label.action.download.template=Download Template +label.action.download.volume.processing=Downloading Volume.... +label.action.download.volume=Download Volume +label.action.edit.account=Edit account +label.action.edit.disk.offering=Edit Disk Offering +label.action.edit.domain=Edit Domain +label.action.edit.global.setting=Edit Global Setting +label.action.edit.host=Edit Host +label.action.edit.instance=Edit Instance +label.action.edit.ISO=Edit ISO +label.action.edit.network.offering=Edit Network Offering +label.action.edit.network.processing=Editing Network.... +label.action.edit.network=Edit Network +label.action.edit.pod=Edit Pod +label.action.edit.primary.storage=Edit Primary Storage +label.action.edit.resource.limits=Edit Resource Limits +label.action.edit.service.offering=Edit Service Offering +label.action.edit.template=Edit Template +label.action.edit.user=Edit User +label.action.edit.zone=Edit Zone +label.action.enable.account.processing=Enabling account.... +label.action.enable.account=Enable account +label.action.enable.cluster.processing=Enabling Cluster.... +label.action.enable.cluster=Enable Cluster +label.action.enable.maintenance.mode.processing=Enabling Maintenance Mode.... +label.action.enable.maintenance.mode=Enable Maintenance Mode +label.action.enable.nexusVswitch=Enable Nexus 1000v +label.action.enable.physical.network=Enable physical network +label.action.enable.pod.processing=Enabling Pod.... +label.action.enable.pod=Enable Pod +label.action.enable.static.NAT.processing=Enabling Static NAT.... +label.action.enable.static.NAT=Enable Static NAT +label.action.enable.user.processing=Enabling User.... +label.action.enable.user=Enable User +label.action.enable.zone.processing=Enabling Zone.... +label.action.enable.zone=Enable Zone +label.action.expunge.instance=Expunge Instance +label.action.expunge.instance.processing=Expunging Instance.... +label.action.force.reconnect.processing=Reconnecting.... +label.action.force.reconnect=Force Reconnect +label.action.generate.keys.processing=Generate Keys.... +label.action.generate.keys=Generate Keys +label.action.list.nexusVswitch=List Nexus 1000v +label.action.lock.account.processing=Locking account.... +label.action.lock.account=Lock account +label.action.manage.cluster.processing=Managing Cluster.... +label.action.manage.cluster=Manage Cluster +label.action.migrate.instance.processing=Migrating Instance.... +label.action.migrate.instance=Migrate Instance +label.action.migrate.router.processing=Migrating Router.... +label.action.migrate.router=Migrate Router +label.action.migrate.systemvm.processing=Migrating System VM.... +label.action.migrate.systemvm=Migrate System VM +label.action.reboot.instance.processing=Rebooting Instance.... +label.action.reboot.instance=Reboot Instance +label.action.reboot.router.processing=Rebooting Router.... +label.action.reboot.router=Reboot Router +label.action.reboot.systemvm.processing=Rebooting System VM.... +label.action.reboot.systemvm=Reboot System VM +label.action.recurring.snapshot=Recurring Snapshots +label.action.register.iso=Register ISO +label.action.register.template=Register template +label.action.release.ip.processing=Releasing IP.... +label.action.release.ip=Release IP +label.action.remove.host.processing=Removing Host.... +label.action.remove.host=Remove Host +label.action.reset.password.processing=Resetting Password.... +label.action.reset.password=Reset Password +label.action.resize.volume.processing=Resizing Volume.... +label.action.resize.volume=Resize Volume +label.action.resource.limits=Resource limits +label.action.restore.instance.processing=Restoring Instance.... +label.action.restore.instance=Restore Instance +label.action.start.instance.processing=Starting Instance.... +label.action.start.instance=Start Instance +label.action.start.router.processing=Starting Router.... +label.action.start.router=Start Router +label.action.start.systemvm.processing=Starting System VM.... +label.action.start.systemvm=Start System VM +label.action.stop.instance.processing=Stopping Instance.... +label.action.stop.instance=Stop Instance +label.action.stop.router.processing=Stopping Router.... +label.action.stop.router=Stop Router +label.action.stop.systemvm.processing=Stopping System VM.... +label.action.stop.systemvm=Stop System VM +label.action.take.snapshot.processing=Taking Snapshot.... +label.action.take.snapshot=Take Snapshot +label.action.revert.snapshot.processing=Reverting to Snapshot... +label.action.revert.snapshot=Revert to Snapshot +label.action.unmanage.cluster.processing=Unmanaging Cluster.... +label.action.unmanage.cluster=Unmanage Cluster +label.action.update.OS.preference.processing=Updating OS Preference.... +label.action.update.OS.preference=Update OS Preference +label.action.update.resource.count.processing=Updating Resource Count.... +label.action.update.resource.count=Update Resource Count +label.action.vmsnapshot.create=Take VM Snapshot +label.action.vmsnapshot.delete=Delete VM snapshot +label.action.vmsnapshot.revert=Revert to VM snapshot +label.actions=Actions +label.activate.project=Activate Project +label.active.sessions=Active Sessions +label.add.account.to.project=Add account to project +label.add.account=Add Account +label.add.accounts.to=Add accounts to +label.add.accounts=Add accounts +label.add.ACL=Add ACL +label.add.affinity.group=Add new affinity group +label.add.BigSwitchVns.device=Add BigSwitch Vns Controller +label.add.by.cidr=Add By CIDR +label.add.by.group=Add By Group +label.add.by=Add by +label.add.cluster=Add Cluster +label.add.compute.offering=Add compute offering +label.add.direct.iprange=Add Direct Ip Range +label.add.disk.offering=Add Disk Offering +label.add.domain=Add Domain +label.add.egress.rule=Add egress rule +label.add.F5.device=Add F5 device +label.add.firewall=Add firewall rule +label.add.guest.network=Add guest network +label.add.isolated.guest.network=Add Isolated Guest Network +label.add.host=Add Host +label.add.ingress.rule=Add Ingress Rule +label.add.intermediate.certificate=Add intermediate certificate +label.add.ip.range=Add IP Range +label.add.load.balancer=Add Load Balancer +label.add.more=Add More +label.add.netScaler.device=Add Netscaler device +label.add.network.ACL=Add network ACL +label.add.network.device=Add Network Device +label.add.network.offering=Add network offering +label.add.network=Add Network +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.NuageVsp.device=Add Nuage Virtualized Services Directory (VSD) +label.add.BrocadeVcs.device=Add Brocade Vcs Switch +label.add.physical.network=Add physical network +label.add.pod=Add Pod +label.add.port.forwarding.rule=Add port forwarding rule +label.add.primary.storage=Add Primary Storage +label.add.region=Add Region +label.add.resources=Add Resources +label.add.route=Add route +label.add.rule=Add rule +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 +label.add.template=Add Template +label.add.to.group=Add to group +label.add.user=Add User +label.add.vlan=Add VLAN +label.add.vxlan=Add VXLAN +label.add.VM.to.tier=Add VM to tier +label.add.vm=Add VM +label.add.vms.to.lb=Add VM(s) to load balancer rule +label.add.vms=Add VMs +label.add.volume=Add Volume +label.add.vpc=Add VPC +label.add.vpn.customer.gateway=Add VPN Customer Gateway +label.add.VPN.gateway=Add VPN Gateway +label.add.vpn.user=Add VPN user +label.add.zone=Add Zone +label.add=Add +label.adding.cluster=Adding Cluster +label.adding.failed=Adding Failed +label.adding.pod=Adding Pod +label.adding.processing=Adding.... +label.adding.succeeded=Adding Succeeded +label.adding.user=Adding User +label.adding.zone=Adding Zone +label.adding=Adding +label.additional.networks=Additional Networks +label.admin.accounts=Admin Accounts +label.admin=Admin +label.advanced.mode=Advanced Mode +label.advanced.search=Advance Search +label.advanced=Advanced +label.affinity.group=Affinity Group +label.affinity.groups=Affinity Groups +label.affinity=Affinity +label.agent.password=Agent Password +label.agent.username=Agent Username +label.agree=Agree +label.alert=Alert +label.algorithm=Algorithm +label.allocated=Allocated +label.allocation.state=Allocation State +label.anti.affinity.group=Anti-affinity Group +label.anti.affinity.groups=Anti-affinity Groups +label.anti.affinity=Anti-affinity +label.api.key=API Key +label.api.version=API Version +label.apply=Apply +label.assign.to.load.balancer=Assigning instance to load balancer +label.assign=Assign +label.associated.network.id=Associated Network ID +label.associated.network=Associated Network +label.attached.iso=Attached ISO +label.author.email=Author e-mail +label.author.name=Author name +label.availability.zone=Availability Zone +label.availability=Availability +label.available.public.ips=Available Public IP Addresses +label.available=Available +label.back=Back +label.bandwidth=Bandwidth +label.basic.mode=Basic Mode +label.basic=Basic +label.bigswitch.controller.address=BigSwitch Vns Controller Address +label.bootable=Bootable +label.broadcast.domain.range=Broadcast domain range +label.broadcast.domain.type=Broadcast Domain Type +label.broadcast.uri=Broadcast URI +label.by.account=By Account +label.by.availability=By Availability +label.by.domain=By Domain +label.by.end.date=By End Date +label.by.level=By Level +label.by.pod=By Pod +label.by.role=By Role +label.by.start.date=By Start Date +label.by.state=By State +label.by.traffic.type=By Traffic Type +label.by.type.id=By Type ID +label.by.type=By Type +label.by.zone=By Zone +label.bytes.received=Bytes Received +label.bytes.sent=Bytes Sent +label.cancel=Cancel +label.capacity=Capacity +label.capacity.bytes=Capacity Bytes +label.capacity.iops=Capacity IOPS +label.certificate=Server certificate +label.change.service.offering=Change service offering +label.change.value=Change value +label.character=Character +label.md5.checksum=MD5 checksum +label.cidr.account=CIDR or Account/Security Group +label.CIDR.list=CIDR list +label.cidr.list=Source CIDR +label.CIDR.of.destination.network=CIDR of destination network +label.cidr=CIDR +label.clean.up=Clean up +label.clear.list=Clear list +label.close=Close +label.cloud.console=Cloud Management Console +label.cloud.managed=Cloud.com Managed +label.cluster.name=Cluster Name +label.cluster.type=Cluster Type +label.cluster=Cluster +label.clusters=Clusters +label.clvm=CLVM +label.rbd=RBD +label.rbd.monitor=Ceph monitor +label.rbd.pool=Ceph pool +label.rbd.id=Cephx user +label.rbd.secret=Cephx secret +label.code=Code +label.community=Community +label.compute.and.storage=Compute and Storage +label.compute.offering=Compute offering +label.compute.offerings=Compute Offerings +label.compute=Compute +label.configuration=Configuration +label.configure.network.ACLs=Configure Network ACLs +label.configure.vpc=Configure VPC +label.configure=Configure +label.confirm.password=Confirm password +label.confirmation=Confirmation +label.congratulations=Congratulations\! +label.conserve.mode=Conserve mode +label.console.proxy=Console proxy +label.continue.basic.install=Continue with basic installation +label.continue=Continue +label.corrections.saved=Corrections saved +label.cpu.allocated.for.VMs=CPU Allocated for VMs +label.cpu.allocated=CPU Allocated +label.CPU.cap=CPU Cap +label.cpu.limits=CPU limits +label.cpu.mhz=CPU (in MHz) +label.cpu.utilized=CPU Utilized +label.cpu=CPU +label.create.project=Create project +label.create.template=Create template +label.create.VPN.connection=Create VPN Connection +label.created.by.system=Created by system +label.created=Created +label.cross.zones=Cross Zones +label.custom.disk.size=Custom Disk Size +label.daily=Daily +label.data.disk.offering=Data Disk Offering +label.date=Date +label.day.of.month=Day of Month +label.day.of.week=Day of Week +label.dead.peer.detection=Dead Peer Detection +label.decline.invitation=Decline invitation +label.dedicated=Dedicated +label.default.use=Default Use +label.default.view=Default View +label.default=Default +label.delete.affinity.group=Delete Affinity Group +label.delete.BigSwitchVns=Remove BigSwitch Vns Controller +label.delete.F5=Delete F5 +label.delete.gateway=Delete gateway +label.delete.NetScaler=Delete NetScaler +label.delete.NiciraNvp=Remove Nvp Controller +label.delete.NuageVsp=Remove Nuage VSD +label.delete.BrocadeVcs=Remove Brocade Vcs Switch +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 +label.delete.vpn.user=Delete VPN user +label.delete=Delete +label.deleting.failed=Deleting Failed +label.deleting.processing=Deleting.... +label.description=Description +label.destination.physical.network.id=Destination physical network ID +label.destination.zone=Destination Zone +label.destroy.router=Destroy router +label.destroy=Destroy +label.detaching.disk=Detaching Disk +label.details=Details +label.device.id=Device ID +label.devices=Devices +label.DHCP.server.type=DHCP Server Type +label.dhcp=DHCP +label.direct.ips=Shared Network IPs +label.disable.provider=Disable provider +label.disable.vpn=Disable Remote Access VPN +label.disabled=Disabled +label.disabling.vpn.access=Disabling VPN Access +label.disk.allocated=Disk Allocated +label.disk.bytes.read.rate=Disk Read Rate (BPS) +label.disk.bytes.write.rate=Disk Write Rate (BPS) +label.disk.iops.read.rate=Disk Read Rate (IOPS) +label.disk.iops.write.rate=Disk Write Rate (IOPS) +label.disk.offering=Disk Offering +label.disk.provisioningtype=Provisioning Type +label.disk.read.bytes=Disk Read (Bytes) +label.disk.read.io=Disk Read (IO) +label.disk.size.gb=Disk Size (in GB) +label.disk.size=Disk Size +label.disk.total=Disk Total +label.disk.volume=Disk Volume +label.disk.write.bytes=Disk Write (Bytes) +label.disk.write.io=Disk Write (IO) +label.display.name=Display name +label.display.text=Display Text +label.dns.1=DNS 1 +label.dns.2=DNS 2 +label.DNS.domain.for.guest.networks=DNS domain for Guest Networks +label.dns=DNS +label.domain.admin=Domain Admin +label.domain.id=Domain ID +label.domain.name=Domain Name +label.domain.router=Domain router +label.domain.suffix=DNS Domain Suffix (i.e., xyz.com) +label.domain=Domain +label.done=Done +label.double.quotes.are.not.allowed=Double quotes are not allowed +label.download.progress=Download Progress +label.drag.new.position=Drag to new position +label.edit.affinity.group=Edit Affinity Group +label.edit.lb.rule=Edit LB rule +label.edit.network.details=Edit network details +label.edit.project.details=Edit project details +label.edit.tags=Edit tags +label.edit.traffic.type=Edit traffic type +label.edit.vpc=Edit VPC +label.edit=Edit +label.egress.rule=Egress rule +label.egress.rules=Egress rules +label.elastic.IP=Elastic IP +label.elastic.LB=Elastic LB +label.elastic=Elastic +label.email=Email +label.enable.provider=Enable provider +label.enable.s3=Enable S3-backed Secondary Storage +label.enable.swift=Enable Swift +label.enable.vpn=Enable Remote Access VPN +label.enabling.vpn.access=Enabling VPN Access +label.enabling.vpn=Enabling VPN +label.end.IP=End IP +label.end.port=End Port +label.end.reserved.system.IP=End Reserved system IP +label.end.vlan=End VLAN +label.end.vxlan=End VXLAN +label.endpoint.or.operation=Endpoint or Operation +label.endpoint=Endpoint +label.enter.token=Enter token +label.error.code=Error Code +label.error=Error +label.ESP.encryption=ESP Encryption +label.ESP.hash=ESP Hash +label.ESP.lifetime=ESP Lifetime (second) +label.ESP.policy=ESP policy +label.esx.host=ESX/ESXi Host +label.example=Example +label.expunge=Expunge +label.external.link=External link +label.f5=F5 +label.failed=Failed +label.featured=Featured +label.fetch.latest=Fetch latest +label.filterBy=Filter by +label.firewall=Firewall +label.first.name=First Name +label.format=Format +label.fqdn=FQDN +label.friday=Friday +label.full.path=Full path +label.full=Full +label.gateway=Gateway +label.general.alerts=General Alerts +label.generating.url=Generating URL +label.gluster.volume=Volume +label.go.step.2=Go to Step 2 +label.go.step.3=Go to Step 3 +label.go.step.4=Go to Step 4 +label.go.step.5=Go to Step 5 +label.group.optional=Group (Optional) +label.group=Group +label.guest.cidr=Guest CIDR +label.guest.end.ip=Guest end IP +label.guest.gateway=Guest Gateway +label.guest.ip.range=Guest IP Range +label.guest.ip=Guest IP Address +label.guest.netmask=Guest Netmask +label.guest.networks=Guest networks +label.guest.start.ip=Guest start IP +label.guest.traffic=Guest Traffic +label.guest.type=Guest Type +label.guest=Guest +label.ha.enabled=HA Enabled +label.help=Help +label.hide.ingress.rule=Hide Ingress Rule +label.hints=Hints +label.host.alerts=Host Alerts +label.host.MAC=Host MAC +label.host.name=Host Name +label.host.tags=Host Tags +label.host=Host +label.hosts=Hosts +label.hourly=Hourly +label.hypervisor.capabilities=Hypervisor capabilities +label.hypervisor.type=Hypervisor Type +label.hypervisor.version=Hypervisor version +label.hypervisor=Hypervisor +label.id=ID +label.IKE.DH=IKE DH +label.IKE.encryption=IKE Encryption +label.IKE.hash=IKE Hash +label.IKE.lifetime=IKE lifetime (second) +label.IKE.policy=IKE policy +label.info=Info +label.ingress.rule=Ingress Rule +label.initiated.by=Initiated By +label.installWizard.addClusterIntro.subtitle=What is a cluster? +label.installWizard.addClusterIntro.title=Let’s add a cluster +label.installWizard.addHostIntro.subtitle=What is a host? +label.installWizard.addHostIntro.title=Let’s add a host +label.installWizard.addPodIntro.subtitle=What is a pod? +label.installWizard.addPodIntro.title=Let’s add a pod +label.installWizard.addPrimaryStorageIntro.subtitle=What is primary storage? +label.installWizard.addPrimaryStorageIntro.title=Let’s add primary storage +label.installWizard.addSecondaryStorageIntro.subtitle=What is secondary storage? +label.installWizard.addSecondaryStorageIntro.title=Let’s add secondary storage +label.installWizard.addZone.title=Add zone +label.installWizard.addZoneIntro.subtitle=What is a zone? +label.installWizard.addZoneIntro.title=Let’s add a zone +label.installWizard.click.launch=Click the launch button. +label.installWizard.subtitle=This tour will aid you in setting up your CloudStack&\#8482 installation +label.installWizard.title=Hello and Welcome to CloudStack&\#8482 +label.instance.limits=Instance Limits +label.instance.name=Instance Name +label.instance=Instance +label.instances=Instances +label.intermediate.certificate=Intermediate certificate {0} +label.internal.dns.1=Internal DNS 1 +label.internal.dns.2=Internal DNS 2 +label.internal.name=Internal name +label.interval.type=Interval Type +label.introduction.to.cloudstack=Introduction to CloudStack&\#8482 +label.invalid.integer=Invalid Integer +label.invalid.number=Invalid Number +label.invitations=Invitations +label.invite.to=Invite to +label.invite=Invite +label.invited.accounts=Invited accounts +label.ip.address=IP Address +label.ip.allocations=IP Allocations +label.ip.limits=Public IP Limits +label.ip.or.fqdn=IP or FQDN +label.ip.range=IP Range +label.ip.ranges=IP Ranges +label.ip=IP +label.ipaddress=IP Address +label.ips=IPs +label.IPsec.preshared.key=IPsec Preshared-Key +label.is.default=Is Default +label.is.redundant.router=Redundant +label.is.shared=Is Shared +label.is.system=Is System +label.iscsi=iSCSI +label.iso.boot=ISO Boot +label.iso=ISO +label.isolated.networks=Isolated networks +label.isolation.method=Isolation method +label.isolation.mode=Isolation Mode +label.isolation.uri=Isolation URI +label.item.listing=Item listing +label.keep=Keep +label.key=Key +label.keyboard.type=Keyboard type +label.kvm.traffic.label=KVM traffic label +label.label=Label +label.lang.arabic=Arabic +label.lang.brportugese=Brazilian Portugese +label.lang.catalan=Catalan +label.lang.chinese=Chinese (Simplified) +label.lang.dutch=Dutch (Netherlands) +label.lang.english=English +label.lang.french=French +label.lang.german=German +label.lang.italian=Italian +label.lang.japanese=Japanese +label.lang.korean=Korean +label.lang.norwegian=Norwegian +label.lang.polish=Polish +label.lang.russian=Russian +label.lang.spanish=Spanish +label.lang.hungarian=Hungarian +label.last.disconnected=Last Disconnected +label.last.name=Last Name +label.latest.events=Latest events +label.launch.vm=Launch VM +label.launch.zone=Launch zone +label.launch=Launch +label.lb.algorithm.leastconn=Least connections +label.lb.algorithm.roundrobin=Round-robin +label.lb.algorithm.source=Source +label.LB.isolation=LB isolation +label.level=Level +label.linklocal.ip=Link Local IP Address +label.load.balancer=Load Balancer +label.load.balancing.policies=Load balancing policies +label.load.balancing=Load Balancing +label.loading=Loading +label.local.storage.enabled=Enable local storage for User VMs +label.local.storage.enabled.system.vms=Enable local storage for System VMs +label.local.storage=Local Storage +label.local=Local +label.login=Login +label.logout=Logout +label.saml.enable=Authorize SAML SSO +label.saml.entity=Identity Provider +label.add.LDAP.account=Add LDAP Account +label.LUN.number=LUN \# +label.lun=LUN +label.make.project.owner=Make account project owner +label.manage.resources=Manage Resources +label.management.server=Management Server +label.manage=Manage +label.managed=Managed +label.management.ips=Management IP Addresses +label.management=Management +label.max.cpus=Max. CPU cores +label.max.guest.limit=Max guest limit +label.max.memory=Max. memory (MiB) +label.max.networks=Max. networks +label.max.primary.storage=Max. primary (GiB) +label.max.public.ips=Max. public IPs +label.max.secondary.storage=Max. secondary (GiB) +label.max.snapshots=Max. snapshots +label.max.templates=Max. templates +label.max.vms=Max. user VMs +label.max.volumes=Max. volumes +label.max.vpcs=Max. VPCs +label.maximum=Maximum +label.may.continue=You may now continue. +label.memory.allocated=Memory Allocated +label.memory.limits=Memory limits (MiB) +label.memory.mb=Memory (in MB) +label.memory.total=Memory Total +label.memory.used=Memory Used +label.memory=Memory +label.menu.accounts=Accounts +label.menu.alerts=Alerts +label.menu.all.accounts=All Accounts +label.menu.all.instances=All Instances +label.menu.community.isos=Community ISOs +label.menu.community.templates=Community Templates +label.menu.configuration=Configuration +label.menu.dashboard=Dashboard +label.menu.destroyed.instances=Destroyed Instances +label.menu.disk.offerings=Disk Offerings +label.menu.domains=Domains +label.menu.events=Events +label.menu.featured.isos=Featured ISOs +label.menu.featured.templates=Featured Templates +label.menu.global.settings=Global Settings +label.menu.infrastructure=Infrastructure +label.menu.instances=Instances +label.menu.ipaddresses=IP Addresses +label.menu.isos=ISOs +label.menu.my.accounts=My Accounts +label.menu.my.instances=My Instances +label.menu.my.isos=My ISOs +label.menu.my.templates=My Templates +label.menu.network.offerings=Network Offerings +label.menu.network=Network +label.menu.physical.resources=Physical Resources +label.menu.regions=Regions +label.menu.running.instances=Running Instances +label.menu.security.groups=Security Groups +label.menu.service.offerings=Service Offerings +label.menu.snapshots=Snapshots +label.menu.stopped.instances=Stopped Instances +label.menu.storage=Storage +label.menu.system.service.offerings=System Offerings +label.menu.system.vms=System VMs +label.menu.system=System +label.menu.templates=Templates +label.menu.virtual.appliances=Virtual Appliances +label.menu.virtual.resources=Virtual Resources +label.menu.volumes=Volumes +label.migrate.instance.to.host=Migrate instance to another host +label.migrate.instance.to.ps=Migrate instance to another primary storage +label.migrate.instance.to=Migrate instance to +label.migrate.router.to=Migrate Router to +label.migrate.systemvm.to=Migrate System VM to +label.migrate.to.host=Migrate to host +label.migrate.to.storage=Migrate to storage +label.migrate.volume=Migrate Volume +label.migrate.volume.to.primary.storage=Migrate volume to another primary storage +label.minimum=Minimum +label.minute.past.hour=minute(s) past the hour +label.monday=Monday +label.monthly=Monthly +label.more.templates=More Templates +label.move.down.row=Move down one row +label.move.to.bottom=Move to bottom +label.move.to.top=Move to top +label.move.up.row=Move up one row +label.my.account=My Account +label.my.network=My network +label.my.templates=My templates +label.na=N/A +label.name.optional=Name (Optional) +label.name=Name +label.nat.port.range=NAT Port Range +label.netmask=Netmask +label.netScaler=NetScaler +label.network.ACL.total=Network ACL Total +label.network.ACL=Network ACL +label.network.ACLs=Network ACLs +label.network.desc=Network Desc +label.network.device.type=Network Device Type +label.network.device=Network Device +label.network.domain.text=Network domain +label.network.domain=Network Domain +label.network.id=Network ID +label.network.label.display.for.blank.value=Use default gateway +label.network.limits=Network limits +label.network.name=Network Name +label.network.offering.display.text=Network Offering Display Text +label.network.offering.id=Network Offering ID +label.network.offering.name=Network Offering Name +label.network.offering=Network Offering +label.network.rate.megabytes=Network Rate (MB/s) +label.network.rate=Network Rate (Mb/s) +label.network.read=Network Read +label.network.service.providers=Network Service Providers +label.network.type=Network Type +label.network.write=Network Write +label.network=Network +label.networking.and.security=Networking and security +label.networks=Networks +label.new.password=New Password +label.new.project=New Project +label.new.vm=New VM +label.new=New +label.next=Next +label.nexusVswitch=Nexus 1000v +label.nfs.server=NFS Server +label.nfs.storage=NFS Storage +label.nfs=NFS +label.nic.adapter.type=NIC adapter type +label.nicira.controller.address=Controller Address +label.nicira.l3gatewayserviceuuid=L3 Gateway Service Uuid +label.nicira.transportzoneuuid=Transport Zone Uuid +label.brocade.vcs.address=Vcs Switch Address +label.nics=NICs +label.no.actions=No Available Actions +label.no.alerts=No Recent Alerts +label.no.data=No data to show +label.no.errors=No Recent Errors +label.no.isos=No available ISOs +label.no.items=No Available Items +label.no.security.groups=No Available Security Groups +label.no.thanks=No thanks +label.no=No +label.none=None +label.not.found=Not Found +label.notifications=Notifications +label.num.cpu.cores=\# of CPU Cores +label.number.of.clusters=Number of Clusters +label.number.of.hosts=Number of Hosts +label.number.of.pods=Number of Pods +label.number.of.system.vms=Number of System VMs +label.number.of.virtual.routers=Number of Virtual Routers +label.number.of.zones=Number of Zones +label.numretries=Number of Retries +label.ocfs2=OCFS2 +label.offer.ha=Offer HA +label.ok=OK +label.optional=Optional +label.order=Order +label.os.preference=OS Preference +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 +label.path=Path +label.perfect.forward.secrecy=Perfect Forward Secrecy +label.physical.network.ID=Physical network ID +label.physical.network=Physical Network +label.PING.CIFS.password=PING CIFS password +label.PING.CIFS.username=PING CIFS username +label.PING.dir=PING Directory +label.PING.storage.IP=PING storage IP +label.please.specify.netscaler.info=Please specify Netscaler info +label.please.wait=Please Wait +label.plugin.details=Plugin details +label.plugins=Plugins +label.pod.name=Pod name +label.pod=Pod +label.pods=Pods +label.port.forwarding.policies=Port forwarding policies +label.port.forwarding=Port Forwarding +label.port.range=Port Range +label.PreSetup=PreSetup +label.prev=Prev +label.previous=Previous +label.primary.allocated=Primary Storage Allocated +label.primary.network=Primary Network +label.primary.storage.count=Primary Storage Pools +label.primary.storage.limits=Primary Storage limits (GiB) +label.primary.storage=Primary Storage +label.primary.used=Primary Storage Used +label.private.Gateway=Private Gateway +label.private.interface=Private Interface +label.private.ip.range=Private IP Range +label.private.ip=Private IP Address +label.private.ips=Private IP Addresses +label.private.network=Private network +label.private.port=Private Port +label.private.zone=Private Zone +label.privatekey=PKCS\#8 Private Key +label.project.dashboard=Project dashboard +label.project.id=Project ID +label.project.invite=Invite to project +label.project.name=Project name +label.project.businessservice=Business Service +label.project.client=Client +label.project.component=Component +label.project.subcomponent=Sub-component +label.project.product=Product +label.project.detailedusage=Detailed Usage +label.project.view=Project View +label.project=Project +label.projects=Projects +label.protocol=Protocol +label.provider=Provider +label.providers=Providers +label.public.interface=Public Interface +label.public.ip=Public IP Address +label.public.ips=Public IP Addresses +label.public.network=Public network +label.public.port=Public Port +label.public.traffic=Public traffic +label.public.zone=Public Zone +label.public=Public +label.purpose=Purpose +label.Pxe.server.type=Pxe Server Type +label.quickview=Quickview +label.reboot=Reboot +label.recent.errors=Recent Errors +label.redundant.router.capability=Redundant router capability +label.redundant.router=Redundant Router +label.redundant.state=Redundant state +label.refresh=Refresh +label.region=Region +label.related=Related +label.remind.later=Remind me later +label.remove.ACL=Remove ACL +label.remove.egress.rule=Remove egress rule +label.remove.from.load.balancer=Removing instance from load balancer +label.remove.ingress.rule=Remove ingress rule +label.remove.ip.range=Remove IP range +label.remove.pf=Remove port forwarding rule +label.remove.project.account=Remove account from project +label.remove.region=Remove Region +label.remove.rule=Remove rule +label.remove.static.nat.rule=Remove static NAT rule +label.remove.static.route=Remove static route +label.remove.tier=Remove tier +label.remove.vm.from.lb=Remove VM from load balancer rule +label.remove.vpc=Remove VPC +label.removing.user=Removing User +label.removing=Removing +label.required=Required +label.reserved.system.gateway=Reserved system gateway +label.reserved.system.ip=Reserved System IP +label.reserved.system.netmask=Reserved system netmask +label.reset.VPN.connection=Reset VPN connection +label.resize.new.offering.id=New Offering +label.resize.new.size=New Size (GB) +label.resize.shrink.ok=Shrink OK +label.resource.limits=Resource Limits +label.resource.state=Resource state +label.resource=Resource +label.resources=Resources +label.restart.network=Restart network +label.restart.required=Restart required +label.restart.vpc=Restart VPC +label.restore=Restore +label.retry.interval=Retry Interval +label.review=Review +label.revoke.project.invite=Revoke invitation +label.role=Role +label.root.certificate=Root certificate +label.root.disk.controller=Root disk controller +label.root.disk.offering=Root Disk Offering +label.rules=Rules +label.running.vms=Running VMs +label.s3.access_key=Access Key +label.s3.bucket=Bucket +label.s3.connection_timeout=Connection Timeout +label.s3.endpoint=Endpoint +label.s3.max_error_retry=Max Error Retry +label.s3.secret_key=Secret Key +label.s3.socket_timeout=Socket Timeout +label.s3.use_https=Use HTTPS +label.saturday=Saturday +label.save.and.continue=Save and continue +label.save=Save +label.saving.processing=Saving.... +label.scope=Scope +label.search=Search +label.secondary.storage.count=Secondary Storage Pools +label.secondary.storage.limits=Secondary Storage limits (GiB) +label.secondary.storage.vm=Secondary storage VM +label.secondary.storage=Secondary Storage +label.secondary.used=Secondary Storage Used +label.secret.key=Secret Key +label.security.group.name=Security Group Name +label.security.group=Security Group +label.security.groups.enabled=Security Groups Enabled +label.security.groups=Security Groups +label.select-view=Select view +label.select.a.template=Select a template +label.select.a.zone=Select a zone +label.select.instance.to.attach.volume.to=Select instance to attach volume to +label.select.instance=Select instance +label.select.iso.or.template=Select ISO or template +label.select.offering=Select offering +label.select.project=Select Project +label.select.tier=Select Tier +label.select.vm.for.static.nat=Select VM for static NAT +label.select=Select +label.sent=Sent +label.server=Server +label.service.capabilities=Service Capabilities +label.service.offering=Service Offering +label.session.expired=Session Expired +label.set.up.zone.type=Set up zone type +label.setup.network=Setup Network +label.setup.zone=Setup Zone +label.setup=Setup +label.shared=Shared +label.SharedMountPoint=SharedMountPoint +label.show.ingress.rule=Show Ingress Rule +label.shutdown.provider=Shutdown provider +label.site.to.site.VPN=Site-to-site VPN +label.size=Size +label.skip.guide=I have used CloudStack before, skip this guide +label.snapshot.limits=Snapshot Limits +label.snapshot.name=Snapshot Name +label.snapshot.s=Snapshot(s) +label.snapshot.schedule=Setup Recurring Snapshot +label.snapshot=Snapshot +label.snapshots=Snapshots +label.source.nat=Source NAT +label.specify.IP.ranges=Specify IP ranges +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 +label.start.vlan=Start VLAN +label.start.vxlan=Start VXLAN +label.state=State +label.static.nat.enabled=Static NAT Enabled +label.static.nat.to=Static NAT to +label.static.nat.vm.details=Static NAT VM Details +label.static.nat=Static NAT +label.statistics=Statistics +label.status=Status +label.step.1.title=Step 1\: Select a Template +label.step.1=Step 1 +label.step.2.title=Step 2\: Service Offering +label.step.2=Step 2 +label.step.3.title=Step 3\: Select a Disk Offering +label.step.3=Step 3 +label.step.4.title=Step 4\: Network +label.step.4=Step 4 +label.step.5.title=Step 5\: Review +label.step.5=Step 5 +label.stickiness=Stickiness +label.sticky.cookie-name=Cookie name +label.sticky.domain=Domain +label.sticky.expire=Expires +label.sticky.holdtime=Hold time +label.sticky.indirect=Indirect +label.sticky.length=Length +label.sticky.mode=Mode +label.sticky.nocache=No cache +label.sticky.postonly=Post only +label.sticky.prefix=Prefix +label.sticky.request-learn=Request learn +label.sticky.tablesize=Table size +label.stop=Stop +label.stopped.vms=Stopped VMs +label.storage.tags=Storage Tags +label.storage.traffic=Storage Traffic +label.storage.type=Storage Type +label.qos.type=QoS Type +label.cache.mode=Write-cache Type +label.storage=Storage +label.subdomain.access=Subdomain Access +label.submit=Submit +label.submitted.by=[Submitted by\: ] +label.succeeded=Succeeded +label.sunday=Sunday +label.super.cidr.for.guest.networks=Super CIDR for Guest Networks +label.supported.services=Supported Services +label.supported.source.NAT.type=Supported Source NAT type +label.suspend.project=Suspend Project +label.system.capacity=System Capacity +label.system.offering=System Offering +label.system.service.offering=System Service Offering +label.system.vm.type=System VM Type +label.system.vm=System VM +label.system.vms=System VMs +label.system.wide.capacity=System-wide capacity +label.tagged=Tagged +label.tags=Tags +label.target.iqn=Target IQN +label.task.completed=Task completed +label.template.limits=Template Limits +label.template=Template +label.TFTP.dir=TFTP Directory +label.theme.default=Default Theme +label.theme.grey=Custom - Grey +label.theme.lightblue=Custom - Light Blue +label.thursday=Thursday +label.tier.details=Tier details +label.tier=Tier +label.time.zone=Timezone +label.time=Time +label.timeout.in.second = Timeout (seconds) +label.timeout=Timeout +label.timezone=Timezone +label.token=Token +label.total.CPU=Total CPU +label.total.cpu=Total CPU +label.total.hosts=Total Hosts +label.total.memory=Total Memory +label.total.of.ip=Total of IP Addresses +label.total.of.vm=Total of VMs +label.total.storage=Total Storage +label.total.vms=Total VMs +label.traffic.label=Traffic label +label.traffic.type=Traffic Type +label.traffic.types=Traffic Types +label.tuesday=Tuesday +label.type.id=Type ID +label.type=Type +label.unavailable=Unavailable +label.unlimited=Unlimited +label.untagged=Untagged +label.update.project.resources=Update project resources +label.update.ssl.cert= SSL Certificate +label.update.ssl= SSL Certificate +label.updating=Updating +label.upload.volume=Upload volume +label.upload=Upload +label.url=URL +label.usage.interface=Usage Interface +label.use.vm.ip=Use VM IP\: +label.used=Used +label.user=User +label.username=Username +label.users=Users +label.value=Value +label.vcdcname=vCenter DC name +label.vcenter.cluster=vCenter Cluster +label.vcenter.datacenter=vCenter Datacenter +label.vcenter.datastore=vCenter Datastore +label.vcenter.host=vCenter Host +label.vcenter.password=vCenter Password +label.vcenter.username=vCenter Username +label.vcipaddress=vCenter IP Address +label.version=Version +label.view.all=View all +label.view.console=View console +label.view.more=View more +label.view=View +label.viewing=Viewing +label.virtual.appliance=Virtual Appliance +label.virtual.appliances=Virtual Appliances +label.virtual.machines=Virtual machines +label.virtual.network=Virtual Network +label.virtual.router=Virtual Router +label.virtual.routers=Virtual Routers +label.vlan.id=VLAN/VNI ID +label.vlan.range=VLAN/VNI Range +label.vlan=VLAN/VNI +label.vnet=VLAN/VNI +label.vnet.id=VLAN/VNI ID +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN Range +label.vxlan=VXLAN +label.vm.add=Add Instance +label.vm.destroy=Destroy +label.vm.display.name=VM display name +label.vm.name=VM name +label.vm.reboot=Reboot +label.vm.start=Start +label.vm.state=VM state +label.vm.stop=Stop +label.VMFS.datastore=VMFS datastore +label.vmfs=VMFS +label.VMs.in.tier=VMs in tier +label.vms=VMs +label.vmsnapshot.current=isCurrent +label.vmsnapshot.memory=Snapshot memory +label.vmsnapshot.parentname=Parent +label.vmsnapshot.type=Type +label.vmsnapshot=VM Snapshots +label.vmware.traffic.label=VMware traffic label +label.volgroup=Volume Group +label.volume.limits=Volume Limits +label.volume.name=Volume Name +label.volume=Volume +label.volumes=Volumes +label.vpc.id=VPC ID +label.VPC.router.details=VPC router details +label.vpc=VPC +label.VPN.connection=VPN Connection +label.VPN.customer.gateway=VPN Customer Gateway +label.vpn.customer.gateway=VPN Customer Gateway +label.VPN.gateway=VPN Gateway +label.vpn=VPN +label.vsmctrlvlanid=Control VLAN ID +label.vsmpktvlanid=Packet VLAN ID +label.vsmstoragevlanid=Storage VLAN ID +label.vsphere.managed=vSphere Managed +label.waiting=Waiting +label.warn=Warn +label.wednesday=Wednesday +label.weekly=Weekly +label.welcome.cloud.console=Welcome to Management Console +label.welcome=Welcome +label.what.is.cloudstack=What is CloudStack&\#8482? +label.xenserver.traffic.label=XenServer traffic label +label.yes=Yes +label.zone.details=Zone details +label.zone.id=Zone ID +label.zone.name=Zone name +label.zone.step.1.title=Step 1\: Select a Network +label.zone.step.2.title=Step 2\: Add a Zone +label.zone.step.3.title=Step 3\: Add a Pod +label.zone.step.4.title=Step 4\: Add an IP range +label.zone.type=Zone Type +label.zone.wide=Zone-Wide +label.zone=Zone +label.zones=Zones +label.zoneWizard.trafficType.guest=Guest\: Traffic between end-user virtual machines +label.zoneWizard.trafficType.management=Management\: Traffic between CloudStack\\\\'s internal resources, including any components that communicate with the Management Server, such as hosts and CloudStack system VMs +label.zoneWizard.trafficType.public=Public\: Traffic between the internet and virtual machines in the cloud. +label.zoneWizard.trafficType.storage=Storage\: Traffic between primary and secondary storage servers, such as VM templates and snapshots +label.ldap.group.name=LDAP Group +label.password.reset.confirm=Password has been reset to +label.provider=Provider +label.resetVM=Reset VM +label.openDaylight=OpenDaylight +label.assign.instance.another=Assign Instance to Another Account +label.network.addVM=Add network to VM +label.set.default.NIC=Set default NIC +label.Xenserver.Tools.Version61plus=Original XS Version is 6.1\+ +label.supportsstrechedl2subnet=Supports Streched L2 Subnet +label.menu.vpc.offerings=VPC Offerings +label.vpc.offering=VPC Offering +label.regionlevelvpc=Region Level VPC +label.add.vpc.offering=Add VPC Offering +label.distributedrouter=Distributed Router +label.vpc.offering.details=VPC offering details +label.disable.vpc.offering=Disable VPC offering +label.enable.vpc.offering=Enable VPC offering +label.remove.vpc.offering=Remove VPC offering +label.vpc.distributedvpcrouter=Distributed VPC Router +label.vpc.supportsregionlevelvpc=Supports Region Level VPC +label.dynamically.scalable=Dynamically Scalable +label.instance.scaled.up=Instance Scaled Up +label.tag.key=Tag Key +label.tag.value=Tag Value +label.ipv6.address=IPv6 IP Address +label.ipv6.gateway=IPv6 Gateway +label.ipv6.CIDR=IPv6 CIDR +label.VPC.limits=VPC limits +label.edit.region=Edit Region +label.gslb.domain.name=GSLB Domain Name +label.add.gslb=Add GSLB +label.gslb.servicetype=Service Type +label.gslb.details=GSLB details +label.gslb.delete=Delete GSLB +label.opendaylight.controller=OpenDaylight Controller +label.opendaylight.controllers=OpenDaylight Controllers +label.portable.ip.ranges=Portable IP Ranges +label.add.portable.ip.range=Add Portable IP Range +label.delete.portable.ip.range=Delete Portable IP Range +label.opendaylight.controllerdetail=OpenDaylight Controller Details +label.portable.ip.range.details=Portable IP Range details +label.portable.ips=Portable IPs +label.gslb.assigned.lb=Assigned load balancing +label.gslb.assigned.lb.more=Assign more load balancing +label.gslb.lb.rule=Load balancing rule +label.gslb.lb.details=Load balancing details +label.gslb.lb.remove=Remove load balancing from this GSLB +label.enable.autoscale=Enable Autoscale +label.disable.autoscale=Disable Autoscale +label.min.instances=Min Instances +label.max.instances=Max Instances +label.add.OpenDaylight.device=Add OpenDaylight Controller +label.show.advanced.settings=Show advanced settings +label.delete.OpenDaylight.device=Delete OpenDaylight Controller +label.polling.interval.sec=Polling Interval (in sec) +label.quiet.time.sec=Quiet Time (in sec) +label.destroy.vm.graceperiod=Destroy VM Grace Period +label.SNMP.community=SNMP Community +label.SNMP.port=SNMP Port +label.add.ucs.manager=Add UCS Manager +label.ovm.traffic.label=OVM traffic label +label.lxc.traffic.label=LXC Traffic Label +label.hyperv.traffic.label=HyperV Traffic Label +label.resource.name=Resource Name +label.reource.id=Resource ID +label.vnmc.devices=VNMC Devices +label.add.vnmc.provider=Add VNMC provider +label.enable.vnmc.provider=Enable VNMC provider +label.add.vnmc.device=Add VNMC device +label.ciscovnmc.resource.details=CiscoVNMC resource details +label.delete.ciscovnmc.resource=Delete CiscoVNMC resource +label.enable.vnmc.device=Enable VNMC device +label.disbale.vnmc.device=Disable VNMC device +label.disable.vnmc.provider=Disable VNMC provider +label.services=Services +label.secondary.staging.store=Secondary Staging Store +label.release.account=Release from Account +label.release.account.lowercase=Release from account +label.vlan.vni.ranges=VLAN/VNI Range(s) +label.dedicated.vlan.vni.ranges=Dedicated VLAN/VNI Ranges +label.dedicate.vlan.vni.range=Dedicate VLAN/VNI Range +label.vlan.vni.range=VLAN/VNI Range +label.vlan.range.details=VLAN Range details +label.release.dedicated.vlan.range=Release dedicated VLAN range +label.broadcat.uri=Broadcast URI +label.ipv4.cidr=IPv4 CIDR +label.guest.network.details=Guest network details +label.ipv4.gateway=IPv4 Gateway +label.release.dedicated.vlan.range=Release dedicated VLAN range +label.vlan.ranges=VLAN Range(s) +label.virtual.appliance.details=Virtual applicance details +label.start.lb.vm=Start LB VM +label.stop.lb.vm=Stop LB VM +label.migrate.lb.vm=Migrate LB VM +label.vpc.virtual.router=VPC Virtual Router +label.ovs=OVS +label.gslb.service=GSLB service +label.gslb.service.public.ip=GSLB service Public IP +label.gslb.service.private.ip=GSLB service Private IP +label.baremetal.dhcp.provider=Baremetal DHCP Provider +label.add.baremetal.dhcp.device=Add Baremetal DHCP Device +label.baremetal.pxe.provider=Baremetal PXE Provider +label.baremetal.pxe.device=Add Baremetal PXE Device +label.tftp.root.directory=Tftp root directory +label.add.vmware.datacenter=Add VMware datacenter +label.remove.vmware.datacenter=Remove VMware datacenter +label.dc.name=DC Name +label.vcenter=vcenter +label.dedicate.zone=Dedicate Zone +label.zone.dedicated=Zone Dedicated +label.release.dedicated.zone=Release Dedicated Zone +label.ipv6.dns1=IPv6 DNS1 +label.ipv6.dns2=IPv6 DNS2 +label.vmware.datacenter.name=VMware datacenter Name +label.vmware.datacenter.vcenter=VMware datacenter vcenter +label.vmware.datacenter.id=VMware datacenter ID +label.system.vm.details=System VM details +label.system.vm.scaled.up=System VM Scaled Up +label.console.proxy.vm=Console Proxy VM +label.settings=Settings +label.requires.upgrade=Requires Upgrade +label.upgrade.router.newer.template=Upgrade Router to Use Newer Template +label.router.vm.scaled.up=Router VM Scaled Up +label.total.virtual.routers=Total of Virtual Routers +label.upgrade.required=Upgrade is required +label.virtual.routers.group.zone=Virtual Routers group by zone +label.total.virtual.routers.upgrade=Total of Virtual Routers that require upgrade +label.virtual.routers.group.pod=Virtual Routers group by pod +label.virtual.routers.group.cluster=Virtual Routers group by cluster +label.zone.lower=zone +label.virtual.routers.group.account=Virtual Routers group by account +label.netscaler.details=NetScaler details +label.baremetal.dhcp.devices=Baremetal DHCP Devices +label.baremetal.pxe.devices=Baremetal PXE Devices +label.addes.new.f5=Added new F5 +label.f5.details=F5 details +label.srx.details=SRX details +label.palo.alto.details=Palo Alto details +label.added.nicira.nvp.controller=Added new Nicira NVP Controller +label.nicira.nvp.details=Nicira NVP details +label.added.brocade.vcs.switch=Added new Brocade Vcs Switch +label.brocade.vcs.details=Brocade Vcs Switch details +label.added.new.bigswitch.vns.controller=Added new BigSwitch VNS Controller +label.bigswitch.vns.details=BigSwitch VNS details +label.dedicate=Dedicate +label.dedicate.pod=Dedicate Pod +label.pod.dedicated=Pod Dedicated +label.release.dedicated.pod=Release Dedicated Pod +label.override.public.traffic=Override Public-Traffic +label.public.traffic.vswitch.type=Public Traffic vSwitch Type +label.public.traffic.vswitch.name=Public Traffic vSwitch Name +label.override.guest.traffic=Override Guest-Traffic +label.guest.traffic.vswitch.type=Guest Traffic vSwitch Type +label.guest.traffic.vswitch.name=Guest Traffic vSwitch Name +label.cisco.nexus1000v.ip.address=Nexus 1000v IP Address +label.cisco.nexus1000v.username=Nexus 1000v Username +label.cisco.nexus1000v.password=Nexus 1000v Password +label.dedicate.cluster=Dedicate Cluster +label.release.dedicated.cluster=Release Dedicated Cluster +label.dedicate.host=Dedicate Host +label.release.dedicated.host=Release Dedicated Host +label.number.of.cpu.sockets=The Number of CPU Sockets +label.delete.ucs.manager=Delete UCS Manager +label.blades=Blades +label.chassis=Chassis +label.blade.id=Blade ID +label.associated.profile=Associated Profile +label.refresh.blades=Refresh Blades +label.instanciate.template.associate.profile.blade=Instanciate Template and Associate Profile to Blade +label.select.template=Select Template +label.profile=Profile +label.delete.profile=Delete Profile +label.disassociate.profile.blade=Disassociate Profile from Blade +label.secondary.storage.details=Secondary storage details +label.secondary.staging.store.details=Secondary Staging Store details +label.add.nfs.secondary.staging.store=Add NFS Secondary Staging Store +label.delete.secondary.staging.store=Delete Secondary Staging Store +label.ipv4.start.ip=IPv4 Start IP +label.ipv4.end.ip=IPv4 End IP +label.ipv6.start.ip=IPv6 Start IP +label.ipv6.end.ip=IPv6 End IP +label.vm.password=Password of the VM is +label.group.by.zone=Group by zone +label.group.by.pod=Group by pod +label.group.by.cluster=Group by cluster +label.group.by.account=Group by account +label.no.grouping=(no grouping) +label.create.nfs.secondary.staging.storage=Create NFS Secondary Staging Store +label.username.lower=username +label.password.lower=password +label.email.lower=email +label.firstname.lower=firstname +label.lastname.lower=lastname +label.domain.lower=domain +label.account.lower=account +label.type.lower=type +label.rule.number=Rule Number +label.action=Action +label.name.lower=name +label.ucs=UCS +label.change.affinity=Change Affinity +label.persistent=Persistent +label.broadcasturi=broadcasturi +label.network.cidr=Network CIDR +label.reserved.ip.range=Reserved IP Range +label.autoscale=AutoScale +label.autoscale.vm.group.name=AutoScale group name +label.health.check=Health Check +label.public.load.balancer.provider=Public Load Balancer Provider +label.add.isolated.network=Add Isolated Network +label.vlan.only=VLAN +label.secondary.isolated.vlan.id=Secondary Isolated VLAN ID +label.ipv4.netmask=IPv4 Netmask +label.custom=Custom +label.disable.network.offering=Disable network offering +label.enable.network.offering=Enable network offering +label.remove.network.offering=Remove network offering +label.system.offering.for.router=System Offering for Router +label.mode=Mode +label.associate.public.ip=Associate Public IP +label.acl=ACL +label.user.data=User Data +label.virtual.networking=Virtual Networking +label.allow=Allow +label.deny=Deny +label.default.egress.policy=Default egress policy +label.xenserver.tools.version.61.plus=Original XS Version is 6.1\+ +label.gpu=GPU +label.vgpu.type=vGPU type +label.vgpu.video.ram=Video RAM +label.vgpu.max.resolution=Max resolution +label.vgpu.max.vgpu.per.gpu=vGPUs per GPU +label.vgpu.remaining.capacity=Remaining capacity +label.routing.host=Routing Host +label.usage.server=Usage Server +label.user.vm=User VM +label.resource.limit.exceeded=Resource Limit Exceeded +label.direct.attached.public.ip=Direct Attached Public IP +label.usage.sanity.result=Usage Sanity Result +label.select.region=Select region +label.info.upper=INFO +label.warn.upper=WARN +label.error.upper=ERROR +label.event.deleted=Event Deleted +label.add.ciscoASA1000v=Add CiscoASA1000v Resource +label.delete.ciscoASA1000v=Delete CiscoASA1000v +label.inside.port.profile=Inside Port Profile +label.archive=Archive +label.event.archived=Event Archived +label.alert.details=Alert details +label.alert.deleted=Alert Deleted +label.alert.archived=Alert Archived +label.volume.details=Volume details +label.volume.migrated=Volume migrated +label.storage.pool=Storage Pool +label.enable.host=Enable Host +label.disable.host=Disable Host +label.copying.iso=Copying ISO +label.add.internal.lb=Add Internal LB +label.internal.lb.details=Internal LB details +label.delete.internal.lb=Delete Internal LB +label.remove.vm.load.balancer=Remove VM from load balancer +label.add.acl.list=Add ACL List +label.add.list.name=ACL List Name +label.add.network.acl.list=Add Network ACL List +label.delete.acl.list=Delete ACL List +label.acl.replaced=ACL replaced +label.ipv4.dns1=IPv4 DNS1 +label.ipv4.dns2=IPv4 DNS2 +label.protocol.number=Protocol Number +label.edit.acl.rule=Edit ACL rule +label.source.ip.address=Source IP Address +label.source.port=Source Port +label.instance.port=Instance Port +label.assigned.vms=Assigned VMs +label.replace.acl=Replace ACL +label.source.nat.supported=SourceNAT Supported +label.acl.name=ACL Name +label.acl.id=ACL ID +label.passive=Passive +label.replace.acl.list=Replace ACL List +label.vswitch.name=vSwitch Name +label.vSwitch.type=vSwitch Type +label.ping.path=Ping Path +label.response.timeout.in.sec=Response Timeout (in sec) +label.health.check.interval.in.sec=Health Check Interval (in sec) +label.healthy.threshold=Healthy Threshold +label.unhealthy.threshold=Unhealthy Threshold +label.other=Other +label.vm.id=VM ID +label.vnmc=VNMC +label.scale.up.policy=SCALE UP POLICY +label.counter=Counter +label.operator=Operator +label.threshold=Threshold +label.load.balancer.type=Load Balancer Type +label.vgpu=VGPU +label.sticky.name=Sticky Name +label.stickiness.method=Stickiness method +label.gslb=GSLB +label.portable.ip=Portable IP +label.internallbvm=InternalLbVm +label.agent.state=Agent State +label.duration.in.sec=Duration (in sec) +label.scaleup.step=Scale up step +label.scaledown.step=Scale down step +label.scale.logicaloperator=Logical Operator +managed.state=Managed State +message.acquire.new.ip.vpc=Please confirm that you would like to acquire a new IP for this VPC. +message.acquire.new.ip=Please confirm that you would like to acquire a new IP for this network. +message.acquire.public.ip=Please select a zone from which you want to acquire your new IP from. +message.action.cancel.maintenance.mode=Please confirm that you want to cancel this maintenance. +message.action.cancel.maintenance=Your host has been successfully canceled for maintenance. This process can take up to several minutes. +message.action.change.service.warning.for.instance=Your instance must be stopped before attempting to change its current service offering. +message.action.change.service.warning.for.router=Your router must be stopped before attempting to change its current service offering. +message.action.delete.cluster=Please confirm that you want to delete this cluster. +message.action.delete.disk.offering=Please confirm that you want to delete this disk offering. +message.action.delete.domain=Please confirm that you want to delete this domain. +message.action.delete.external.firewall=Please confirm that you would like to remove this external firewall. Warning\: If you are planning to add back the same external firewall, you must reset usage data on the device. +message.action.delete.external.load.balancer=Please confirm that you would like to remove this external load balancer. Warning\: If you are planning to add back the same external load balancer, you must reset usage data on the device. +message.action.delete.ingress.rule=Please confirm that you want to delete this ingress rule. +message.action.delete.ISO.for.all.zones=The ISO is used by all zones. Please confirm that you want to delete it from all zones. +message.action.delete.ISO=Please confirm that you want to delete this ISO. +message.action.delete.network=Please confirm that you want to delete this network. +message.action.delete.nexusVswitch=Please confirm that you want to delete this nexus 1000v +message.action.delete.physical.network=Please confirm that you want to delete this physical network +message.action.delete.pod=Please confirm that you want to delete this pod. +message.action.delete.primary.storage=Please confirm that you want to delete this primary storage. +message.action.delete.secondary.storage=Please confirm that you want to delete this secondary storage. +message.action.delete.security.group=Please confirm that you want to delete this security group. +message.action.delete.service.offering=Please confirm that you want to delete this service offering. +message.action.delete.snapshot=Please confirm that you want to delete this snapshot. +message.action.delete.system.service.offering=Please confirm that you want to delete this system service offering. +message.action.delete.template.for.all.zones=The template is used by all zones. Please confirm that you want to delete it from all zones. +message.action.delete.template=Please confirm that you want to delete this template. +message.action.delete.volume=Please confirm that you want to delete this volume. +message.action.delete.zone=Please confirm that you want to delete this zone. +message.action.destroy.instance=Please confirm that you want to destroy this instance. +message.action.destroy.systemvm=Please confirm that you want to destroy this System VM. +message.action.disable.cluster=Please confirm that you want to disable this cluster. +message.action.disable.nexusVswitch=Please confirm that you want to disable this nexus 1000v +message.action.disable.physical.network=Please confirm that you want to disable this physical network. +message.action.disable.pod=Please confirm that you want to disable this pod. +message.action.disable.static.NAT=Please confirm that you want to disable static NAT. +message.action.disable.zone=Please confirm that you want to disable this zone. +message.action.download.iso=Please confirm that you want to download this ISO. +message.action.download.template=Please confirm that you want to download this template. +message.action.enable.cluster=Please confirm that you want to enable this cluster. +message.action.enable.maintenance=Your host has been successfully prepared for maintenance. This process can take up to several minutes or longer depending on how many VMs are currently on this host. +message.action.enable.nexusVswitch=Please confirm that you want to enable this nexus 1000v +message.action.enable.physical.network=Please confirm that you want to enable this physical network. +message.action.enable.pod=Please confirm that you want to enable this pod. +message.action.enable.zone=Please confirm that you want to enable this zone. +message.action.expunge.instance=Please confirm that you want to expunge this instance. +message.action.force.reconnect=Your host has been successfully forced to reconnect. This process can take up to several minutes. +message.action.host.enable.maintenance.mode=Enabling maintenance mode will cause a live migration of all running instances on this host to any available host. +message.action.instance.reset.password=Please confirm that you want to change the ROOT password for this virtual machine. +message.action.manage.cluster=Please confirm that you want to manage the cluster. +message.action.primarystorage.enable.maintenance.mode=Warning\: placing the primary storage into maintenance mode will cause all VMs using volumes from it to be stopped. Do you want to continue? +message.action.reboot.instance=Please confirm that you want to reboot this instance. +message.action.reboot.router=All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router. +message.action.reboot.systemvm=Please confirm that you want to reboot this system VM. +message.action.release.ip=Please confirm that you want to release this IP. +message.action.remove.host=Please confirm that you want to remove this host. +message.action.reset.password.off=Your instance currently does not support this feature. +message.action.reset.password.warning=Your instance must be stopped before attempting to change its current password. +message.action.restore.instance=Please confirm that you want to restore this instance. +message.action.start.instance=Please confirm that you want to start this instance. +message.action.start.router=Please confirm that you want to start this router. +message.action.start.systemvm=Please confirm that you want to start this system VM. +message.action.stop.instance=Please confirm that you want to stop this instance. +message.action.stop.router=All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router. +message.action.stop.systemvm=Please confirm that you want to stop this system VM. +message.action.take.snapshot=Please confirm that you want to take a snapshot of this volume. +message.action.revert.snapshot=Please confirm that you want to revert the owning volume to this snapshot. +message.action.unmanage.cluster=Please confirm that you want to unmanage the cluster. +message.action.vmsnapshot.delete=Please confirm that you want to delete this VM snapshot. +message.action.vmsnapshot.revert=Revert VM snapshot +message.activate.project=Are you sure you want to activate this project? +message.add.cluster.zone=Add a hypervisor managed cluster for zone +message.add.cluster=Add a hypervisor managed cluster for zone , pod +message.add.disk.offering=Please specify the following parameters to add a new disk offering +message.add.domain=Please specify the subdomain you want to create under this domain +message.add.firewall=Add a firewall to zone +message.add.guest.network=Please confirm that you would like to add a guest network +message.add.host=Please specify the following parameters to add a new host +message.add.ip.range.direct.network=Add an IP range to direct network in zone +message.add.ip.range.to.pod=

Add an IP range to pod\:

+message.add.ip.range=Add an IP range to public network in zone +message.add.load.balancer.under.ip=The load balancer rule has been added under IP\: +message.add.load.balancer=Add a load balancer to zone +message.add.network=Add a new network for zone\: +message.add.new.gateway.to.vpc=Please specify the information to add a new gateway to this VPC. +message.add.pod.during.zone.creation=Each zone must contain in one or more pods, and we will add the first pod now. A pod contains hosts and primary storage servers, which you will add in a later step. First, configure a range of reserved IP addresses for CloudStack\\'s internal management traffic. The reserved IP range must be unique for each zone in the cloud. +message.add.pod=Add a new pod for zone +message.add.primary.storage=Add a new Primary Storage for zone , pod +message.add.primary=Please specify the following parameters to add a new primary storage +message.add.region=Please specify the required information to add a new region. +message.add.secondary.storage=Add a new storage for zone +message.add.service.offering=Please fill in the following data to add a new compute offering. +message.add.system.service.offering=Please fill in the following data to add a new system service offering. +message.add.template=Please enter the following data to create your new template +message.add.volume=Please fill in the following data to add a new volume. +message.add.VPN.gateway=Please confirm that you want to add a VPN Gateway +message.adding.host=Adding host +message.adding.Netscaler.device=Adding Netscaler device +message.adding.Netscaler.provider=Adding Netscaler provider +message.additional.networks.desc=Please select additional network(s) that your virtual instance will be connected to. +message.advanced.mode.desc=Choose this network model if you wish to enable VLAN support. This network model provides the most flexibility in allowing administrators to provide custom network offerings such as providing firewall, vpn, or load balancer support as well as enabling direct vs virtual networking. +message.advanced.security.group=Choose this if you wish to use security groups to provide guest VM isolation. +message.advanced.virtual=Choose this if you wish to use zone-wide VLANs to provide guest VM isolation. +message.after.enable.s3=S3-backed Secondary Storage configured. Note\: When you leave this page, you will not be able to re-configure S3 again. +message.after.enable.swift=Swift configured. Note\: When you leave this page, you will not be able to re-configure Swift again. +message.alert.state.detected=Alert state detected +message.allow.vpn.access=Please enter a username and password of the user that you want to allow VPN access. +message.apply.snapshot.policy=You have successfully updated your current snapshot policy. +message.attach.iso.confirm=Please confirm that you want to attach the ISO to this virtual instance. +message.attach.volume=Please fill in the following data to attach a new volume. If you are attaching a disk volume to a Windows based virtual machine, you will need to reboot the instance to see the attached disk. +message.basic.mode.desc=Choose this network model if you do *not* want to enable any VLAN support. All virtual instances created under this network model will be assigned an IP directly from the network and security groups are used to provide security and segregation. +message.change.offering.confirm=Please confirm that you wish to change the service offering of this virtual instance. +message.change.password=Please change your password. +message.configure.all.traffic.types=You have multiple physical networks; please configure labels for each traffic type by clicking on the Edit button. +message.configuring.guest.traffic=Configuring guest traffic +message.configuring.physical.networks=Configuring physical networks +message.configuring.public.traffic=Configuring public traffic +message.configuring.storage.traffic=Configuring storage traffic +message.confirm.action.force.reconnect=Please confirm that you want to force reconnect this host. +message.confirm.delete.F5=Please confirm that you would like to delete F5 +message.confirm.delete.BrocadeVcs=Please confirm that you would like to delete Brocade Vcs Switch +message.confirm.delete.NetScaler=Please confirm that you would like to delete NetScaler +message.confirm.delete.NuageVsp=Please confirm that you would like to delete Nuage Virtualized Services Directory +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 +message.confirm.join.project=Please confirm you wish to join this project. +message.confirm.remove.IP.range=Please confirm that you would like to remove this IP range. +message.confirm.shutdown.provider=Please confirm that you would like to shutdown this provider +message.confirm.current.guest.CIDR.unchanged=Do you want to keep the current guest network CIDR unchanged? +message.confirm.delete.ciscoASA1000v=Please confirm you want to delete CiscoASA1000v +message.confirm.remove.selected.events=Please confirm you would like to remove the selected events +message.confirm.archive.selected.events=Please confirm you would like to archive the selected events +message.confirm.remove.event=Are you sure you want to remove this event? +message.confirm.archive.event=Please confirm that you want to archive this event. +message.confirm.remove.selected.alerts=Please confirm you would like to remove the selected alerts +message.confirm.archive.selected.alerts=Please confirm you would like to archive the selected alerts +message.confirm.delete.alert=Are you sure you want to delete this alert ? +message.confirm.archive.alert=Please confirm that you want to archive this alert. +message.confirm.migrate.volume=Do you want to migrate this volume? +message.confirm.attach.disk=Are you sure you want to attach disk? +message.confirm.create.volume=Are you sure you want to create volume? +message.confirm.enable.host=Please confirm that you want to enable the host +message.confirm.disable.host=Please confirm that you want to disable the host +message.confirm.delete.internal.lb=Please confirm you want to delete Internal LB +message.confirm.remove.load.balancer=Please confirm you want to remove VM from load balancer +message.confirm.delete.acl.list=Are you sure you want to delete this ACL list? +message.confirm.replace.acl.new.one=Do you want to replace the ACL with a new one? +message.copy.iso.confirm=Please confirm that you wish to copy your ISO to +message.copy.template=Copy template XXX from zone to +message.create.template.vm=Create VM from template +message.create.template.volume=Please specify the following information before creating a template of your disk volume\: . Creation of the template can range from several minutes to longer depending on the size of the volume. +message.create.template=Are you sure you want to create template? +message.creating.cluster=Creating cluster +message.creating.guest.network=Creating guest network +message.creating.physical.networks=Creating physical networks +message.creating.pod=Creating pod +message.creating.primary.storage=Creating primary storage +message.creating.secondary.storage=Creating secondary storage +message.creating.zone=Creating zone +message.decline.invitation=Are you sure you want to decline this project invitation? +message.dedicate.zone=Dedicating zone +message.delete.account=Please confirm that you want to delete this account. +message.delete.affinity.group=Please confirm that you would like to remove this affinity group. +message.delete.gateway=Please confirm you want to delete the gateway +message.delete.project=Are you sure you want to delete this project? +message.delete.user=Please confirm that you would like to delete this user. +message.delete.VPN.connection=Please confirm that you want to delete VPN connection +message.delete.VPN.customer.gateway=Please confirm that you want to delete this VPN Customer Gateway +message.delete.VPN.gateway=Please confirm that you want to delete this VPN Gateway +message.desc.advanced.zone=For more sophisticated network topologies. This network model provides the most flexibility in defining guest networks and providing custom network offerings such as firewall, VPN, or load balancer support. +message.desc.basic.zone=Provide a single network where each VM instance is assigned an IP directly from the network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). +message.desc.cluster=Each pod must contain one or more clusters, and we will add the first cluster now. A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Each cluster consists of one or more hosts and one or more primary storage servers. +message.desc.host=Each cluster must contain at least one host (computer) for guest VMs to run on, and we will add the first host now. For a host to function in CloudStack, you must install hypervisor software on the host, assign an IP address to the host, and ensure the host is connected to the CloudStack management server.

Give the host\\'s DNS or IP address, the user name (usually root) and password, and any labels you use to categorize hosts. +message.desc.primary.storage=Each cluster must contain one or more primary storage servers, and we will add the first one now. Primary storage contains the disk volumes for all the VMs running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor. +message.desc.secondary.storage=Each zone must have at least one NFS or secondary storage server, and we will add the first one now. Secondary storage stores VM templates, ISO images, and VM disk volume snapshots. This server must be available to all hosts in the zone.

Provide the IP address and exported path. +message.desc.zone=A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone. +message.detach.disk=Are you sure you want to detach this disk? +message.detach.iso.confirm=Please confirm that you want to detach the ISO from this virtual instance. +message.disable.account=Please confirm that you want to disable this account. By disabling the account, all users for this account will no longer have access to their cloud resources. All running virtual machines will be immediately shut down. +message.disable.snapshot.policy=You have successfully disabled your current snapshot policy. +message.disable.user=Please confirm that you would like to disable this user. +message.disable.vpn.access=Please confirm that you want to disable Remote Access VPN. +message.disable.vpn=Are you sure you want to disable VPN? +message.download.ISO=Please click 00000 to download ISO +message.download.template=Please click 00000 to download template +message.download.volume.confirm=Please confirm that you want to download this volume. +message.download.volume=Please click 00000 to download volume +message.edit.account=Edit ("-1" indicates no limit to the amount of resources create) +message.edit.confirm=Please confirm that your changes before clicking "Save". +message.edit.limits=Please specify limits to the following resources. A "-1" indicates no limit to the amount of resources create. +message.edit.traffic.type=Please specify the traffic label you want associated with this traffic type. +message.enable.account=Please confirm that you want to enable this account. +message.enable.user=Please confirm that you would like to enable this user. +message.enable.vpn.access=VPN is currently disabled for this IP Address. Would you like to enable VPN access? +message.enable.vpn=Please confirm that you want Remote Access VPN enabled for this IP address. +message.enabled.vpn.ip.sec=Your IPSec pre-shared key is +message.enabled.vpn=Your Remote Access VPN is currently enabled and can be accessed via the IP +message.enabling.security.group.provider=Enabling Security Group provider +message.enabling.zone=Enabling zone +message.enter.token=Please enter the token that you were given in your invite e-mail. +message.generate.keys=Please confirm that you would like to generate new keys for this user. +message.guest.traffic.in.advanced.zone=Guest network traffic is communication between end-user virtual machines. Specify a range of VLAN IDs to carry guest traffic for each physical network. +message.guest.traffic.in.basic.zone=Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range. +message.installWizard.click.retry=Click the button to retry launch. +message.installWizard.copy.whatIsACluster=A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Virtual machine instances (VMs) can be live-migrated from one host to another within the same cluster, without interrupting service to the user. A cluster is the third-largest organizational unit within a CloudStack&\#8482; deployment. Clusters are contained within pods, and pods are contained within zones.

CloudStack&\#8482; allows multiple clusters in a cloud deployment, but for a Basic Installation, we only need one cluster. +message.installWizard.copy.whatIsAHost=A host is a single computer. Hosts provide the computing resources that run the guest virtual machines. Each host has hypervisor software installed on it to manage the guest VMs (except for bare metal hosts, which are a special case discussed in the Advanced Installation Guide). For example, a Linux KVM-enabled server, a Citrix XenServer server, and an ESXi server are hosts. In a Basic Installation, we use a single host running XenServer or KVM.

The host is the smallest organizational unit within a CloudStack&\#8482; deployment. Hosts are contained within clusters, clusters are contained within pods, and pods are contained within zones. +message.installWizard.copy.whatIsAPod=A pod often represents a single rack. Hosts in the same pod are in the same subnet.

A pod is the second-largest organizational unit within a CloudStack&\#8482; deployment. Pods are contained within zones. Each zone can contain one or more pods; in the Basic Installation, you will have just one pod in your zone. +message.installWizard.copy.whatIsAZone=A zone is the largest organizational unit within a CloudStack&\#8482; deployment. A zone typically corresponds to a single datacenter, although it is permissible to have multiple zones in a datacenter. The benefit of organizing infrastructure into zones is to provide physical isolation and redundancy. For example, each zone can have its own power supply and network uplink, and the zones can be widely separated geographically (though this is not required). +message.installWizard.copy.whatIsCloudStack=CloudStack&\#8482 is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack&\#8482 manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack&\#8482 to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack&\#8482 provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features. +message.installWizard.copy.whatIsPrimaryStorage=A CloudStack&\#8482; cloud infrastructure makes use of two types of storage\: primary storage and secondary storage. Both of these can be iSCSI or NFS servers, or localdisk.

Primary storage is associated with a cluster, and it stores the disk volumes of each guest VM for all the VMs running on hosts in that cluster. The primary storage server is typically located close to the hosts. +message.installWizard.copy.whatIsSecondaryStorage=Secondary storage is associated with a zone, and it stores the following\:
  • Templates - OS images that can be used to boot VMs and can include additional configuration information, such as installed applications
  • ISO images - OS images that can be bootable or non-bootable
  • Disk volume snapshots - saved copies of VM data which can be used for data recovery or to create new templates
+message.installWizard.now.building=Now building your cloud... +message.installWizard.tooltip.addCluster.name=A name for the cluster. This can be text of your choosing and is not used by CloudStack. +message.installWizard.tooltip.addHost.hostname=The DNS name or IP address of the host. +message.installWizard.tooltip.addHost.password=This is the password for the user named above (from your XenServer install). +message.installWizard.tooltip.addHost.username=Usually root. +message.installWizard.tooltip.addPod.name=A name for the pod +message.installWizard.tooltip.addPod.reservedSystemEndIp=This is the IP range in the private network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers. +message.installWizard.tooltip.addPod.reservedSystemGateway=The gateway for the hosts in that pod. +message.installWizard.tooltip.addPod.reservedSystemNetmask=The netmask in use on the subnet the guests will use. +message.installWizard.tooltip.addPod.reservedSystemStartIp=This is the IP range in the private network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers. +message.installWizard.tooltip.addPrimaryStorage.name=The name for the storage device. +message.installWizard.tooltip.addPrimaryStorage.path=(for NFS) In NFS this is the exported path from the server. Path (for SharedMountPoint). With KVM this is the path on each host that is where this primary storage is mounted. For example, "/mnt/primary". +message.installWizard.tooltip.addPrimaryStorage.server=(for NFS, iSCSI, or PreSetup) The IP address or DNS name of the storage device. +message.installWizard.tooltip.addSecondaryStorage.nfsServer=The IP address of the NFS server hosting the secondary storage +message.installWizard.tooltip.addSecondaryStorage.path=The exported path, located on the server you specified above +message.installWizard.tooltip.addZone.dns1=These are DNS servers for use by guest VMs in the zone. These DNS servers will be accessed via the public network you will add later. The public IP addresses for the zone must have a route to the DNS server named here. +message.installWizard.tooltip.addZone.dns2=These are DNS servers for use by guest VMs in the zone. These DNS servers will be accessed via the public network you will add later. The public IP addresses for the zone must have a route to the DNS server named here. +message.installWizard.tooltip.addZone.internaldns1=These are DNS servers for use by system VMs in the zone. These DNS servers will be accessed via the private network interface of the System VMs. The private IP address you provide for the pods must have a route to the DNS server named here. +message.installWizard.tooltip.addZone.internaldns2=These are DNS servers for use by system VMs in the zone. These DNS servers will be accessed via the private network interface of the System VMs. The private IP address you provide for the pods must have a route to the DNS server named here. +message.installWizard.tooltip.addZone.name=A name for the zone +message.installWizard.tooltip.configureGuestTraffic.description=A description for your network +message.installWizard.tooltip.configureGuestTraffic.guestEndIp=The range of IP addresses that will be available for allocation to guests in this zone. If one NIC is used, these IPs should be in the same CIDR as the pod CIDR. +message.installWizard.tooltip.configureGuestTraffic.guestGateway=The gateway that the guests should use +message.installWizard.tooltip.configureGuestTraffic.guestNetmask=The netmask in use on the subnet that the guests should use +message.installWizard.tooltip.configureGuestTraffic.guestStartIp=The range of IP addresses that will be available for allocation to guests in this zone. If one NIC is used, these IPs should be in the same CIDR as the pod CIDR. +message.installWizard.tooltip.configureGuestTraffic.name=A name for your network +message.instanceWizard.noTemplates=You do not have any templates available; please add a compatible template, and re-launch the instance wizard. +message.ip.address.changed=Your IP addresses may have changed; would you like to refresh the listing? Note that in this case the details pane will close. +message.iso.desc=Disc image containing data or bootable media for OS +message.join.project=You have now joined a project. Please switch to Project view to see the project. +message.launch.vm.on.private.network=Do you wish to launch your instance on your own private dedicated network? +message.launch.zone=Zone is ready to launch; please proceed to the next step. +message.lock.account=Please confirm that you want to lock this account. By locking the account, all users for this account will no longer be able to manage their cloud resources. Existing resources can still be accessed. +message.migrate.instance.confirm=Please confirm the host you wish to migrate the virtual instance to. +message.migrate.instance.to.host=Please confirm that you want to migrate instance to another host. +message.migrate.instance.to.ps=Please confirm that you want to migrate instance to another primary storage. +message.migrate.router.confirm=Please confirm the host you wish to migrate the router to\: +message.migrate.systemvm.confirm=Please confirm the host you wish to migrate the system VM to\: +message.migrate.volume=Please confirm that you want to migrate volume to another primary storage. +message.new.user=Specify the following to add a new user to the account +message.no.network.support.configuration.not.true=You do not have any zone that has security group enabled. Thus, no additional network features. Please continue to step 5. +message.no.network.support=Your selected hypervisor, vSphere, does not have any additional network features. Please continue to step 5. +message.no.projects.adminOnly=You do not have any projects.
Please ask your administrator to create a new project. +message.no.projects=You do not have any projects.
Please create a new one from the projects section. +message.number.clusters=

\# of Clusters

+message.number.hosts=

\# of Hosts

+message.number.pods=

\# of Pods

+message.number.storage=

\# of Primary Storage Volumes

+message.number.zones=

\# of Zones

+message.pending.projects.1=You have pending project invitations\: +message.pending.projects.2=To view, please go to the projects section, then select invitations from the drop-down. +message.please.add.at.lease.one.traffic.range=Please add at least one traffic range. +message.please.proceed=Please proceed to the next step. +message.please.select.a.configuration.for.your.zone=Please select a configuration for your zone. +message.please.select.a.different.public.and.management.network.before.removing=Please select a different public and management network before removing +message.please.select.networks=Please select networks for your virtual machine. +message.please.wait.while.zone.is.being.created=Please wait while your zone is being created; this may take a while... +message.project.invite.sent=Invite sent to user; they will be added to the project once they accept the invitation +message.public.traffic.in.advanced.zone=Public traffic is generated when VMs in the cloud access the internet. Publicly-accessible IPs must be allocated for this purpose. End users can use the CloudStack UI to acquire these IPs to implement NAT between their guest network and their public network.

Provide at least one range of IP addresses for internet traffic. +message.public.traffic.in.basic.zone=Public traffic is generated when VMs in the cloud access the Internet or provide services to clients over the Internet. Publicly accessible IPs must be allocated for this purpose. When a instance is created, an IP from this set of Public IPs will be allocated to the instance in addition to the guest IP address. Static 1-1 NAT will be set up automatically between the public IP and the guest IP. End users can also use the CloudStack UI to acquire additional IPs to implement static NAT between their instances and the public IP. +message.redirecting.region=Redirecting to region... +message.remove.region=Are you sure you want to remove this region from this management server? +message.remove.vpc=Please confirm that you want to remove the VPC +message.remove.vpn.access=Please confirm that you want to remove VPN access from the following user. +message.reset.password.warning.notPasswordEnabled=The template of this instance was created without password enabled +message.reset.password.warning.notStopped=Your instance must be stopped before attempting to change its current password +message.reset.VPN.connection=Please confirm that you want to reset VPN connection +message.restart.mgmt.server=Please restart your management server(s) for your new settings to take effect. +message.restart.mgmt.usage.server=Please restart your management server(s) and usage server(s) for your new settings to take effect. +message.restart.network=All services provided by this network will be interrupted. Please confirm that you want to restart this network. +message.restart.vpc=Please confirm that you want to restart the VPC +message.security.group.usage=(Use Ctrl-click to select all applicable security groups) +message.select.a.zone=A zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy. +message.select.instance=Please select an instance. +message.select.iso=Please select an ISO for your new virtual instance. +message.select.item=Please select an item. +message.select.security.groups=Please select security group(s) for your new VM +message.select.template=Please select a template for your new virtual instance. +message.setup.physical.network.during.zone.creation.basic=When adding a basic zone, you can set up one physical network, which corresponds to a NIC on the hypervisor. The network carries several types of traffic.

You may also drag and drop other traffic types onto the physical network. +message.setup.physical.network.during.zone.creation=When adding an advanced zone, you need to set up one or more physical networks. Each network corresponds to a NIC on the hypervisor. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined.

Drag and drop one or more traffic types onto each physical network. +message.setup.successful=Cloud setup successful\! +message.snapshot.schedule=You can setup recurring snapshot schedules by selecting from the available options below and applying your policy preference +message.specify.url=Please specify URL +message.step.1.continue=Please select a template or ISO to continue +message.step.1.desc=Please select a template for your new virtual instance. You can also choose to select a blank template from which an ISO image can be installed onto. +message.step.2.continue=Please select a service offering to continue +message.step.2.desc= +message.step.3.continue=Please select a disk offering to continue +message.step.3.desc= +message.step.4.continue=Please select at least one network to continue +message.step.4.desc=Please select the primary network that your virtual instance will be connected to. +message.storage.traffic=Traffic between CloudStack\\'s internal resources, including any components that communicate with the Management Server, such as hosts and CloudStack system VMs. Please configure storage traffic here. +message.suspend.project=Are you sure you want to suspend this project? +message.template.desc=OS image that can be used to boot VMs +message.tooltip.dns.1=Name of a DNS server for use by VMs in the zone. The public IP addresses for the zone must have a route to this server. +message.tooltip.dns.2=A second DNS server name for use by VMs in the zone. The public IP addresses for the zone must have a route to this server. +message.tooltip.internal.dns.1=Name of a DNS server for use by CloudStack internal system VMs in the zone. The private IP address for the pods must have a route to this server. +message.tooltip.internal.dns.2=Name of a DNS server for use by CloudStack internal system VMs in the zone. The private IP address for the pods must have a route to this server. +message.tooltip.network.domain=A DNS suffix that will create a custom domain name for the network that is accessed by guest VMs. +message.tooltip.pod.name=A name for this pod. +message.tooltip.reserved.system.gateway=The gateway for the hosts in the pod. +message.tooltip.reserved.system.netmask=The network prefix that defines the pod subnet. Uses CIDR notation. +message.tooltip.zone.name=A name for the zone. +message.update.os.preference=Please choose a OS preference for this host. All virtual instances with similar preferences will be first allocated to this host before choosing another. +message.update.resource.count=Please confirm that you want to update resource counts for this account. +message.update.ssl=Please submit a new X.509 compliant SSL certificate chain to be updated to each console proxy and secondary storage virtual instance\: +message.update.ssl.succeeded=Update SSL Certificates succeeded +message.update.ssl.failed=Failed to update SSL Certificate. +message.validate.instance.name=Instance name can not be longer than 63 characters. Only ASCII letters a~z, A~Z, digits 0~9, hyphen are allowed. Must start with a letter and end with a letter or a digit. +message.virtual.network.desc=A dedicated virtualized network for your account. The broadcast domain is contained within a VLAN and all public network access is routed out by a virtual router. +message.vm.create.template.confirm=Create Template will reboot the VM automatically. +message.vm.review.launch=Please review the following information and confirm that your virtual instance is correct before launch. +message.volume.create.template.confirm=Please confirm that you wish to create a template for this disk volume. Creation of the template can range from several minutes to longer depending on the size of the volume. +message.you.must.have.at.least.one.physical.network=You must have at least one physical network +message.zone.creation.complete.would.you.like.to.enable.this.zone=Zone creation complete. Would you like to enable this zone? +message.Zone.creation.complete=Zone creation complete +message.zone.no.network.selection=The zone you selected does not have any choices for network selection. +message.zone.step.1.desc=Please select a network model for your zone. +message.zone.step.2.desc=Please enter the following info to add a new zone +message.zone.step.3.desc=Please enter the following info to add a new pod +message.zoneWizard.enable.local.storage=WARNING\: If you enable local storage for this zone, you must do the following, depending on where you would like your system VMs to launch\:

1. If system VMs need to be launched in shared primary storage, shared primary storage needs to be added to the zone after creation. You must also start the zone in a disabled state.

2. If system VMs need to be launched in local primary storage, system.vm.use.local.storage needs to be set to true before you enable the zone.


Would you like to continue? +message.validate.fieldrequired=This field is required. +message.validate.fixfield=Please fix this field. +message.validate.email.address=Please enter a valid email address. +message.validate.URL=Please enter a valid URL. +message.validate.date=Please enter a valid date. +message.validate.date.ISO=Please enter a valid date (ISO). +message.validate.number=Please enter a valid number. +message.validate.digits=Please enter only digits. +message.validate.creditcard=Please enter a valid credit card number. +message.validate.equalto=Please enter the same value again. +message.validate.accept=Please enter a value with a valid extension. +message.validate.maxlength=Please enter no more than {0} characters. +message.validate.minlength=Please enter at least {0} characters. +message.validate.range.length=Please enter a value between {0} and {1} characters long. +message.validate.range=Please enter a value between {0} and {1}. +message.validate.max=Please enter a value less than or equal to {0}. +messgae.validate.min=Please enter a value greater than or equal to {0}. +message.creating.systemVM=Creating system VMs (this may take a while) +message.enabling.zone.dots=Enabling zone... +message.restoreVM=Do you want to restore the VM ? +message.no.host.available=No Hosts are available for Migration +message.network.addVM.desc=Please specify the network that you would like to add this VM to. A new NIC will be added for this network. +message.network.addVMNIC=Please confirm that you would like to add a new VM NIC for this network. +message.set.default.NIC=Please confirm that you would like to make this NIC the default for this VM. +message.set.default.NIC.manual=Please manually update the default NIC on the VM now. +message.instance.scaled.up.confirm=Do you really want to scale Up your instance ? +message.copy.template.confirm=Are you sure you want to copy template? +message.template.copying=Template is being copied. +message.XSTools61plus.update.failed=Failed to update Original XS Version is 6.1\+ field. Error\: +message.gslb.delete.confirm=Please confirm you want to delete this GSLB +message.portable.ip.delete.confirm=Please confirm you want to delete Portable IP Range +message.gslb.lb.remove.confirm=Please confirm you want to remove load balancing from GSLB +message.admin.guide.read=For VMware-based VMs, please read the dynamic scaling section in the admin guide before scaling. Would you like to continue?\, +message.tier.required=Tier is required +message.remove.ldap=Are you sure you want to delete the LDAP configuration? +message.action.downloading.template=Downloading template. +message.configure.ldap=Please confirm you would like to configure LDAP. +message.confirm.delete.ciscovnmc.resource=Please confirm you want to delete CiscoVNMC resource +message.confirm.add.vnmc.provider=Please confirm you would like to add the VNMC provider. +message.confirm.enable.vnmc.provider=Please confirm you would like to enable the VNMC provider. +message.confirm.disable.vnmc.provider=Please confirm you would like to disable the VNMC provider. +message.vnmc.available.list=VNMC is not available from provider list. +message.vnmc.not.available.list=VNMC is not available from provider list. +message.confirm.release.dedicate.vlan.range=Please confirm you want to release dedicated VLAN range +message.confirm.start.lb.vm=Please confirm you want to start LB VM +message.confirm.stop.lb.vm=Please confirm you want to stop LB VM +message.confirm.remove.vmware.datacenter=Please confirm you want to remove VMware datacenter +message.confirm.dedicate.zone=Do you really want to dedicate this zone to a domain/account? +message.confirm.release.dedicated.zone=Do you want to release this dedicated zone ? +message.dedicated.zone.released=Zone dedication released +message.read.admin.guide.scaling.up=Please read the dynamic scaling section in the admin guide before scaling up. +message.confirm.scale.up.system.vm=Do you really want to scale up the system VM ? +message.confirm.upgrade.router.newer.template=Please confirm that you want to upgrade router to use newer template +message.confirm.scale.up.router.vm=Do you really want to scale up the Router VM ? +message.confirm.upgrade.routers.newtemplate=Please confirm that you want to upgrade all routers in this zone to use newer template +message.confirm.upgrade.routers.pod.newtemplate=Please confirm that you want to upgrade all routers in this pod to use newer template +message.confirm.upgrade.routers.cluster.newtemplate=Please confirm that you want to upgrade all routers in this cluster to use newer template +message.confirm.upgrade.routers.account.newtemplate=Please confirm that you want to upgrade all routers in this account to use newer template +message.confirm.dedicate.pod.domain.account=Do you really want to dedicate this pod to a domain/account? +message.confirm.release.dedicated.pod=Do you want to release this dedicated pod ? +message.pod.dedication.released=Pod dedication released +message.confirm.dedicate.cluster.domain.account=Do you really want to dedicate this cluster to a domain/account? +message.cluster.dedicated=Cluster Dedicated +message.confirm.release.dedicated.cluster=Do you want to release this dedicated cluster ? +message.cluster.dedication.released=Cluster dedication released +message.confirm.dedicate.host.domain.account=Do you really want to dedicate this host to a domain/account? +message.host.dedicated=Host Dedicated +message.confirm.release.dedicated.host=Do you want to release this dedicated host ? +message.host.dedication.released=Host dedication released +message.confirm.delete.ucs.manager=Please confirm that you want to delete UCS Manager +message.confirm.refresh.blades=Please confirm that you want to refresh blades. +message.confirm.delete.secondary.staging.store=Please confirm you want to delete Secondary Staging Store. +message.select.tier=Please select a tier +message.disallowed.characters=Disallowed characters: \<\,\> +message.waiting.for.builtin.templates.to.load=Waiting for builtin templates to load... +message.systems.vms.ready=System VMs ready. +message.your.cloudstack.is.ready=Your CloudStack is ready\! +message.specifiy.tag.key.value=Please specify a tag key and value +message.enter.seperated.list.multiple.cidrs=Please enter a comma separated list of CIDRs if more than one +message.disabling.network.offering=Disabling network offering +message.confirm.enable.network.offering=Are you sure you want to enable this network offering? +message.enabling.network.offering=Enabling network offering +message.confirm.remove.network.offering=Are you sure you want to remove this network offering? +message.confirm.disable.network.offering=Are you sure you want to disable this network offering? +message.disabling.vpc.offering=Disabling VPC offering +message.confirm.enable.vpc.offering=Are you sure you want to enable this VPC offering? +message.enabling.vpc.offering=Enabling VPC offering +message.confirm.remove.vpc.offering=Are you sure you want to remove this VPC offering? +message.confirm.disable.vpc.offering=Are you sure you want to disable this VPC offering? +mode=Mode +network.rate=Network Rate +notification.reboot.instance=Reboot instance +notification.start.instance=Start instance +notification.stop.instance=Stop instance +side.by.side=Side by Side +state.Accepted=Accepted +state.Active=Active +state.Allocated=Allocated +state.Allocating=Allocating +state.BackedUp=Backed Up +state.BackingUp=Backing Up +state.Completed=Completed +state.Creating=Creating +state.Declined=Declined +state.Destroyed=Destroyed +state.detached=Detached +state.Disabled=Disabled +state.Enabled=Enabled +state.enabled=Enabled +state.Error=Error +state.Expunging=Expunging +state.Migrating=Migrating +state.Pending=Pending +state.Ready=Ready +state.ready=Ready +state.Running=Running +state.Starting=Starting +state.Stopped=Stopped +state.Stopping=Stopping +state.Suspended=Suspended +ui.listView.filters.all=All +ui.listView.filters.mine=Mine +label.na=NA +label.globo.dns=GloboDNS +label.add.globo.dns=Add GloboDNS +label.globo.dns.configuration=GloboDNS Configuration +label.globo.network.lbmethod=Load Balancer Method +label.action.registry.dns.for.load.balancer=Please confirm that you want to register DNS for this Load Balancer. +label.action.registry.dns.for.vm.nic=Please confirm that you want to register DNS for this NIC. +message.registry.dns.for.load.balancer.successful=DNS succesfully registered for Load Balancer. +message.registry.dns.for.vm.nic.successful=DNS succesfully registered for NIC. +message.oauth2.usernotfound=Your user doesn\\'t exist. Contact the administrator to create it. +message.oauth2.error=Error authenticate your account using OAuth2. +label.globonetwork.add.device=Add GloboNetwork Controller +label.globonetwork.add.network=Add GloboNetwork Network +label.globonetwork.environment=environment +label.globo.aclapi=Globo ACL API +label.globo.aclapi.configuration=Globo ACL API Configuration +label.add.globo.aclapi=Add Globo ACL API +label.max.hosts.supported=Max hosts supported + + +message.action.unlink.loadbalancer=Unlink Load balancer from another will remove pools and create new ones. This operation needs to redeploy VIP. Are you sure want to unlink load balancer? +label.action.unlink.loadbalancer=Unlink load balancer +label.action.unlink.loadbalancer.processing=Unlinking load balancer... +message.action.link.loadbalancer=Link Load balancer with another lb will destroy pools and associate lb with second lb\\'s pools. This operation needs to redeploy VIP. Are you sure want to link load balancer? +label.action.link.loadbalancer=Link Load Balancer +label.action.link.loadbalancer.processing=Linking Load Balancer diff --git a/client/WEB-INF/classes/resources/messages_pt_BR.properties b/client/WEB-INF/classes/resources/messages_pt_BR.properties new file mode 100644 index 000000000000..5a5ab489ffa6 --- /dev/null +++ b/client/WEB-INF/classes/resources/messages_pt_BR.properties @@ -0,0 +1,2073 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +changed.item.properties=Propriedades do item alteradas +confirm.enable.s3=Por favor, preencha as informa\u00e7\u00f5es abaixo para habilitar suporte o Storage Secund\u00e1rio fornecido por S3 +confirm.enable.swift=Por favor, preencha as informa\u00e7\u00f5es abaixo para habilitar suporte ao Swift +error.could.not.change.your.password.because.ldap.is.enabled=Erro\: a nuvem n\u00e3o alterou sua senha porque o LDAP est\u00e1 ativo. +error.could.not.enable.zone=N\u00e3o foi poss\u00edvel habilitar a zona +error.installWizard.message=Alguma coisa est\u00e1 errada; voc\u00ea pode voltar e corrigir quaisquer erros +error.invalid.username.password=Usu\u00e1rio ou senha inv\u00e1lidos +error.login=O seu usu\u00e1rio/senha n\u00e3o coincidem com nossos registros. +error.menu.select=N\u00e3o foi poss\u00edvel realizar a a\u00e7\u00e3o pois nenhum item foi selecionado. +error.mgmt.server.inaccessible=O Servidor de Gerenciamento est\u00e1 inacess\u00edvel. Tente novamente mais tarde. +error.password.not.match=Os campos de senha n\u00e3o combinam +error.please.specify.physical.network.tags=As Ofertas de Rede n\u00e3o estar\u00e3o dispon\u00edveis enquanto voc\u00ea n\u00e3o especificar tags para esta interface f\u00edsica. +error.session.expired=Sua sess\u00e3o expirou. +error.something.went.wrong.please.correct.the.following=Alguma coisa est\u00e1 errada; por favor corrija abaixo +error.unable.to.reach.management.server=N\u00e3o foi poss\u00edvel acessar o Servidor de Gerenciamento +error.unresolved.internet.name=Imposs\u00edvel resolver DNS +force.delete.domain.warning=Aten\u00e7\u00e3o\: Esta op\u00e7\u00e3o remover\u00e1 todos os dom\u00ednios, contas e recursos associados. +force.delete=For\u00e7ar Exclus\u00e3o +force.remove=For\u00e7ar Remo\u00e7\u00e3o +force.remove.host.warning=Aten\u00e7\u00e3o\: O CloudStack desligar\u00e1 de maneira for\u00e7ada todas as VMs antes de remover o host do cluster. +force.stop=For\u00e7ar Parada +force.stop.instance.warning=Aviso\: For\u00e7ar o desligamento desta inst\u00e2ncia deve ser sua \u00faltima op\u00e7\u00e3o. Isto pode levar a perda de dados, bem como comportamento inconsist\u00eante do estado da m\u00e1quina virtual. +ICMP.code=C\u00f3digo ICMP +ICMP.type=Tipo ICMP +image.directory=Diret\u00f3rio da Imagem +inline=Inline +instances.actions.reboot.label=Reiniciar inst\u00e2ncia +label.about.app=Sobre o CloudStack +label.about=Sobre +label.accept.project.invitation=Aceitar convite de projeto. +label.account.and.security.group=Contas, grupos de Seguran\u00e7a +label.account=Conta +label.account.id=ID da Conta +label.account.lower=conta +label.account.name=Nome da Conta +label.accounts=Contas +label.account.specific=Conta-Specific +label.acl=ACL +label.acl.id=ACL ID +label.acl.name=Nome da ACL +label.acl.replaced=ACL trocado +label.acquire.new.ip=Adquirir novo IP +label.acquire.new.secondary.ip=Adquira um novo IP secund\u00e1rio +label.action.attach.disk=Anexar Disco +label.action.attach.disk.processing=Anexando Disco.... +label.action.attach.iso=Anexar ISO +label.action.attach.iso.processing=Anexando ISO.... +label.action=A\u00e7\u00e3o +label.action.cancel.maintenance.mode=Cancelar Modo de Manuten\u00e7\u00e3o +label.action.cancel.maintenance.mode.processing=Cancelando Modo de Manuten\u00e7\u00e3o.... +label.action.change.password=Troca de Senha +label.action.change.service.processing=Trocando de Plano.... +label.action.change.service=Trocar Plano +label.action.configure.samlauthorization=Configurar Autoriza\u00e7\u00e3o SAML SSO +label.action.copy.ISO=Copiar ISO +label.action.copy.ISO.processing=Copiando ISO... +label.action.copy.template=Copiar Template +label.action.copy.template.processing=Copiando Template... +label.action.create.template=Criar Template +label.action.create.template.from.vm=Criar Template a partir da VM +label.action.create.template.from.volume=Criar Template a partir do Disco +label.action.create.template.processing=Criando Template.... +label.action.create.vm=Criar VM +label.action.create.vm.processing=Criando VM.... +label.action.create.volume=Criar Disco +label.action.create.volume.processing=Criando Disco.... +label.action.delete.account.processing=Removendo conta.... +label.action.delete.account=Remover conta +label.action.delete.cluster.processing=Removendo Cluster.... +label.action.delete.cluster=Remover Cluster +label.action.delete.disk.offering.processing=Removendo Oferta de Disco.... +label.action.delete.disk.offering=Remover Oferta de Disco +label.action.delete.domain.processing=Removendo Dom\u00ednio.... +label.action.delete.domain=Remover Dom\u00ednio +label.action.delete.firewall.processing=Removendo Firewall.... +label.action.delete.firewall=Remover regra de firewall +label.action.delete.ingress.rule.processing=Removendo Regra de Entrada.... +label.action.delete.ingress.rule=Remover Regra de Entrada +label.action.delete.IP.range.processing=Removendo Range de IP.... +label.action.delete.IP.range=Remover Range IP +label.action.delete.ISO.processing=Removendo ISO.... +label.action.delete.ISO=Removendo ISO +label.action.delete.load.balancer.processing=Removendo Balanceador de Carga... +label.action.delete.load.balancer=Remover regra de balanceador de carga +label.action.delete.network.processing=Removendo Rede.... +label.action.delete.network=Remover Rede +label.action.delete.nexusVswitch=Remover NexusVswitch +label.action.delete.nic=Remover Interface de Rede +label.action.delete.physical.network=Deletar rede f\u00edsica +label.action.delete.pod.processing=Removendo POD.... +label.action.delete.pod=Remover POD +label.action.delete.primary.storage.processing=Removendo Storage Prim\u00e1rio.... +label.action.delete.primary.storage=Remover Storage Prim\u00e1rio +label.action.delete.secondary.storage.processing=Removendo Storage Secund\u00e1rio.... +label.action.delete.secondary.storage=Remover Storage Secund\u00e1rio +label.action.delete.security.group.processing=Removendo Security Group.... +label.action.delete.security.group=Remover Security Group +label.action.delete.service.offering.processing=Removendo Plano.... +label.action.delete.service.offering=Remover Plano +label.action.delete.snapshot.processing=Removendo Snapshot.... +label.action.delete.snapshot=Remover Snapshot +label.action.delete.system.service.offering=Deletar Oferta de Servi\u00e7o de Sistema +label.action.delete.template.processing=Removendo Template.... +label.action.delete.template=Remover Template +label.action.delete.user.processing=Removendo Usu\u00e1rio.... +label.action.delete.user=Remover Usu\u00e1rio +label.action.delete.volume.processing=Removendo Disco.... +label.action.delete.volume=Remover Disco +label.action.delete.zone.processing=Removendo Zona.... +label.action.delete.zone=Remover Zona +label.action.destroy.instance=Apagar Inst\u00e2ncia +label.action.destroy.instance.processing=Apagando Inst\u00e2ncia.... +label.action.destroy.systemvm=Apagar VM de Sistema +label.action.destroy.systemvm.processing=Apagando VM de Sistema.... +label.action.detach.disk=Desplugar Disco +label.action.detach.disk.processing=Desplugando Disco.... +label.action.detach.iso=Desplugar ISO +label.action.detach.iso.processing=Desplugando ISO.... +label.action.disable.account=Desativar conta +label.action.disable.account.processing=Desativando conta.... +label.action.disable.cluster=Desativar Cluster +label.action.disable.cluster.processing=Desativando Cluster.... +label.action.disable.nexusVswitch=Desabilitar NexusVswitch +label.action.disable.physical.network=Desabilitar rede f\u00edsica +label.action.disable.pod=Desativar POD +label.action.disable.pod.processing=Desativando POD.... +label.action.disable.static.NAT=Desativar NAT Est\u00e1tico +label.action.disable.static.NAT.processing=Desativando NAT Est\u00e1tico.... +label.action.disable.user=Desativar Usu\u00e1rio +label.action.disable.user.processing=Desativando Usu\u00e1rio.... +label.action.disable.zone=Desativar Zona +label.action.disable.zone.processing=Desativando Zona.... +label.action.download.ISO=Baixar ISO +label.action.download.template=Baixar Template +label.action.download.volume=Baixar Disco +label.action.download.volume.processing=Baixando Disco.... +label.action.edit.account=Editar conta +label.action.edit.disk.offering=Editar Oferta de Disco +label.action.edit.domain=Editar Dom\u00ednio +label.action.edit.global.setting=Editar Configura\u00e7\u00f5es Globais +label.action.edit.host=Editar Host +label.action.edit.instance=Editar Inst\u00e2ncia +label.action.edit.ISO=Editar ISO +label.action.edit.network=Editar Rede +label.action.edit.network.offering=Editar Oferta de Rede +label.action.edit.network.processing=Editarando Rede.... +label.action.edit.pod=Editar Pod +label.action.edit.primary.storage=Editar Storage Prim\u00e1rio +label.action.edit.resource.limits=Editar Limite de Recursos +label.action.edit.service.offering=Editar Plano +label.action.edit.template=Editar Template +label.action.edit.user=Editar Usu\u00e1rio +label.action.edit.zone=Editar Zona +label.action.enable.account=Ativar conta +label.action.enable.account.processing=Ativando conta.... +label.action.enable.cluster=Ativar Cluster +label.action.enable.cluster.processing=Ativando Cluster.... +label.action.enable.maintenance.mode=Ativar Modo de Manuten\u00e7\u00e3o +label.action.enable.maintenance.mode.processing=Ativando Modo de Manuten\u00e7\u00e3o.... +label.action.enable.nexusVswitch=Habilitar NexusVswitch +label.action.enable.physical.network=Habilitar rede f\u00edsica +label.action.enable.pod=Ativar POD +label.action.enable.pod.processing=Ativando POD.... +label.action.enable.static.NAT=Ativar NAT Est\u00e1tico +label.action.enable.static.NAT.processing=Ativando NAT Est\u00e1tico.... +label.action.enable.user=Habilitar usu\u00e1rio +label.action.enable.user.processing=Habilitando Usu\u00e1rio... +label.action.enable.zone=Ativar Zona +label.action.enable.zone.processing=Ativando Zona.... +label.action.expunge.instance=Eliminar Inst\u00e2ncia +label.action.expunge.instance.processing=Expurgando Inst\u00e2ncia.... +label.action.force.reconnect=Force Reconnect +label.action.force.reconnect.processing=Reconectando.... +label.action.generate.keys=Gerar Chaves +label.action.generate.keys.processing=Gerando Chaves.... +label.action.list.nexusVswitch=Listar NexusVswitch +label.action.lock.account=Bloquear conta +label.action.lock.account.processing=Bloqueando conta.... +label.action.manage.cluster.processing=Vinculando o Cluster.... +label.action.manage.cluster=Vincular Cluster +label.action.migrate.instance=Migrar Inst\u00e2ncia +label.action.migrate.instance.processing=Migrando Inst\u00e2ncia.... +label.action.migrate.router=Migrar Roteador +label.action.migrate.router.processing=Migrando Roteador... +label.action.migrate.systemvm=Migrar VM de Sistema +label.action.migrate.systemvm.processing=Migrando VM de Sistema... +label.action.reboot.instance.processing=Reiniciando Inst\u00e2ncia... +label.action.reboot.instance=Reiniciar Inst\u00e2ncia +label.action.reboot.router.processing=Reiniciando Roteador.... +label.action.reboot.router=Reiniciar Roteador +label.action.reboot.systemvm.processing=Reiniciando VM de Sistema.... +label.action.reboot.systemvm=Reiniciar VM de Sistema +label.action.recurring.snapshot=Snapshots recorrentes +label.action.register.iso=Registrar ISO +label.action.register.template=Registrar template +label.action.release.ip=Liberar IP +label.action.release.ip.processing=Liberando IP.... +label.action.remove.host.processing=Removendo Host.... +label.action.remove.host=Remover Host +label.action.reset.password.processing=Recuperando a Senha.... +label.action.reset.password=Recuperar Senha +label.action.resize.volume.processing=Resizing Volume.... +label.action.resize.volume=Resize Volume +label.action.resource.limits=Limite de Recursos +label.action.restore.instance.processing=Restaurando Inst\u00e2ncia... +label.action.restore.instance=Restaurar Inst\u00e2ncia +label.action.revert.snapshot.processing=Revertendo para Snapshot... +label.action.revert.snapshot=Reverter para Snapshot +label.actions=A\u00e7\u00f5es +label.action.start.instance=Iniciar Inst\u00e2ncia +label.action.start.instance.processing=Iniciando Inst\u00e2ncia... +label.action.start.router=Iniciar Roteador +label.action.start.router.processing=Iniciando Roteador.... +label.action.start.systemvm=Iniciar VM de Sistema +label.action.start.systemvm.processing=Iniciando VM de Sistema.... +label.action.stop.instance=Parar Inst\u00e2ncia +label.action.stop.instance.processing=Parando Inst\u00e2ncia... +label.action.stop.router=Parar Roteador +label.action.stop.router.processing=Parando Roteador.... +label.action.stop.systemvm=Parar VM de Sistema +label.action.stop.systemvm.processing=Parando VM de Sistema.... +label.action.take.snapshot.processing=Tirando Snapshot.... +label.action.take.snapshot=Tirar Snapshot +label.action.unmanage.cluster=Desvincular Cluster +label.action.unmanage.cluster.processing=Desvinculando Cluster.... +label.action.update.OS.preference=Atualizar Prefer\u00eancia de SO +label.action.update.OS.preference.processing=Atualizando Prefer\u00eancia de SO.... +label.action.update.resource.count=Atualiza Contador de Recursos +label.action.update.resource.count.processing=Atualizando Contador de Recursos.... +label.action.vmsnapshot.create=Fazer Snapshot de VM +label.action.vmsnapshot.delete=Remover snapshot de VM +label.action.vmsnapshot.revert=Reverter snapshot de VM +label.activate.project=Ativar Projeto +label.active.sessions=Sess\u00f5es Ativas +label.add.account=Adicionar Conta +label.add.accounts=Adicionar contas +label.add.accounts.to=Adicionar contas para +label.add.account.to.project=Adicionar conta ao projeto +label.add.ACL=Adicionar ACL +label.add.acl.list=Adiciona Lista ACL +label.add=Adicionar +label.add.affinity.group=Adicionar um grupo de afinidade +label.add.baremetal.dhcp.device=Adiciona Dispositivo DHCP Baremetal +label.add.BigSwitchVns.device=Adicionar BigSwitch Vns Controller +label.add.BrocadeVcs.device=Adicionar Brocade Vcs Switch +label.add.by=Adicionado por +label.add.by.cidr=Adicionar por CIDR +label.add.by.group=Adicionar por Grupo +label.add.ciscoASA1000v=Adicone Recurso CiscoASA1000v +label.add.cluster=Adicionar Cluster +label.add.compute.offering=Adicionar oferta de computa\u00e7\u00e3o +label.add.direct.iprange=Add Direct Ip Range +label.add.disk.offering=Adicionar Oferta de Disco +label.add.domain=Adicionar Dom\u00ednio +label.added.brocade.vcs.switch=Adicionado novo Brocade Vcs Switch +label.added.new.bigswitch.vns.controller=Adicionado BigSwitch VNS Controller +label.added.nicira.nvp.controller=Adicionado nova Controladora Nicira NVP +label.add.egress.rule=Adicionar regra egress +label.addes.new.f5=Adicionado novo F5 +label.add.F5.device=Adicionar dispositivo F5 +label.add.firewall=Adicionar regra de Firewall +label.add.gslb=Adicionar GSLB +label.add.guest.network=Adicionar rede guest +label.add.host=Adicionar Host +label.adding=Adicionando +label.adding.cluster=Adicionando Cluster +label.adding.failed=Falha ao Adicionar +label.adding.pod=Adicionando POD +label.adding.processing=Adicionando.... +label.add.ingress.rule=Adicionar Regra de Entrada +label.adding.succeeded=Adicionado com Sucesso +label.adding.user=Adicionando Usu\u00e1rio +label.adding.zone=Adicionando Zona +label.add.intermediate.certificate=Adicionar certificado intermedi\u00e1rio +label.add.internal.lb=Adiciona LB Interno +label.add.ip.range=Adicionar Range de IP +label.add.isolated.guest.network=Adiciona Rede Guest Isolada +label.add.isolated.network=Adiciona Rede Isolada +label.additional.networks=Redes Adicionais +label.add.LDAP.account=Adicionar Conta LDAP +label.add.list.name=Nome de Lista ACL +label.add.load.balancer=Adicionar Balanceador de Carga +label.add.more=Adicionar Mais +label.add.netScaler.device=Adicionar dispositivo Netscaler +label.add.network.ACL=Adicione ACL de rede +label.add.network.acl.list=Adicionar Lista de ACL de Rede +label.add.network=Adicionar Rede +label.add.network.device=Adicionar Dispositivo de Rede +label.add.network.offering=Adicionar oferta de rede +label.add.new.F5=Adicionar um novo F5 +label.add.new.gateway=Adicionar novo gateway +label.add.new.NetScaler=Adicionar um novo NetScaler +label.add.new.PA=Adicionar novo Palo Alto +label.add.new.SRX=Adicionar um novo SRX +label.add.new.tier=Adicionar nova camada +label.add.nfs.secondary.staging.store=Adiciona Armazenamento NFS de Est\u00e1gio Secund\u00e1rio +label.add.NiciraNvp.device=Adicionar Controlador Nvp +label.add.NuageVsp.device=Adicionar Nuage Virtualized Services Directory (VSD) +label.add.OpenDaylight.device=Adiciona Controlador OpenDaylight +label.add.PA.device=Adicionar dispositivo Palo Alto +label.add.physical.network=Adicionar rede f\u00edsica +label.add.pod=Adicionar POD +label.add.portable.ip.range=Adicionar Faixa de Endere\u00e7os IPs Port\u00e1veis +label.add.port.forwarding.rule=Adicionar regra de encaminhamento de porta +label.add.primary.storage=Adicionar Storage Prim\u00e1rio +label.add.region=Adicionar Regi\u00e3o +label.add.resources=Adicionar Recursos +label.add.route=Adicionar rota +label.add.rule=Adicionar regra +label.add.secondary.storage=Adicionar Storage Secund\u00e1rio +label.add.security.group=Adicionar Security Group +label.add.service.offering=Adicionar Plano +label.add.SRX.device=Adicionar dispositivo SRX +label.add.static.nat.rule=Adicionar regra de NAT est\u00e1tico +label.add.static.route=Adicionar rota est\u00e1tica +label.add.system.service.offering=Adicionar Plano para VM de Sistema +label.add.template=Adicionar Template +label.add.to.group=Adicionar ao grupo +label.add.ucs.manager=Adiciona Gerenciador UCS +label.add.user=Adicionar Usu\u00e1rio +label.add.vlan=Adicionar VLAN +label.add.vm=Adicionar VM +label.add.vms=Adicionar VMs +label.add.vms.to.lb=Adicionar VM(s) na regra de balanceamento de carga +label.add.VM.to.tier=Adicionar m\u00e1quina virtual \u00e0 camada +label.add.vmware.datacenter=Adicionar Datacenter VMware +label.add.vnmc.device=Adiciona dispositivo VNMC +label.add.vnmc.provider=Adiciona provedor VNMC +label.add.volume=Adicionar Disco +label.add.vpc=Adicionar VPC +label.add.vpc.offering=Adicionar Oferta VPC +label.add.vpn.customer.gateway=Adicionar Gateway de VPN de usu\u00e1rio +label.add.VPN.gateway=Adicionar gateway de VPN +label.add.vpn.user=Adicionar usu\u00e1rio VPN +label.add.vxlan=Adicionar VXLAN +label.add.zone=Adicionar Zona +label.admin.accounts=Contas Administrativas +label.admin=Administrador +label.advanced=Avan\u00e7ado +label.advanced.mode=Modo Avan\u00e7ado +label.advanced.search=Busca Avan\u00e7ada +label.affinity=Afinidade +label.affinity.group=Grupo de Afinidade +label.affinity.groups=Grupos de Afinidade +label.agent.password=Senha do Agente +label.agent.state=Estado do Agente +label.agent.username=Usu\u00e1rio do Agente +label.agree=Concordo +label.alert=Alerta +label.alert.archived=Alerta Arquivado +label.alert.deleted=Alerta Apagado +label.alert.details=Detalhes de alerta +label.algorithm=Algoritmo +label.allocated=Alocado +label.allocation.state=Status da Aloca\u00e7\u00e3o +label.allow=Pertitir +label.anti.affinity=Anti-afinidade +label.anti.affinity.group=Grupo de Anti-afinidade +label.anti.affinity.groups=Grupos de Anti-afinidade +label.api.key=API Key +label.api.version=Ver\u00e3o da API +label.apply=Aplicar +label.app.name=CloudStack +label.archive.alerts=Guardar alertas +label.archive=Arquivo +label.archive.events=Guardar eventos +label.assign=Atribuir +label.assigned.vms=VMs designadas +label.assign.instance.another=Atribuir Inst\u00e2ncia para outra Conta +label.assign.to.load.balancer=Atribuindo Inst\u00e2ncia ao balanceador de carga +label.associated.network.id=ID de Rede Associado +label.associated.network=Rede associada +label.associated.profile=Perfil Associado +label.associate.public.ip=Associa IP P\u00fablico +label.attached.iso=Imagem ISO Plugada +label.author.email=E-mail do autor +label.author.name=Nome do autor +label.autoscale=Escalonamento Autom\u00e1tico +label.availability=Availability +label.availability.zone=Datacenter +label.available=Dispon\u00edvel +label.available.public.ips=IP P\u00fablico Dispon\u00edvel +label.back=Voltar +label.bandwidth=Bandwidth +label.baremetal.dhcp.devices=Dispositivos DHCP Baremetal +label.baremetal.dhcp.provider=Provedor DHCP Baremetal +label.baremetal.pxe.device=Adiciona Dispositivo PXE Baremetal +label.baremetal.pxe.devices=Dispositivo PXE Baremetal +label.baremetal.pxe.provider=Provedor PXE Baremetal +label.basic=B\u00e1sico +label.basic.mode=Modo B\u00e1sico +label.bigswitch.controller.address=Endere\u00e7o do Controlador BigSwitch Vns +label.bigswitch.vns.details=Detalhes do +label.blade.id=ID da L\u00e2mina +label.blades=L\u00e2minas +label.bootable=Inicializ\u00e1vel +label.broadcast.domain.range=Range do dom\u00ednio de Broadcast +label.broadcast.domain.type=Tipo de Dom\u00ednio Broadcast +label.broadcast.uri=URI de broadcast +label.broadcasturi=url de broadcast +label.broadcat.uri=URI de broadcast +label.brocade.vcs.address=Endere\u00e7o do Vcs Switch +label.brocade.vcs.details=Detalhes do Brocade Vcs Switch +label.by.account=por Conta +label.by.alert.type=Por tipo de alerta +label.by.availability=By Availability +label.by.date.end=Por data (final) +label.by.date.start=Por data (in\u00edcio) +label.by.domain=por Dom\u00ednio +label.by.end.date=por Data Final +label.by.event.type=Por tipo de evento +label.by.level=por N\u00edvel +label.by.pod=por Pod +label.by.role=por Fun\u00e7\u00e3o +label.by.start.date=por Data Inicial +label.by.state=por estado +label.bytes.received=Bytes Recebidos +label.bytes.sent=Bytes Enviados +label.by.traffic.type=por Tipo de Tr\u00e1fego +label.by.type.id=por Tipo de ID +label.by.type=Por Tipo +label.by.zone=por Zona +label.cache.mode=Tipo do cache de escrita +label.cancel=Cancelar +label.capacity.bytes=Capacidade de Bytes +label.capacity=Capacidade +label.capacity.iops=Capacidade de IOPS +label.certificate=Certificado +label.change.affinity=Muda Afinidade +label.change.service.offering=Alterar oferta de servi\u00e7o +label.change.value=Alterar valor +label.character=Caracter +label.chassis=Chassis +label.cidr.account=CIDR ou Conta/Security Group +label.cidr=CIDR +label.cidr.list=CIDR de Origem +label.CIDR.list=Lista CIDR +label.CIDR.of.destination.network=CIDR da rede de destino +label.cisco.nexus1000v.ip.address=Endere\u00e7o IP do Nexus 1000v +label.cisco.nexus1000v.password=Senha do Nexus 1000v +label.cisco.nexus1000v.username=Nome do Usu\u00e1rio do Nexus 1000v +label.ciscovnmc.resource.details=Detalhes de recurso CiscoVNMC +label.clean.up=Limpar +label.clear.list=Limpar lista +label.close=Fechar +label.cloud.console=Console de Gerenciamento da Nuvem +label.cloud.managed=Cloud.com Managed +label.cluster=Cluster +label.cluster.name=Nome do Cluster +label.clusters=Clusters +label.cluster.type=Tipo de Cluster +label.clvm=CLVM +label.code=C\u00f3digo +label.community=Comunidade +label.compute.and.storage=Processamento e Armazenamento +label.compute=Computa\u00e7\u00e3o +label.compute.offering=Oferta de Computa\u00e7\u00e3o +label.compute.offerings=Oferta de Computa\u00e7\u00e3o +label.configuration=Configura\u00e7\u00e3o +label.configure=Configurar +label.configure.ldap=Configurar LDAP +label.configure.network.ACLs=Configure ACLs de rede +label.configure.vpc=Configurar VPC +label.confirmation=Confirma\u00e7\u00e3o +label.confirm.password=Confirme a senha +label.congratulations=Parab\u00e9ns\! +label.conserve.mode=Modo Conservativo +label.console.proxy=Console proxy +label.console.proxy.vm=VM da Console Proxy +label.continue.basic.install=Continuar com a instala\u00e7\u00e3o b\u00e1sica +label.continue=Continuar +label.copying.iso=Copiando ISO +label.corrections.saved=Altera\u00e7\u00f5es salvas +label.counter=Contador +label.cpu.allocated=CPU Alocada +label.cpu.allocated.for.VMs=CPU Alocada por VMs +label.CPU.cap=CPU Cap +label.cpu=CPU +label.cpu.limits=Limite de CPU +label.cpu.mhz=CPU (em MHz) +label.cpu.utilized=CPU Utilizada +label.created.by.system=Criado pelo sistema +label.created=Criado +label.create.nfs.secondary.staging.storage=Cria Armazenamento NFS de Est\u00e1gio Secund\u00e1rio +label.create.nfs.secondary.staging.store=Criar storage staging secund\u00e1rio NFS +label.create.project=Criar um projeto +label.create.template=Criar template +label.create.VPN.connection=Criar uma conex\u00e3o VPN +label.cross.zones=Inter Zonas +label.custom=Customizado +label.custom.disk.iops=IOPS personalizado +label.custom.disk.size=Tamanho Customizado +label.daily=Di\u00e1rio +label.data.disk.offering=Oferta de Disco Adicional +label.date=Data +label.day.of.month=Dia do M\u00eas +label.day.of.week=Dia da Semana +label.dc.name=Nome do DC +label.dead.peer.detection=Detec\u00e7\u00e3o de correspondente morto +label.decline.invitation=Rejeitar convite +label.dedicate.cluster=Dedica Cluster +label.dedicated=Dedicado +label.dedicate=Dedicado +label.dedicated.vlan.vni.ranges=Ranges de VLAN/VNI Dedicados +label.dedicate.host=Dedica Host +label.dedicate.pod=Dedique Pod +label.dedicate.vlan.vni.range=Range de VLAN/VNI Dedicado +label.dedicate.zone=Dedicar Zona +label.default.egress.policy=Pol\u00edtica padr\u00e3o de egress\u00e3o +label.default=Padr\u00e3o +label.default.use=Uso padr\u00e3o +label.default.view=Vis\u00e3o Padr\u00e3o +label.delete.acl.list=Apagar Lista ACL +label.delete.affinity.group=Deletar Grupo de Afinidade +label.delete.alerts=Remover alertas +label.delete.BigSwitchVns=Remover Controlador BigSwitch Vns +label.delete.BrocadeVcs=Remover Brocade Vcs Switch +label.delete.ciscoASA1000v=Apaga CiscoASA1000v +label.delete.ciscovnmc.resource=Apaga recurso CiscoVNMC +label.delete.events=Remover eventos +label.delete.F5=Remover F5 +label.delete.gateway=delete gateway +label.delete.internal.lb=Apaga LB Interno +label.delete.NetScaler=Remover NetScaler +label.delete.NiciraNvp=Remover Controlador Nvp +label.delete.NuageVsp=Remover Nuage VSD +label.delete.OpenDaylight.device=Apaga Controladora OpenDaylight +label.delete.PA=Remover Palo Alto +label.delete.portable.ip.range=Deletar Endere\u00e7os IPs Port\u00e1teis +label.delete.profile=Apaga Perfil +label.delete.project=Deletar projeto +label.delete=Remover +label.delete.secondary.staging.store=Apaga Armazenamento de Est\u00e1gio Secund\u00e1rio +label.delete.SRX=Remover SRX +label.delete.ucs.manager=Apaga Gerenciador UCS +label.delete.VPN.connection=deletar a conex\u00e3o VPN +label.delete.VPN.customer.gateway=deletar gateway de VPN de usu\u00e1rio +label.delete.VPN.gateway=deletar um gateway de VPN +label.delete.vpn.user=Deletar usu\u00e1rio VPN +label.deleting.failed=Falha ao remover +label.deleting.processing=Removendo.... +label.deny=Negar +label.deployment.planner=Deployment planejado +label.description=Descri\u00e7\u00e3o +label.destination.physical.network.id=ID de destino da rede f\u00edsica +label.destination.zone=Zona de Destino +label.destroy=Apagar +label.destroy.router=Destruir roteador +label.destroy.vm.graceperiod=Destruir Grace Period da VM +label.detaching.disk=Desplugando Disco +label.details=Detalhes +label.device.id=ID do Dispositivo +label.devices=Dispositivos +label.dhcp=DHCP +label.DHCP.server.type=Tipo de Servidor DHCP +label.direct.attached.public.ip=IP P\u00fablico Conectado Diretamente +label.direct.ips=IPs Diretos +label.disable.autoscale=Desabilita Auto-escala +label.disabled=Desativado +label.disable.host=Desabilita Host +label.disable.network.offering=Desabilita oferta de rede +label.disable.provider=Desabilitar Provider +label.disable.vnmc.provider=Habilita provedor VNMC +label.disable.vpc.offering=Desabilitar oferta VPC +label.disable.vpn=Desabilitar VPN +label.disabling.vpn.access=Desativando Acesso VPN +label.disassociate.profile.blade=Desassocia Perfil de L\u00e2mina +label.disbale.vnmc.device=Desabilita dispositivo VNMC +label.disk.allocated=Disco Alocado +label.disk.bytes.read.rate=Taxa de Leitura do Disco (BPS) +label.disk.bytes.write.rate=Taxa de Escrita no Disco (BPS) +label.disk.iops.max=M\u00e1x IOPS +label.disk.iops.min=M\u00edn IOPS +label.disk.iops.read.rate=Taxa de Leitura do Disco (IOPS) +label.disk.iops.total=IOPS Total +label.disk.iops.write.rate=Taxa de Escrita no Disco (IOPS) +label.disk.offering=Oferta de Disco +label.disk.provisioningtype=Tipo de Provisionamento +label.disk.read.bytes=Leitura do Disco (Bytes) +label.disk.read.io=Leitura do Disk (I/O) +label.disk.size.gb=Tamanho (em GB) +label.disk.size=Tamanho do Disco +label.disk.total=Disco Total +label.disk.volume=Disco +label.disk.write.bytes=Escrita no Disco (Bytes) +label.disk.write.io=Escrita no Disco (I/O) +label.display.name=Nome de exibi\u00e7\u00e3o +label.display.text=Descri\u00e7\u00e3o +label.distributedrouter=Roteador Distribuido +label.dns.1=DNS 1 +label.dns.2=DNS 2 +label.dns=DNS +label.DNS.domain.for.guest.networks=Dom\u00ednio DNS para redes h\u00f3spedes +label.domain.admin=Administrador de Dom\u00ednio +label.domain=Dom\u00ednio +label.domain.id=ID do Dom\u00ednio +label.domain.lower=dom\u00ednio +label.domain.name=Nome do Dom\u00ednio +label.domain.router=Roteador do Dom\u00ednio +label.domain.suffix=Sufixo de Dom\u00ednio DNS (ex. xyz.com) +label.done=Pronto +label.double.quotes.are.not.allowed=Aspas duplas n\u00e3o s\u00e3o permitidas +label.download.progress=Status do Download +label.drag.new.position=Arrastar para uma nova posi\u00e7\u00e3o +label.duration.in.sec=Dura\u00e7\u00e3o (em seg) +label.scaleup.step=Passo do scale up +label.scale.logicaloperator=Operador L\u00f3gico +label.scaledown.step=Passo do scale down +label.dynamically.scalable=Dinamicamente Escal\u00e1vel +label.edit.acl.rule=Edita regra ACL +label.edit.affinity.group=Editar Grupo de Afinidade +label.edit=Editar +label.edit.lb.rule=Editar regra de LB +label.edit.network.details=Editar detalhes de rede +label.edit.project.details=Editar detalhes do projeto +label.edit.region=Editar Regi\u00e3o +label.edit.tags=Edite etiquetas +label.edit.traffic.type=Editar tipo de tr\u00e1fego +label.edit.vpc=Editar VPC +label.egress.default.policy=Pol\u00edtica de Entrada Padr\u00e3o +label.egress.rule=Regra Egress +label.egress.rules=Regras de sa\u00edda +label.elastic=El\u00e1stico +label.elastic.IP=IP El\u00e1stico +label.elastic.LB=LB El\u00e1stico +label.email=Email +label.email.lower=email +label.enable.autoscale=Habilita Auto-escala +label.enable.host=Habilita Host +label.enable.network.offering=Habilita oferta de rede +label.enable.provider=Habilitar provider +label.enable.s3=Habilita storage secund\u00e1ria fornecida por S3 +label.enable.swift=Habilitar Swift +label.enable.vnmc.device=Habilita dispositivo VNMC +label.enable.vnmc.provider=Habilita provedor VNMC +label.enable.vpc.offering=Habilitar oferta VPC +label.enable.vpn=Habilitar VPN +label.enabling.vpn.access=Ativando Acesso VPN +label.enabling.vpn=Ativando VPN +label.end.IP=IP do fim +label.endpoint.or.operation=Endpoint or Operation +label.endpoint=Ponto de acesso +label.end.port=Porta Final +label.end.reserved.system.IP=Fim dos IPs reservados para o sistema +label.end.vlan=VLAN final +label.end.vxlan=VXLAN final +label.enter.token=Digite o token +label.error.code=C\u00f3digo de Erro +label.error=Erro +label.error.upper=ERRO +label.ESP.encryption=Encripta\u00e7\u00e3o ESP +label.ESP.hash=Hash ESP +label.ESP.lifetime=Tempo de vida do ESP (segundos) +label.ESP.policy=Pol\u00edtica ESP +label.esx.host=ESX/ESXi Host +label.event.archived=Evento Arquivado +label.event.deleted=Evento Detectado +label.example=Examplo +label.expunge=Eliminar +label.external.link=Link externo +label.extractable=Extra\u00edvel +label.f5.details=Detalhes do F5 +label.f5=F5 +label.failed=Falhou +label.featured=Featured +label.fetch.latest=Obter \u00faltimos +label.filterBy=Filtrar por +label.firewall=Firewall +label.firstname.lower=primeiro nome +label.first.name=Primeiro Nome +label.format=Formato +label.friday=Sexta-feira +label.full=Full +label.full.path=Path completo +label.gateway=Gateway +label.general.alerts=Alertas Gerais +label.generating.url=Criando URL +label.gluster.volume=Disco +label.go.step.2=V\u00e1 para passo 2 +label.go.step.3=V\u00e1 para passo 3 +label.go.step.4=V\u00e1 para passo 4 +label.go.step.5=V\u00e1 para passo 5 +label.gpu=CPU +label.group.by.account=Agrupamento por conta +label.group.by.cluster=Agrupamento por cluster +label.group.by.pod=Agrupamento por pod +label.group.by.zone=Agrupamento por zona +label.group=Grupo +label.group.optional=Grupo (Opcional) +label.gslb.assigned.lb=Balanceamento de carga designado +label.gslb.assigned.lb.more=Designe mais balanceamento de carga +label.gslb.delete=Apaga GSLB +label.gslb.details=Detalhes do GSLB +label.gslb.domain.name=Nome do Dom\u00ednio GSLB +label.gslb=GSLB +label.gslb.lb.details=Detalhes de balanceamento de carga +label.gslb.lb.remove=Remova balanceamento de carga deste GSLB +label.gslb.lb.rule=Regra de balanceamento de carga +label.gslb.service.private.ip=Servi\u00e7o GSLB - IP Privado +label.gslb.service.public.ip=Servi\u00e7o GSLB - IP P\u00fablico +label.gslb.service=Servi\u00e7o GSLB +label.gslb.servicetype=Tipo do Servi\u00e7o +label.guest.cidr=CIDR de rede Convidado +label.guest.end.ip=IP do fim do guest +label.guest.gateway=Gateway de rede Convidado +label.guest=Guest +label.guest.ip=Endere\u00e7o IP Convidado +label.guest.ip.range=Intervalo de rede convidado +label.guest.netmask=M\u00e1scara de rede Guest +label.guest.network.details=Detalhes de rede convidada +label.guest.networks=Redes Guest +label.guest.start.ip=IP de in\u00edcio do guest +label.guest.traffic=Tr\u00e1fego de h\u00f3spedes +label.guest.traffic.vswitch.name=Nome do vSwitch de Tr\u00e1fego Convidado +label.guest.traffic.vswitch.type=Tipo de vSwitch de Tr\u00e1fego Convidado +label.guest.type=Tipo de Guest +label.ha.enabled=HA Ativado +label.health.check=Checagem de Sa\u00fade +label.health.check.interval.in.sec=Intervalo de Health Check (em seg) +label.healthy.threshold=Limiar de Salubridade +label.help=Ajuda +label.hide.ingress.rule=Ocultar Regra de Entrada +label.hints=Dicas +label.home=Home +label.host.alerts=Alertas de Host +label.host=Host +label.host.MAC=Host MAC +label.host.name=Host Name +label.hosts=Hosts +label.host.tags=Tags de Host +label.hourly=A cada hora +label.hvm=HVM +label.hypervisor.capabilities=Recursos de Virtualizador +label.hypervisor=Hipervisor +label.hypervisors=Hypervisors +label.hypervisor.snapshot.reserve=Reserva de Snapshot do Hypervisor +label.hypervisor.type=Tipo do Hypervisor +label.hypervisor.version=Vers\u00e3o de Virtualizador +label.hyperv.traffic.label=R\u00f3tulo de tr\u00e1fego HyperV +label.id=ID +label.IKE.DH=DH IKE +label.IKE.encryption=Encripta\u00e7\u00e3o IKE +label.IKE.hash=Hash IKE +label.IKE.lifetime=Tempo de vida IKE (segundos) +label.IKE.policy=Pol\u00edtica IKE +label.info=Info +label.info.upper=INFO +label.ingress.rule=Regra de Entrada +label.initiated.by=Iniciado por +label.inside.port.profile=Perfil de Porta Interna +label.installWizard.addClusterIntro.subtitle=O que \u00e9 um cluster? +label.installWizard.addClusterIntro.title=Vamos adicionar um cluster +label.installWizard.addHostIntro.subtitle=O que \u00e9 um Host ? +label.installWizard.addHostIntro.title=Vamos adicionar um host +label.installWizard.addPodIntro.subtitle=O que \u00e9 um pod ? +label.installWizard.addPodIntro.title=Vamos adicionar um pod +label.installWizard.addPrimaryStorageIntro.subtitle=Qual \u00e9 o storage prim\u00e1rio ? +label.installWizard.addPrimaryStorageIntro.title=Vamos adicionar o storage prim\u00e1rio +label.installWizard.addSecondaryStorageIntro.subtitle=Qual \u00e9 o storage secund\u00e1rio ? +label.installWizard.addSecondaryStorageIntro.title=Vamos adicionar o storage secund\u00e1rio +label.installWizard.addZoneIntro.subtitle=O que \u00e9 uma zona? +label.installWizard.addZoneIntro.title=Vamos adicionar uma zona +label.installWizard.addZone.title=Adicionar zona +label.installWizard.click.launch=Click no bot\u00e3o executar. +label.installWizard.subtitle=Este tour vai auxiliar voc\u00ea na configura\u00e7\u00e3o da sua instala\u00e7\u00e3o de CloudStack&\#8482 +label.installWizard.title=Ol\u00e1, seja bem vindo ao CloudStack&\#8482 +label.instance=Inst\u00e2ncia +label.instance.limits=Limites da Inst\u00e2ncia +label.instance.name=Nome da Inst\u00e2ncia +label.instance.port=Instanciar Porta +label.instance.scaled.up=Inst\u00e2ncia Escalada +label.instances=Inst\u00e2ncias +label.instanciate.template.associate.profile.blade=Instancia Template e Associa Perfil \u00e0 L\u00e2mina +label.intermediate.certificate=Certificado intermedi\u00e1rio {0} +label.internal.dns.1=DNS 1 Interno +label.internal.dns.2=DNS 2 Interno +label.internal.lb.details=Detalhes de LB Interno +label.internallbvm=LbVm Interno +label.internal.name=Nome interno +label.interval.type=Tipo de Intervalo +label.introduction.to.cloudstack=Introdu\u00e7\u00e3o ao CloudStack&\#8482 +label.invalid.integer=Invalid Integer +label.invalid.number=N\u00famero inv\u00e1lido +label.invitations=Convites +label.invite=Convidar +label.invited.accounts=Contas convidadas +label.invite.to=Convidar para +label.ip.address=Endere\u00e7o IP +label.ipaddress=Endere\u00e7o IP +label.ip.allocations=Aloca\u00e7\u00f5es de IP +label.ip=IP +label.ip.limits=Limites de IP P\u00fablico +label.ip.or.fqdn=IP ou FQDN +label.ip.range=Range de IP +label.ip.ranges=Ranges de IP +label.IPsec.preshared.key=Chave IPSec pr\u00e9 compartilhada +label.ips=IPs +label.ipv4.cidr=CIDR IPv4 +label.ipv4.dns1=IPv4 DNS1 +label.ipv4.dns2=IPv4 DNS2 +label.ipv4.end.ip=IP FInal IPv4 +label.ipv4.gateway=Gateway IPV4 +label.ipv4.netmask=M\u00e1scara de Rede IPv4 +label.ipv4.start.ip=IP Inicial IPv4 +label.ipv6.address=Endere\u00e7o IPv6 +label.ipv6.CIDR=CIDR IPv6 +label.ipv6.dns1=IPv6 DNS1 +label.ipv6.dns2=IPv6 DNS2 +label.ipv6.end.ip=IP FInal IPv6 +label.ipv6.gateway=Gateway IPv6 +label.ipv6.start.ip=IP Inicial IPv6 +label.iscsi=iSCSI +label.is.default=\u00c9\u0089 Padr\u00e3o +label.iso.boot=ISO de Boot +label.iso=ISO +label.isolated.networks=Redes Isoladas +label.isolation.method=M\u00e9todo de isolamento +label.isolation.mode=Modo Isolado +label.isolation.uri=URI de isolamento +label.is.redundant.router=Redundante +label.is.shared=\u00c9 Compartilhado +label.is.system=\u00e9 um sistema +label.item.listing=Listar items +label.keep=Manter +label.keyboard.type=Tipo de Teclado +label.key=Chave +label.kvm.traffic.label=Etiqueta de tr\u00e1fego KVM +label.label=Etiqueta +label.lang.arabic=Arabe +label.lang.brportugese=Portugu\u00eas brasileiro +label.lang.catalan=Catal\u00e3o +label.lang.chinese=Chinese (Simplified) +label.lang.dutch=Holand\u00eas (Holanda) +label.lang.english=English +label.lang.french=Franc\u00eas +label.lang.german=Alem\u00e3o +label.lang.hungarian=H\u00fangaro +label.lang.italian=Italiano +label.lang.japanese=Japanese +label.lang.korean=Coreano +label.lang.norwegian=Noruegu\u00eas +label.lang.polish=Polon\u00eas +label.lang.russian=Russo +label.lang.spanish=Spanish +label.last.disconnected=Last Disconnected +label.lastname.lower=\u00faltimo nome +label.last.name=\u00daltimo Nome +label.latest.events=\u00daltimos eventos +label.launch=Executar +label.launch.vm=Executar VM +label.launch.zone=Executar zona. +label.lb.algorithm.leastconn=Least connections +label.lb.algorithm.roundrobin=Round-robin +label.lb.algorithm.source=Origem +label.LB.isolation=Isolamento de LB +label.ldap.configuration=Configura\u00e7\u00e3o do LDAP +label.ldap.group.name=Grupo LDAP +label.ldap.port=Porta do LDAP +label.level=N\u00edvel +label.linklocal.ip=Endere\u00e7o IP do Link Local +label.load.balancer=Balanceador de Carga +label.load.balancer.type=Tipo de Balanceador de Carga +label.load.balancing=Balanceamento de Carga +label.load.balancing.policies=Pol\u00edticas de balanceamento de carga +label.loading=Carregando +label.local=Local +label.local.storage.enabled=Habilitar storage local para VMs de usu\u00e1rios +label.local.storage.enabled.system.vms=Habilitar storage local para VMs de Sistema +label.local.storage=Storage Local +label.login=Entrar +label.logout=Sair +label.lun=LUN +label.LUN.number=LUN \# +label.lxc.traffic.label=R\u00f3tulo de tr\u00e1fego LXC +label.make.project.owner=Criar propriet\u00e1rio de conta de projeto +label.managed=Gerenciado +label.manage=Gerenciar +label.management=Gerenciamento +label.management.ips=Gerenciamento de Endere\u00e7os IP +label.management.server=Servidor de Gerenciamento +label.manage.resources=Gerenciar Recursos +label.max.cpus=M\u00e1ximo de cores de CPU +label.max.guest.limit=Limite m\u00e1x. de guest +label.maximum=M\u00e1ximo +label.max.instances=Inst\u00e2ncias Max +label.max.memory=M\u00e1x. de mem\u00f3ria (MiB) +label.max.networks=M\u00e1x. de redes +label.max.primary.storage=M\u00e1x. prim\u00e1rio (GiB) +label.max.public.ips=M\u00e1x. IPs p\u00fablicos +label.max.secondary.storage=Max. Secund\u00e1rio (GiB) +label.max.snapshots=Max. snapshots +label.max.templates=M\u00e1x. templates +label.max.vms=M\u00e1x. VMs de usu\u00e1rio +label.max.volumes=M\u00e1x. volumes +label.max.vpcs=M\u00e1x. VPCs +label.may.continue=Voc\u00ea pode continuar agora +label.md5.checksum=MD5 checksum +label.memory.allocated=Mem\u00f3ria Alocada +label.memory.limits=Limites de mem\u00f3ria (MiB) +label.memory.mb=Mem\u00f3ria (em MB) +label.memory=Mem\u00f3ria (em MB) +label.memory.total=Mem\u00f3ria Total +label.memory.used=Mem\u00f3ria Usada +label.menu.accounts=Contas +label.menu.alerts=Alertas +label.menu.all.accounts=Todas as Contas +label.menu.all.instances=Todas Inst\u00e2ncias +label.menu.community.isos=ISOs P\u00fablicas +label.menu.community.templates=Templates P\u00fablicos +label.menu.configuration=Configura\u00e7\u00e3o +label.menu.dashboard=Dashboard +label.menu.destroyed.instances=Inst\u00e2ncias Apagadas +label.menu.disk.offerings=Oferta de Discos +label.menu.domains=Dom\u00ednios +label.menu.events=Eventos +label.menu.featured.isos=ISOs Customizada +label.menu.featured.templates=Templates Customizados +label.menu.global.settings=Configura\u00e7\u00f5es Globais +label.menu.infrastructure=Infra-estrutura +label.menu.instances=Inst\u00e2ncias +label.menu.ipaddresses=Endere\u00e7os IP +label.menu.isos=ISOs +label.menu.my.accounts=Minhas Contas +label.menu.my.instances=Minhas Inst\u00e2ncias +label.menu.my.isos=Minhas ISOs +label.menu.my.templates=Meus Templates +label.menu.network.offerings=Oferta de Rede +label.menu.network=Rede +label.menu.physical.resources=Recursos B\u00e1\u00adsicos +label.menu.regions=Regi\u00f5es +label.menu.running.instances=Inst\u00e2ncias Rodando +label.menu.security.groups=Grupos de seguran\u00e7a +label.menu.service.offerings=Oferta de Servi\u00e7os +label.menu.snapshots=Snapshots +label.menu.stopped.instances=Inst\u00e2ncias Paradas +label.menu.storage=Storage +label.menu.system.service.offerings=Ofertas do Sistema +label.menu.system=Sistema +label.menu.system.vms=VM de Sistemas +label.menu.templates=Templates +label.menu.virtual.appliances=Appliance Virtual +label.menu.virtual.resources=Recursos Virtuais +label.menu.volumes=Discos +label.menu.vpc.offerings=Ofertas VPC +label.migrate.instance.to.host=Migrar inst\u00e2ncia para outro host +label.migrate.instance.to=Migrar Inst\u00e2ncia para +label.migrate.instance.to.ps=Migrar inst\u00e2ncia para outro storage prim\u00e1rio +label.migrate.lb.vm=Migre LB VM +label.migrate.router.to=Migrar Roteador para +label.migrate.systemvm.to=Migrar VM de sistema para +label.migrate.to.host=Migrar para outro host +label.migrate.to.storage=Migrar para storage +label.migrate.volume=Migrar Volume +label.migrate.volume.to.primary.storage=Migrar volume para outro storage prim\u00e1rio +label.minimum=M\u00ed\u00adnimo +label.min.instances=Inst\u00e2ncias Min +label.minute.past.hour=minuto(s) passado(s) da \u00faltima hora +label.mode=Modo +label.monday=Segunda +label.monthly=Mensal +label.more.templates=Mais Templates +label.move.down.row=Mover uma c\u00e9lula para baixo +label.move.to.bottom=Mover para baixo +label.move.to.top=Mover para o topo +label.move.up.row=Mover uma c\u00e9lula para cima +label.my.account=Minha Conta +label.my.network=Minha rede +label.my.templates=Meus templates +label.name.lower=Nome +label.name=Nome +label.name.optional=Nome (Opcional) +label.na=N/D +label.nat.port.range=Range de Portas NAT +label.netmask=M\u00e1scara de Rede +label.netscaler.details=Detalhes do NetScaler +label.netScaler=NetScaler +label.network.ACL=ACL de rede +label.network.ACLs=Network ACLs +label.network.ACL.total=Total de rede ACL +label.network.addVM=Adicionar rede para VM +label.network.cidr=CIDR da Rede +label.network.desc=Descri\u00e7\u00e3o de Rede +label.network.device=Dispositivo de Rede +label.network.device.type=Tipo de Dispositivo de Rede +label.network.domain=Dom\u00ednio de Rede +label.network.domain.text=Texto do dom\ufffdnio de rede +label.network.id=ID de Rede +label.networking.and.security=Rede e seguran\u00e7a +label.network.label.display.for.blank.value=Utilizar gateway default +label.network.limits=Limites de rede +label.network.name=Nome da Rede +label.network.offering.display.text=Network Offering Display Text +label.network.offering.id=Network Offering ID +label.network.offering.name=Network Offering Name +label.network.offering=Network Offering +label.network.rate.megabytes=Taxa de Rede (MB/s) +label.network.rate=Taxa de Transfer\u00eancia +label.network.read=Network Read +label.network=Rede +label.network.service.providers=Provedores de Servi\u00e7os de Rede +label.networks=Redes +label.network.type=Tipo de Rede +label.network.write=Network Write +label.new=Novo +label.new.password=Nova Senha +label.new.project=Novo Projeto +label.new.vm=Nova VM +label.next=Pr\u00f3ximo +label.nexusVswitch=Nexus Vswitch +label.nfs=NFS +label.nfs.server=Servidor NFS +label.nfs.storage=Storage NFS +label.nic.adapter.type=Tipo de adaptador de Rede +label.nicira.controller.address=Endere\u00e7o do Controlador +label.nicira.l3gatewayserviceuuid=Uuid do Servi\u00e7o de Gateway L3 +label.nicira.nvp.details=Detalhes do Nicira NVP +label.nicira.transportzoneuuid=Uuid da Zona de Transporte +label.nics=Adaptadores de Rede +label.no.actions=Sem A\u00e7\u00f5es Dispon\u00edveis +label.no.alerts=Sem Alertas Recentes +label.no.data=Sem dados para mostrar +label.no.errors=Sem Erros Recentes +label.no.grouping=(sem agrupamento) +label.no.isos=Sem ISO Dispon\u00edvel +label.no.items=Sem Itens Dispon\u00edveis +label.none=Nenhum +label.no=N\u00e3o +label.no.security.groups=Sem Security Groups Dispon\u00edveis +label.not.found=N\u00e3o Encontrado +label.no.thanks=N\u00e3o obrigado +label.notifications=Notifica\u00e7\u00f5es +label.number.of.clusters=N\u00famero de Clusters +label.number.of.cpu.sockets=O N\u00famero de Sockets de CPU +label.number.of.hosts=N\u00famero de Hosts +label.number.of.pods=N\u00famero de Pods +label.number.of.system.vms=N\u00famero de VMs de sistema +label.number.of.virtual.routers=N\u00famero de Roteadores Virtuais +label.number.of.zones=N\u00famero de Zonas +label.num.cpu.cores=\# de Core CPU +label.numretries=N\u00famero de Tentativas +label.ocfs2=OCFS2 +label.offer.ha=Offer HA +label.ok=OK +label.opendaylight.controller=Controlador OpenDaylight +label.opendaylight.controllerdetail=Detalhes do Controlador OpenDaylight +label.opendaylight.controllers=Controladores OpenDaylight +label.openDaylight=OpenDaylight +label.operator=Operador +label.optional=Opcional +label.order=Ordenar +label.os.preference=Prefer\u00eancia de SO +label.os.type=Tipo de SO +label.other=Outro +label.override.guest.traffic=Anula Tr\u00e1fego Convidado +label.override.public.traffic=Anula Tr\u00e1fego P\u00fablico +label.ovm.traffic.label=R\u00f3tulo de tr\u00e1fego OVM +label.ovs=OVS +label.owned.public.ips=IP P\u00fablico Utilizado +label.owner.account=Dono da Conta +label.owner.domain=Dono do Dom\u00ednio +label.palo.alto.details=Detalhes do Palo Alto +label.PA.log.profile=Palo Alto Log Profile +label.PA=Palo Alto +label.parent.domain=Dom\u00ednio Principal +label.passive=Passivo +label.password.enabled=Senha Ativada +label.password.lower=senha +label.password.reset.confirm=A senha foi recuperada para +label.password=Senha +label.path=Caminho (Path) +label.PA.threat.profile=Palo Alto Threat Profile +label.perfect.forward.secrecy=Perfect Forward Secrecy +label.persistent=Persistente +label.physical.network.ID=ID da rede f\u00edsica +label.physical.network=Rede F\u00edsica +label.PING.CIFS.password=PING CIFS password +label.PING.CIFS.username=PING CIFS username +label.PING.dir=PING Directory +label.ping.path=Caminho do Ping +label.PING.storage.IP=Disparar PING para IP do Storage +label.planner.mode=Modo planejado +label.please.specify.netscaler.info=Por favor especifique as informa\u00e7\u00f5es do Netscaler +label.please.wait=Por Favor Aguarde +label.plugin.details=Detalhes do plugin +label.plugins=Plugins +label.pod.dedicated=Pod Dedicado +label.pod.name=Nome do Pod +label.pod=POD +label.pods=Pods +label.polling.interval.sec=Intervalo de Polling (em seg) +label.portable.ip=IP Port\u00e1vel +label.portable.ip.range.details=Detalhes de Range de IP Port\u00e1veis +label.portable.ip.ranges=Faixa de endere\u00e7os IPs Port\u00e1vel +label.portable.ips=IPs Port\u00e1veis +label.port.forwarding=Encaminhamento de Porta +label.port.forwarding.policies=Pol\u00edticas de redirecionamento de portas +label.port=Porta +label.port.range=Range de Porta +label.PreSetup=PreSetup +label.previous=Anterior +label.prev=Prev +label.primary.allocated=Aloca\u00e7\u00e3o do Storage Prim\u00e1rio +label.primary.network=Rede Prim\u00e1ria +label.primary.storage.count=Pools de Storage Prim\u00e1rios +label.primary.storage.limits=Limites do Storage Prim\u00e1rio (GiB) +label.primary.storage=Storage Prim\u00e1rio +label.primary.used=Uso do Storage Prim\u00e1rio +label.private.Gateway=Gateway privado +label.private.interface=Interface Privada +label.private.ip=Endere\u00e7o IP Privado +label.private.ip.range=Range de IP Privado +label.private.ips=IPs Privados +label.privatekey=PKCS\#8 Private Key +label.private.network=Rede Privada +label.private.port=Porta Privada +label.private.zone=Zona Privada +label.profile=Perfil +label.project.dashboard=Dashboard do Projeto +label.project.id=ID de Projeto +label.project.invite=Convidar para o projeto +label.project.name=Nome de projeto +label.project.businessservice=Servi\u00e7o de Neg\u00f3cio +label.project.client=Cliente +label.project.component=Componente +label.project.subcomponent=Sub-componente +label.project.product=Produto +label.project.detailedusage=Consumo detalhado +label.project=Projeto +label.projects=Projetos +label.project.view=Vis\u00e3o de Projeto +label.protocol.number=N\u00famero do Protocolo +label.protocol=Protocolo +label.provider=Provedor +label.providers=Providers +label.public.interface=Interface P\u00fablica +label.public.ip=Endere\u00e7o IP P\u00fablico +label.public.ips=IPs P\u00fablicos +label.public.load.balancer.provider=Provedor P\u00fablico de Balanceamento de Carga +label.public.network=Rede P\u00fablica +label.public.port=Porta P\u00fablica +label.public=P\u00fablico +label.public.traffic=Tr\u00e1fego P\u00fablico +label.public.traffic.vswitch.name=Nome do vSwitch de Tr\u00e1fego P\u00fablico +label.public.traffic.vswitch.type=Tipo de vSwitch de Tr\u00e1fego P\u00fablico +label.public.zone=Zona P\u00fablica +label.purpose=Prop\u00f3sito +label.Pxe.server.type=Tipo de Servidor PXE +label.qos.type=Tipo de QoS +label.quickview=Visualiza\u00e7\u00e3o r\u00e1pida +label.quiesce.vm=Quiesce VM +label.quiet.time.sec=Tempo Silencioso (em seg) +label.rbd.id=Usu\u00e1rio Ceph +label.rbd.monitor=Monitor Ceph +label.rbd.pool=Pool Ceph +label.rbd=RDB +label.rbd.secret=Cephx secret +label.reboot=Reiniciar +label.recent.errors=Erros Recentes +label.recover.vm=Recuperar VM +label.redundant.router.capability=Recurso de roteador redundante +label.redundant.router=Roteador Redundantee +label.redundant.state=Estado redundante +label.refresh=Atualizar +label.refresh.blades=Atualizar L\u00e2minas +label.regionlevelvpc=VPC a N\u00edvel de Regi\u00e3o +label.region=Regi\u00e3o +label.reinstall.vm=Reinstalar VM +label.related=Relacionado +label.release.account=Liberar de Conta +label.release.account.lowercase=Liberar de conta +label.release.dedicated.cluster=Libera Cluster Dedicado +label.release.dedicated.host=Libera Host Dedicado +label.release.dedicated.pod=LIberar Pod Dedicado +label.release.dedicated.vlan.range=Liberar range de VLAN dedicado +label.release.dedicated.zone=Liberar Zona Dedicada +label.remind.later=Me lembre depois +label.remove.ACL=Remove ACL +label.remove.egress.rule=Remover regra egress +label.remove.from.load.balancer=Removendo Inst\u00e2ncia do balanceador de carga +label.remove.ingress.rule=Remover regra ingress +label.remove.ip.range=Remover range de IP +label.remove.ldap=Remover LDAP +label.remove.network.offering=Remove oferta de rede +label.remove.pf=Remover regra de redirecionamento de porta +label.remove.project.account=Remover conta de projeto +label.remove.region=Remover Regi\u00e3o +label.remove.rule=Remover regra +label.remove.static.nat.rule=Remover regra de NAT est\u00e1tico +label.remove.static.route=Remover rota est\u00e1tica +label.remove.tier=Remover camada +label.remove.vm.from.lb=Remover VM da regra de balanceador de carga +label.remove.vm.load.balancer=Remover VM do balanceamento de carga +label.remove.vmware.datacenter=Remover Datacenter VMware +label.remove.vpc.offering=Remover oferta VPC +label.remove.vpc=remover a VPC +label.removing=Removendo +label.removing.user=Removendo Usu\u00e1rio +label.reource.id=ID do Recurso +label.replace.acl.list=Substituir Lista ACL +label.replace.acl=Substituir ACL +label.required=Obrigat\u00f3rio +label.requires.upgrade=Requer Atualiza\u00e7\u00e3o +label.reserved.ip.range=Faixa de IP Reservada +label.reserved.system.gateway=Gateway de sistema reservado +label.reserved.system.ip=IP de Sistema Reservado +label.reserved.system.netmask=M\u00e1scara de rede reservada do sistema +label.resetVM=Restabelecer VM +label.reset.VPN.connection=Resetar a conex\u00e3o VPN +label.resize.new.offering.id=New Offering +label.resize.new.size=Novo Tamanho (GB) +label.resize.shrink.ok=Shrink OK +label.resource.limit.exceeded=Limite de Recurso Excedido +label.resource.limits=Limite de Recursos +label.resource.name=Nome do Recurso +label.resource=Recurso +label.resources=Recursos +label.resource.state=Estado do Recurso +label.response.timeout.in.sec=Timeout de Resposta (em seg) +label.restart.network=Reiniciar rede +label.restart.required=Reiniciar obrigat\u00f3rio +label.restart.vpc=reiniciar a VPC +label.restore=Restaurar +label.retry.interval=Intervalo de repeti\u00e7\u00e3o +label.review=Revisar +label.revoke.project.invite=Revogar convite +label.role=Fun\u00e7\u00e3o +label.root.certificate=Certificado Root +label.root.disk.controller=Controlador do disco Root +label.root.disk.offering=Oferta de Disco ROOT +label.root.disk.size=Tamanho do disco root +label.router.vm.scaled.up=VM do Roteador Escalonada +label.routing.host=Host de Roteamento +label.routing=Roteamento +label.rule.number=Regra N\u00famero +label.rules=Regras +label.running.vms=VMs Rodando +label.s3.access_key=Chave de acesso +label.s3.bucket=Balde +label.s3.connection_timeout=Tempo limite de conex\u00e3o +label.s3.endpoint=Ponto de acesso +label.s3.max_error_retry=Limite de tentativas de recupera\u00e7\u00e3o de erro +label.s3.nfs.path=Caminho NFS S3 +label.s3.nfs.server=Servidor NFS S3 +label.s3.secret_key=Chave Secreta +label.s3.socket_timeout=Tempo limite no socket +label.s3.use_https=Use HTTPS +label.saml.enable=Autorizar SAML SSO +label.saml.entity=Provedor de Identidade +label.saturday=S\u00e1bado +label.save.and.continue=Salvar e continuar +label.save=Salvar +label.saving.processing=Salvando.... +label.scale.up.policy=Pol\u00edtica de Escalonamento +label.scope=Escopo +label.search=Pesquisar +label.secondary.isolated.vlan.id=ID de VLAN Secund\u00e1ria Isolada +label.secondary.staging.store=Armazenamento de Est\u00e1gio Secund\u00e1rio +label.secondary.staging.store.details=Detalhes do Armazenamento de Est\u00e1gio Secund\u00e1rio +label.secondary.storage.count=Pools de Storage secund\u00e1rios +label.secondary.storage.details=Detalhes do armazenamento secund\u00e1rio +label.secondary.storage.limits=Limites do Storage Secund\u00e1rio (GiB) +label.secondary.storage=Storage Secund\u00e1rio +label.secondary.storage.vm=VM de storage secund\u00e1rio +label.secondary.used=Uso do Storage Secund\u00c3\u00a1rio +label.secret.key=Chave Secreta +label.security.group.name=Nome do Security Group +label.security.group=Security Group +label.security.groups.enabled=Security Groups Ativado +label.security.groups=Grupos de seguran\u00e7a +label.select.a.template=Selecione um template +label.select.a.zone=Selecione uma zona +label.select.instance=Selecionar inst\u00e2ncia +label.select.instance.to.attach.volume.to=Escolha uma inst\u00e2ncia para conectar o volume +label.select.iso.or.template=Selecione ISO ou template +label.select.offering=Selecionar Oferta +label.select.project=Selecionar Projeto +label.select.region=Selecione Regi\u00e3o +label.select=Selecionar +label.select.template=Seleciona Template +label.select.tier=Selecione camada +label.select-view=Selecionar visualiza\u00e7\u00e3o +label.select.vm.for.static.nat=Selecionar VM para NAT est\u00e1tico +label.sent=Enviado +label.server=Servidor +label.service.capabilities=Recursos de servi\u00e7os +label.service.offering=Plano +label.services=Servi\u00e7os +label.service.state=Estado do Servi\u00e7o +label.session.expired=Sess\u00e3o Expirada +label.set.default.NIC=Configurar para NIC padr\u00e3o +label.settings=Ajustes +label.setup=Configura\u00e7\u00e3o +label.setup.network=Configurar Rede +label.setup.zone=Configurar Zona +label.set.up.zone.type=Configurar tipo de zona +label.shared=Compatilhado +label.SharedMountPoint=SharedMountPoint +label.show.advanced.settings=Mostra ajustes avan\u00e7ados +label.show.ingress.rule=Mostrar Regra de Entrada +label.shutdown.provider=Desabilitar provider +label.site.to.site.VPN=Site-to-site VPN +label.size=Tamanho +label.skip.guide=Eu utilizei o CloudStack antes, pular este guia +label.smb.domain=Dom\u00ednio SMB +label.smb.password=Senha SMB +label.smb.username=Usu\u00e1rio SMB +label.snapshot.limits=Limites de Snapshot +label.snapshot.name=Nome do Snapshot +label.snapshot.schedule=Configure Snapshots recorrentes +label.snapshot=Snapshot +label.snapshot.s=Snapshot(s) +label.snapshots=Snapshots +label.SNMP.community=Comunidade SNMP +label.SNMP.port=Porta SNMP +label.sockets=Sockets +label.source.ip.address=Endere\u00e7o IP de origem +label.source.nat=Source NAT +label.source.nat.supported=SourceNAT Supportado +label.source.port=Porta de origem +label.specify.IP.ranges=Especifique range de IP +label.specify.vlan=Especificar VLAN +label.specify.vxlan=Especificar VXLAN +label.SR.name=SR Name-Label +label.srx.details=Detalhes do SRX +label.srx=SRX +label.start.IP=IP do in\u00edcio +label.start.lb.vm=Iniciar LB VM +label.start.port=Porta de In\u00edcio +label.start.reserved.system.IP=In\u00edcio dos IPs reservados para o sistema +label.start.vlan=VLAN Inicial +label.start.vxlan=VXLAN Inicial +label.state=Estado +label.static.nat.enabled=NAT est\u00e1tico Habilitado +label.static.nat=NAT Est\u00e1tico +label.static.nat.to=NAT Est\u00e1tico para +label.static.nat.vm.details=Detalhes de NAT est\u00e1tico da VM +label.statistics=Estat\u00edsticas +label.status=Estado +label.step.1=Passo 1 +label.step.1.title=Passo 1\: Selecione o Template +label.step.2=Passo 2 +label.step.2.title=Passo 2\: Plano +label.step.3=Passo 3 +label.step.3.title=Passo 3\: Selecione o Disco Adicional +label.step.4=Passo 4 +label.step.4.title=Passo 4\: Rede +label.step.5=Passo 5 +label.step.5.title=Passo 5\: Revisar +label.stickiness=Ader\u00eancia +label.stickiness.method=M\u00e9todo de Ader\u00eancia +label.sticky.cookie-name=Nome do Cookie +label.sticky.domain=Dom\u00ednio +label.sticky.expire=Expires +label.sticky.holdtime=Tempo de espera +label.sticky.indirect=Indireto +label.sticky.length=Tamanho +label.sticky.mode=Modo +label.sticky.name=Nome Permanente +label.sticky.nocache=Sem Cache +label.sticky.postonly=Apenas publicar +label.sticky.prefix=Prefixo +label.sticky.request-learn=Solicitar para aprender +label.sticky.tablesize=Tamanho da Tabela +label.stop.lb.vm=Pare LB VM +label.stop=Parar +label.stopped.vms=VMs Paradas +label.storage.pool=Pool de Armazanamento +label.storage=Storage +label.storage.tags=Tags de Storage +label.storage.traffic=Tr\u00e1fego do Storage +label.storage.type=Tipo de Storage +label.subdomain.access=Acesso ao subdom\u00ednio +label.submit=Enviar +label.submitted.by=[Enviado por\: ] +label.succeeded=Sucedido +label.sunday=Domingo +label.super.cidr.for.guest.networks=Super CIDR para redes h\u00f3spedes +label.supported.services=Servi\u00e7os Suportados +label.supported.source.NAT.type=Tipo de Source NAT Suportado +label.supportsstrechedl2subnet=Suporte \u00e0 Streched L2 Subnet +label.suspend.project=Suspender Projeto +label.switch.type=Tipo de Switch +label.system.capacity=Capacidade do Sistema +label.system.offering.for.router=Oferta do Sistema para Roteador +label.system.offering=Ofertas de Sistema +label.system.service.offering=System Service Offering +label.system.vm.details=Detalhes do System VM +label.system.vm.scaled.up=System VM Escalonada +label.system.vms=VM de Sistemas +label.system.vm.type=Tipo de VM de Sistema +label.system.vm=VM de Sistema +label.system.wide.capacity=Capacidade Total do Sistema +label.tagged=Tagged +label.tag.key=Chave +label.tags=Tags +label.tag.value=Valor +label.target.iqn=Target IQN +label.task.completed=Tarefa completa +label.template.limits=Limites do Template +label.template=Template +label.TFTP.dir=TFTP Directory +label.tftp.root.directory=Diret\u00f3rio raiz do tftp +label.theme.default=Tema Padr\u00e3o +label.theme.grey=Custom - Grey +label.theme.lightblue=Custom - Light Blue +label.threshold=Limiar +label.thursday=Quinta +label.tier=Camada +label.tier.details=Detalhes da camada +label.timeout.in.second = Timeout (segundos) +label.timeout=Timeout +label.time=Time +label.time.zone=Fuso Hor\u00e1rio +label.timezone=Fuso Hor\u00e1rio +label.token=Token +label.total.cpu=CPU TOTAL +label.total.CPU=CPU TOTAL +label.total.hosts=Total de Hosts +label.total.memory=Total de Mem\u00f3ria +label.total.of.ip=Total de Endere\u00e7os IPs +label.total.of.vm=Total VMs +label.total.storage=Totam de Storage +label.total.virtual.routers=Total de Roteadores Virtuais +label.total.virtual.routers.upgrade=Total de Roteadores Virtuais que requerem atualiza\u00e7\u00e3o +label.total.vms=Total VMs +label.traffic.label=Etiqueta de tr\u00e1fego +label.traffic.types=Tipos de Tr\u00e1fego +label.traffic.type=Tipo de Tr\u00e1fego +label.tuesday=Ter\u00e7a +label.type.id=Tipo do ID +label.type.lower=tipo +label.type=Tipo +label.ucs=UCS +label.unavailable=Indispon\u00edvel +label.unhealthy.threshold=Limiar de Insalubridade +label.unlimited=Ilimitado +label.untagged=N\u00e3o Marcado +label.update.project.resources=Atualizar recursos de projeto +label.update.ssl= Atualizar Certificado SSL +label.update.ssl.cert= Atualizar Certificado SSL +label.updating=Atualizando +label.upgrade.required=Atualiza\u00e7\u00e3o \u00e9 necess\u00e1ria +label.upgrade.router.newer.template=Atualize Roteador Para Usar Template Mais Novo +label.upload=Enviar +label.upload.volume=Enviar o Volume +label.url=URL +label.usage.interface=Usage Interface +label.usage.sanity.result=Resultado de Sanidade de Uso +label.usage.server=Uso do Servidor +label.used=Usado +label.user.data=Dados de Usu\u00e1rio +label.username.lower=nome do usu\u00e1rio +label.username=Nome de usu\u00e1rio +label.users=Usu\u00e1rios +label.user=Usu\u00e1rio +label.user.vm=VM do Usu\u00e1rio +label.use.vm.ips=Usa IPs da VM +label.use.vm.ip=Usar IP da VM\: +label.value=Valor +label.vcdcname=Nome do vCenter DC +label.vcenter.cluster=vCenter Cluster +label.vcenter.datacenter=vCenter Datacenter +label.vcenter.datastore=vCenter Datastore +label.vcenter.host=vCenter Host +label.vcenter.password=vCenter Password +label.vcenter.username=vCenter Username +label.vcenter=vcenter +label.vcipaddress=Endere\u00e7o IP do vCenter +label.version=Vers\u00e3o +label.vgpu.max.resolution=Resulo\u00e7\u00e3o max +label.vgpu.max.vgpu.per.gpu=vGPU por GPU +label.vgpu.remaining.capacity=Capacidade restante +label.vgpu.type=Tipo de vGPU +label.vgpu=VGPU +label.vgpu.video.ram=RAM de v\u00eddeo +label.view.all=Visualizar tudo +label.view.console=Visualizar Console +label.viewing=Visualizar +label.view.more=Ver mais +label.view.secondary.ips=Visualizar os IPs secund\u00e1rios +label.view=Visualizar +label.virtual.appliance=Appliance Virtual +label.virtual.appliance.details=Detalhes de appliance virtual +label.virtual.appliances=Appliance Virtual +label.virtual.machines=M\u00e1quinas virtuais +label.virtual.networking=Rede Virtual +label.virtual.network=Rede Virtual +label.virtual.router=Roteador Virtual +label.virtual.routers.group.account=Grupo de Roteadores Virtuais por conta +label.virtual.routers.group.cluster=Grupo de Roteadores Virtuais por cluster +label.virtual.routers.group.pod=Grupo de Roteadores Virtuais por pod +label.virtual.routers.group.zone=Grupo de Roteadores Virtuais por Zona +label.virtual.routers=Roteadores Virtuais +label.vlan.id=VLAN ID +label.vlan.only=VLAN +label.vlan.range.details=Detalhes de range VLAN +label.vlan.range=Intervalo de VLAN +label.vlan.ranges=Range(s) de VLAN +label.vlan=VLAN +label.vlan.vni.range=Intervalo de VLAN +label.vlan.vni.ranges=Range(s) de VLAN/VNI +label.vm.add=Adicionar Inst\u00e2ncia +label.vm.destroy=Apagar +label.vm.display.name=Nome de exibi\u00e7\u00e3o da VM +label.VMFS.datastore=VMFS datastore +label.vmfs=VMFS +label.vm.id=ID da VM +label.vm.ip=Endere\u00e7o IP da VM +label.vm.name=Nome da VM +label.vm.password=Senha para a VM \u00e9 +label.vm.reboot=Reiniciar +label.VMs.in.tier=M\u00e1quinas virtuais em camadas +label.vmsnapshot.current=isCurrent +label.vmsnapshot.memory=Snapshot da mem\u00f3ria +label.vmsnapshot.parentname=Pai +label.vmsnapshot=Snapshot da VM +label.vmsnapshot.type=Tipo +label.vm.start=In\u00edcio +label.vm.state=Estado da VM +label.vm.stop=Parar +label.vms=VMs +label.vmware.datacenter.id=ID do datacenter VMware +label.vmware.datacenter.name=Nome do datacenter VMware +label.vmware.datacenter.vcenter=Vcednter do datacenter VMware +label.vmware.traffic.label=Etiqueta de tr\u00e1fego VMware +label.vnet.id=VLAN ID +label.vnet=VLAN +label.vnmc.devices=Dispositivos VNMC +label.vnmc=VNMC +label.volatile=Vol\u00e1til +label.volgroup=Grupo de Volume +label.volume.details=Detalhe do volume +label.volume=Disco +label.volume.limits=Limites de Disco +label.volume.migrated=Volume migrado +label.volume.name=Nome do Disco +label.volumes=Discos +label.vpc.distributedvpcrouter=Roteador VPC Distribuido +label.vpc.id=VPC ID +label.VPC.limits=Limites VPC +label.vpc.offering.details=Detalhes da oferta VPC +label.vpc.offering=Oferta VPC +label.VPC.router.details=Detalhes de roteador de VPC +label.vpc.supportsregionlevelvpc=Suporta VPC em N\u00edvel de Regi\u00e3o +label.vpc.virtual.router=Roteador Virtual VPC +label.vpc=VPC +label.VPN.connection=Conex\u00e3o VPN +label.vpn.customer.gateway=Gateway de VPN de usu\u00e1rio +label.VPN.customer.gateway=Gateway de VPN de usu\u00e1rio +label.VPN.gateway=Gateway de VPN +label.vpn=VPN +label.vsmctrlvlanid=Control VLAN ID +label.vsmpktvlanid=Packet VLAN ID +label.vsmstoragevlanid=Storage VLAN ID +label.vsphere.managed=vSphere Managed +label.vswitch.name=Nome do vSwitch +label.vSwitch.type=Tipo do vSwitch +label.vxlan.id=VXLAN ID +label.vxlan.range=Intervalo de VXLAN +label.vxlan=VXLAN +label.waiting=Aguardando +label.warn=Avisar +label.warn.upper=AVISO +label.wednesday=Quarta-Feira +label.weekly=Semanal +label.welcome=Bem-Vindo +label.welcome.cloud.console=Painel de Controle +label.what.is.cloudstack=O que \u00e9 o CloudStack&\#8482? +label.xenserver.tools.version.61.plus=Vers\u00e3o original do XS \u00e9 6.1\\+ +label.Xenserver.Tools.Version61plus=Vers\u00e3o original do XS \u00e9 6.1\\+ +label.xenserver.traffic.label=Etiqueta de tr\u00e1fego XenServer +label.yes=Sim +label.zone.dedicated=Zona Dedicada +label.zone.details=Detalhes de zona +label.zone.id=ID da Zona +label.zone.lower=Zona +label.zone.name=Nome da zona +label.zone.step.1.title=Passo 1\: Selecionar a Rede +label.zone.step.2.title=Passo 2\: Adicionar a Zona +label.zone.step.3.title=Passo 3\: Adicionar o POD +label.zone.step.4.title=Passo 4\: Adicionar um Intervalo de IP +label.zones=Zonas +label.zone.type=Tipo de Zona +label.zone.wide=Zone-Wide +label.zoneWizard.trafficType.guest=H\u00f3spede\: tr\u00e1fego entre m\u00e1quinas virtuais de usu\u00e1rios finais +label.zoneWizard.trafficType.management=Ger\u00eancia\: Tr\u00e1fego entre recursos internos do CloudStack incluindo quaisquer componentes que se comunicam com o servidor de gerenciamento tais como hosts e m\u00e1quinas virtuais de sistema do CloudStack +label.zoneWizard.trafficType.public=P\u00fablico\: tr\u00e1fego entre a internet e m\u00e1quinas virtuais na nuvem. +label.zoneWizard.trafficType.storage=Storage\: tr\u00e1fego entre servidores de storage prim\u00e1ria e secund\u00e1ria, tais como templates de m\u00e1quinas virtuais e snapshots +label.zone=Zona +managed.state=Status do Gerenciamento +message.acquire.ip.nic=Por favor, confirme que voc\u00ea deseja adquirir um novo IP secund\u00e1rio para esta Interface de Rede.
NOTA\: Voc\u00ea precisa configurar manualmente o novo IP secund\u00e1rio dentro da maquinas virtual. +message.acquire.new.ip=Por favor confirme que voc\u00ea gostaria de adquirir um novo IP para esta rede. +message.acquire.new.ip.vpc=Por favor confirme que voc\u00ea gostaria de adquirir um novo IP para esta VPC. +message.acquire.public.ip=Selecione a zona de onde voc\u00ea deseja adquirir o novo IP +message.action.cancel.maintenance=A Manuten\u00e7\u00e3o do seu HOST foi cancelada com sucesso +message.action.cancel.maintenance.mode=Confirme que voc\u00ea deseja cancelar esta Manuten\u00e7\u00e3o +message.action.change.service.warning.for.instance=Sua inst\u00e2ncia deve ser desligada antes de tentar alterar a oferta de servi\u00e7os utilizada. +message.action.change.service.warning.for.router=O roteador precisa ser desligado antes de trocar o plano/tamanho. +message.action.delete.cluster=Confirme que voc\u00ea deseja excluir este HOST +message.action.delete.disk.offering=Confirme que voc\u00ea deseja excluir esta oferta de disco +message.action.delete.domain=Confirme que voc\u00ea deseja excluir este Dom\u00ednio +message.action.delete.external.firewall=Confirme que voc\u00ea gostaria de remover este Firewall externo. Aviso\: Se voc\u00ea Est\u00e1 planejando adicionar novamente este mesmo Firewall, \u00e9 necess\u00e1rio apagar os contadores do dispositivo. +message.action.delete.external.load.balancer=Confirme que voc\u00ea gostaria de remover este Load Balancer Externo. Aviso\: Se voc\u00ea Est\u00e1 planejando adicionar novamente este mesmo Load Balancer, \u00e9 necess\u00e1rio apagar os contadores do dispositivo. +message.action.delete.ingress.rule=Confirme que voc\u00ea deseja excluir esta regra de entrada. +message.action.delete.ISO=Confirme que voc\u00ea deseja excluir esta ISO +message.action.delete.ISO.for.all.zones=Esta ISO \u00e9 usada por todas as Zonas. Confirme se voc\u00ea deseja excluir a ISO de todas as Zonas +message.action.delete.network=Confirme que voc\u00ea deseja remover esta rede. +message.action.delete.nexusVswitch=Por favor confirme que voc\ufffd deseja remover este nexusVswitch. +message.action.delete.nic=Por favor, confirme que deseja remover esta Interface de Rede, esta a\u00e7\u00e3o tamb\u00e9m ir\u00e1 remover a rede associada \u00e0 VM. +message.action.delete.physical.network=Por favor confirme que voc\u00ea deseja deletar esta rede f\u00edsica +message.action.delete.pod=Confirme que voc\u00ea deseja remover este POD. +message.action.delete.primary.storage=Confirme que voc\u00ea deseja remover este Storage Prim\u00e1rio. +message.action.delete.secondary.storage=Confirme que voc\u00ea deseja remover este Storage Secund\u00e1rio. +message.action.delete.security.group=Confirme que voc\u00ea deseja remover este Security Group. +message.action.delete.service.offering=Confirme que voc\u00ea deseja remover este Plano. +message.action.delete.snapshot=Confirme que voc\u00ea deseja remover este Snapshot. +message.action.delete.system.service.offering=Por favor confirme que voc\u00ea deseja deletar esta oferta de servi\u00e7o de sistema. +message.action.delete.template=Confirme que voc\u00ea deseja remover este Template. +message.action.delete.template.for.all.zones=Este Template \u00e9 usado por todas as zonas. Confirme que voc\u00ea deseja remover o Template de todas as zonas. +message.action.delete.volume=Confirme que voc\u00ea deseja remover este Disco. +message.action.delete.zone=Confirme que voc\u00ea deseja remover esta Zona. +message.action.destroy.instance=Por favor, confirme que voc\u00ea deseja excluir esta Inst\u00e2ncia. +message.action.destroy.systemvm=Confirme que voc\u00ea deseja excluir esta VM de Sistema. +message.action.disable.cluster=Confirma a desativa\u00e7\u00e3o do cluster. +message.action.disable.nexusVswitch=Por favor confirme que voc\ufffd deseja desabilitar este nexusVswitch +message.action.disable.physical.network=Por favor confirme que voc\u00ea deseja desabilitar esta rede f\u00edsica. +message.action.disable.pod=Confirma a desativa\u00e7\u00e3o do POD. +message.action.disable.static.NAT=Confirme que voc\u00ea deseja desativar o NAT Est\u00e1tico. +message.action.disable.zone=Confirma a desativa\u00e7\u00e3o da zona. +message.action.downloading.template=Baixando template +message.action.download.iso=Por favor confirme que voc\u00ea deseja baixar esta ISO. +message.action.download.template=Por favor confirme que voc\u00ea deseja baixar este template. +message.action.enable.cluster=Confirma a ativa\u00e7\u00e3o do cluster. +message.action.enable.maintenance=O Host foi preparado com sucesso para Manuten\u00e7\u00e3o. Este processo poder\u00e1 levar alguns minutos ou mais dependendo do n\u00famero de VMs hospedadas neste Host. +message.action.enable.nexusVswitch=Por favor confirme que voc\ufffd deseja habilitar este nexusVswitch. +message.action.enable.physical.network=Por favor confirme que voc\u00ea deseja habilitar esta rede f\u00edsica. +message.action.enable.pod=Confirma a ativa\u00e7\u00e3o do POD. +message.action.enable.zone=Confirma a ativa\u00e7\u00e3o da zona. +message.action.expunge.instance=Por favor, confirme que voc\u00ea deseja eliminar esta inst\u00e2ncia. +message.action.force.reconnect=O procedimento de reconex\u00e3o for\u00e7ada foi preparado com sucesso. Este processo poder\u00e1 levar alguns minutos. +message.action.host.enable.maintenance.mode=Ativar o modo de Manuten\u00e7\u00e3o ir\u00e1 causar o live migration de todas as Inst\u00e2ncias hospedadas neste Host para o pr\u00f3ximo dispon\u00edvel. +message.action.instance.reset.password=Por favor confirme que voc\u00ea deseja alterar a senha de ROOT para est\u00e1 m\u00e1quina virtual. +message.action.manage.cluster=Confirma a vincula\u00e7\u00e3o do cluster. +message.action.primarystorage.enable.maintenance.mode=Aviso\: Colocar o Storage prim\u00e1rio em modo de Manuten\u00e7\u00e3o ir\u00e1 causar a parada de todas as VMs hospedadas nesta unidade. Deseja continuar? +message.action.reboot.instance=Por favor, confirme que voc\u00ea deseja reiniciar esta inst\u00e2ncia. +message.action.reboot.router=Confirme que voc\ufffd deseja reiniciar este roteador. +message.action.reboot.systemvm=Confirme que voc\u00ea deseja reiniciar esta VM de sistema. +message.action.release.ip=Confirme que voc\u00ea deseja liberar este IP. +message.action.remove.host=Favor confirmar que voc\u00ea deseja remover este host. +message.action.reset.password.off=Sua Inst\u00e2ncia n\u00e3o suporta esta funcionalidade. +message.action.reset.password.warning=Para recuperar a senha \u00e9 necess\u00e1rio parar a Inst\u00e2ncia. +message.action.restore.instance=Por favor, confirme que voc\u00ea deseja restaurar esta Inst\u00e2ncia. +message.action.revert.snapshot=Por favor, confirme que voc\u00ea deseja reverter o seu volume deste snapshot. +message.action.start.instance=Por favor, confirme que voc\u00ea deseja iniciar esta Inst\u00e2ncia. +message.action.start.router=Confirme que voc\u00ea deseja inciar este roteador. +message.action.start.systemvm=Confirme que voc\u00ea deseja iniciar esta VM de sistema. +message.action.stop.instance=Por favor, confirme que voc\u00ea deseja parar esta inst\u00e2ncia. +message.action.stop.router=Confirme que voc\ufffd deseja parar este roteador. +message.action.stop.systemvm=Confirme que voc\u00ea deseja parar esta VM de Sistema. +message.action.take.snapshot=Por favor confirme que voc\u00ea deseja criar um snapshot deste volume. +message.action.unmanage.cluster=Confirma a desvincula\u00e7\u00e3o do cluster. +message.action.vmsnapshot.delete=Por favor, confirme que voc\u00ea deseja excluir este snapshot da VM. +message.action.vmsnapshot.revert=Reverter snapshot da VM +message.activate.project=Voc\u00ea tem certeza que deseja ativar este projeto ? +message.add.cluster=Add a hypervisor managed cluster for zone , pod +message.add.cluster.zone=Add a hypervisor managed cluster for zone +message.add.disk.offering=Especifique o seguintes par\u00e2metros para adicionar uma nova oferta de disco. +message.add.domain=Por favor especifique o subdom\u00ednio que voc\u00ea deseja criar neste dom\u00ednio +message.add.firewall=Adicionar Firewall \u00e0\u00a0 zona. +message.add.guest.network=Por favor confirme que voc\u00ea gostaria de adicionar uma rede guest. +message.add.host=Especifique os seguintes par\u00e2metros para adicionar um novo host. +message.adding.host=Adicionando host +message.adding.Netscaler.device=Adicionando dispositivo Nescaler +message.adding.Netscaler.provider=Adicionando Netscaler provider +message.add.ip.range=Add an IP range to public network in zone +message.add.ip.range.direct.network=Add an IP range to direct network in zone +message.add.ip.range.to.pod=

Add an IP range to pod\:

+message.additional.networks.desc=Por favor, selecione a(s) rede(s) adicionais que sua inst\u00e2ncia virtual estar\u00e1 conectada. +message.add.load.balancer=Add a load balancer to zone +message.add.load.balancer.under.ip=A regra do balanceador de carga foi adicionada para o IP\: +message.add.network=Add a new network for zone\: +message.add.new.gateway.to.vpc=Favor especificar a informa\u00e7\u00e3o para adicionar um novo gateway a esta VPC. +message.add.pod=Add a new pod for zone +message.add.pod.during.zone.creation=Cada zona deve conter um ou mais pods e iremos adicionar o primeiro pod agora. Um pod cont\u00e9m hosts e servidores de storage prim\u00e1rio que ser\u00e3o adicionados em uma etapa posterior. Inicialmente, configure um intervalo de endere\u00e7os IP reservados para o tr\u00e1fego de gerenciamento interno do CloudStack. A faixa de IP reservados devem ser \u00fanicos para cada zona na nuvem. +message.add.primary=Especifique os seguintes par\u00e2metros para adicionar um novo Storage prim\u00e1rio. +message.add.primary.storage=Adicionar novo Storage prim\u00e1rio \u00c3\u00a0 zona , pod +message.add.region=Por favor, especifique as informa\u00e7\u00f5es necess\u00e1rias para adicionar uma nova regi\u00e3o. +message.add.secondary.storage=Add a new storage for zone +message.add.service.offering=Por favor preencha os dados abaixo para adicionar uma nova oferta de computa\u00e7\u00e3o. +message.add.system.service.offering=Por favor preencha os dados abaixo para adicionar uma nova oferta de servi\u00e7o de sistema. +message.add.template=Entre com os dados para criar um novo template. +message.add.volume=Entre com os dados para criar um novo disco. +message.add.VPN.gateway=Favor confirmar que voc\u00ea deseja adicionar um gateway de VPN +message.admin.guide.read=Para VMs baseadas em VMware, por favor leia a sess\u00e3o sobre escalonamento din\u00e2mico no guia do administrador antes de escalonar. Voc\u00ea gostaria de continuar? +message.advanced.mode.desc=Escolhe este modelo de rede se deseja ter habilitar o suporte a VLAN. Este modelo permite maior flexibilidade ao administrador ao permitir ofertas de rede customizada, firewall, vpn ou load balancer bem como acesso via rede virtual ou acesso direto. +message.advanced.security.group=Escolha esta op\u00e7\u00e3o se desejar utilizar Security Groups para isolamento das VMs guest. +message.advanced.virtual=Escolha esta op\u00e7\u00e3o se desejar utilizar VLANs para isolamento das VMs guest. +message.after.enable.s3=Storage secund\u00e1ria fornecida por S3 configurada. Nota\: ao deixar esta p\u00e1gina, voc\u00ea n\u00e3o ser\u00e1 capaz de reconfigurar S3 novamente. +message.after.enable.swift=Swift Configurado. Nota\: Ap\u00f3s deixar esta p\u00e1gina, voc\u00ea n\u00e3o ser\u00e1 capaz de reconfigurar o Swift novamente. +message.alert.state.detected=Alerta de estado detectado +message.allow.vpn.access=Entre com nome de Usu\u00e1rio e senha do Usu\u00e1rio que ter\u00e1 acesso VPN. +message.apply.snapshot.policy=Voc\u00ea atualizou com sucesso sua pol\u00edtica de Snapshot. +message.attach.iso.confirm=Por favor, confirme que voc\u00ea deseja conectar o ISO \u00e0 esta inst\u00e2ncia virtual. +message.attach.volume=Preencha os seguintes dados para conectar o novo disco. Se voc\u00ea Est\u00e1 conectando um disco a uma maquina virtual Windows, ser\u00e1 necess\u00e1rio reiniciar a Inst\u00e2ncia para visualizar o novo disco. +message.basic.mode.desc=Escolha este modelo de rede se voc\u00ea *n\u00e3o* quer suporte a VLAN. Toda Inst\u00e2ncia criada neste modelo de rede estar\u00e1 ligado diretamente a um IP da rede e ser\u00e1 usado Security Groups para prover seguran\u00e7a e segrega\u00e7\u00e3o. +message.change.offering.confirm=Por favor, confirme que voc\u00ea deseja mudar a oferta de servi\u00e7o desta inst\u00e2ncia virtual. +message.change.password=Por favor, troque sua senha. +message.cluster.dedicated=Cluster Dedicado +message.cluster.dedication.released=Cluster dedicado liberado +message.configure.all.traffic.types=Voc\u00ea tem m\u00faltiplas redes f\u00edsicas; favor configurar etiquetas para cada tipo de tr\u00e1fego clicando no bot\u00e3o Edit. +message.configure.ldap=Por favor, confirme que voc\u00ea deseja configurar o LDAP. +message.configuring.guest.traffic=Configurando tr\u00e1fego do guest +message.configuring.physical.networks=Configurando redes f\u00edsicas +message.configuring.public.traffic=Configurando tr\u00e1fego p\u00fablico +message.configuring.storage.traffic=Configurando tr\u00e1fego de storage +message.confirm.action.force.reconnect=Por favor confirme que voc\u00ea deseja for\u00e7ar a reconex\u00e3o com este host. +message.confirm.add.vnmc.provider=Por favor confirme que voc\u00ea gostaria de adicionar este provedor VNMC. +message.confirm.archive.alert=Por favor confirme que voc\u00ea deseja arquivar este alerta. +message.confirm.archive.event=Por favor confirme que voc\u00ea deseja arquivar este evento +message.confirm.archive.selected.alerts=Por favor confirme que voc\u00ea deseja arquivar os alertas selecionados +message.confirm.archive.selected.events=Por favor confirme que voc\u00ea deseja arquivar os eventos selecionados +message.confirm.attach.disk=Voc\u00ea tem certeza que deseja conectar este disco? +message.confirm.create.volume=Voc\u00ea tem certeza que deseja criar o volume? +message.confirm.current.guest.CIDR.unchanged=Gostaria de manter o CIDR da rede convidado inalterado? +message.confirm.dedicate.cluster.domain.account=Voc\u00ea realmente quer dedicar este cluster ao dom\u00ednio/conta? +message.confirm.dedicate.host.domain.account=Voc\u00ea realmente quer dedicar este host ao dom\u00ednio/conta? +message.confirm.dedicate.pod.domain.account=Voc\u00ea realmente quer dedicar este pod ao dom\u00ednio/conta? +message.confirm.dedicate.zone=Voc\u00ea realmente quer dedicar esta zona ao dom\u00ednio/conta? +message.confirm.delete.acl.list=Voc\u00ea tem certeza que deseja apagar esta lista ACL? +message.confirm.delete.alert=Voc\u00ea tem certeza que deseja apagar este alerta? +message.confirm.delete.BrocadeVcs=Por favor confirme que voc\u00ea deseja remover o Brocade Vcs Switch +message.confirm.delete.ciscoASA1000v=Favor confirmar que voc\u00ea deseja apagar este CiscoASA1000v +message.confirm.delete.ciscovnmc.resource=Por favor confirme que voc\u00ea deseja apagar este recurso CiscoVNMC +message.confirm.delete.F5=Por favor confirme que voc\u00ea deseja remover o F5 +message.confirm.delete.internal.lb=Por favor confirme que voc\u00ea deseja remover este LB interno +message.confirm.delete.NetScaler=Por favor confirme que voc\u00ea deseja remover o NetScaler +message.confirm.delete.NuageVsp=Por favor confirme que voc\u00ea deseja remover o Nuage Virtualized Services Directory +message.confirm.delete.PA=Por favor, confirme que voc\u00ea deseja remover Palo Alto +message.confirm.delete.secondary.staging.store=Por favor confirme que deseja apagar Armazenamento de Est\u00e1gio Secund\u00e1rio +message.confirm.delete.SRX=Por favor confirme que voc\u00ea deseja remover o SRX +message.confirm.delete.ucs.manager=Confirme se voc\u00ea deseja excluir o Gerente UCS. +message.confirm.destroy.router=Por favor confirme que voc\u00ea gostaria de destruir este roteador +message.confirm.disable.host=Favor confirmar que voc\u00ea deseja desabilitar este host. +message.confirm.disable.network.offering=Voc\u00ea tem certeza que deseja desabilitar esta oferta de rede? +message.confirm.disable.provider=Por favor confirme que voc\u00ea gostaria de desabilitar este provider +message.confirm.disable.vnmc.provider=Por favor confirme que voc\u00ea gostaria de desabilitar este provedor VNMC. +message.confirm.disable.vpc.offering=Voc\u00ea tem certeza que deseja desabilitar esta oferta de VPC? +message.confirm.enable.host=Por favor confirme que voc\u00ea deseja habilitar este host. +message.confirm.enable.network.offering=Voc\u00ea tem certeza que deseja habilitar esta oferta de rede? +message.confirm.enable.provider=Por favor confirme que voc\u00ea gostaria de habilitar este provider +message.confirm.enable.vnmc.provider=Por favor confirme que voc\u00ea gostaria de habilitar este provedor VNMC. +message.confirm.enable.vpc.offering=Voc\u00ea tem certeza que deseja habilitar esta oferta de VPC? +message.confirm.join.project=Por favor confirme que voc\u00ea deseja entrar neste projeto +message.confirm.migrate.volume=Voc\u00ea quer migrar este volume? +message.confirm.refresh.blades=Por favor confirme que voc\u00ea deseja renovar as l\u00e2minas. +message.confirm.release.dedicated.cluster=Voc\u00ea deseja liberar este cluster dedicado? +message.confirm.release.dedicated.host=Voc\u00ea deseja liberar esta host dedicado? +message.confirm.release.dedicated.pod=Voc\u00ea deseja liberar esta pod dedicado? +message.confirm.release.dedicated.zone=Voc\u00ea deseja liberar esta zona dedicada? +message.confirm.release.dedicate.vlan.range=Confirme que voc\u00ea deseja liberar esta faixa de VLAN dedicada. +message.confirm.remove.event=Voc\u00ea tem certeza que deseja remover este evento? +message.confirm.remove.IP.range=Por favor confirme que voc\u00ea deseja remover este range de IP. +message.confirm.remove.load.balancer=Por favor, confirme que voc\u00ea quer remover a VM do Balanceador de Carga +message.confirm.remove.network.offering=Voc\u00ea tem certeza que deseja remover esta oferta de rede? +message.confirm.remove.selected.alerts=Por favor confirme que voc\u00ea deseja remover os alertas selecionados +message.confirm.remove.selected.events=Por favor confirme que voc\u00ea deseja remover os eventos selecionados +message.confirm.remove.vmware.datacenter=Por favor, confirme que voc\u00ea quer remover este VMware datacenter +message.confirm.remove.vpc.offering=Voc\u00ea tem certeza que deseja remover esta oferta de VPC? +message.confirm.replace.acl.new.one=Voc\u00ea deseja substituir a ACL com uma nova? +message.confirm.scale.up.router.vm=Voc\u00ea realmente quer escalonar a VM do Roteador? +message.confirm.scale.up.system.vm=Voc\u00ea realmente quer escalonar a VM do sistema? +message.confirm.shutdown.provider=Por favor confirme que voc\u00ea deseja desligar este provider +message.confirm.start.lb.vm=Confirme que voc\u00ea deseja iniciar esta LB VM +message.confirm.stop.lb.vm=Confirme que voc\u00ea deseja parar esta LB VM +message.confirm.upgrade.router.newer.template=Por favor confirme que voc\u00ea deseja atualizar o roteador para usar template mais novo. +message.confirm.upgrade.routers.account.newtemplate=Por favor confirme que voc\u00ea deseja atualizar todos os roteadores desta conta para o template mais novo. +message.confirm.upgrade.routers.cluster.newtemplate=Por favor confirme que voc\u00ea deseja atualizar todos os roteadores deste cluster para o template mais novo. +message.confirm.upgrade.routers.newtemplate=Por favor confirme que voc\u00ea deseja atualizar todos os roteadores desta zona para o template mais novo. +message.confirm.upgrade.routers.pod.newtemplate=Por favor confirme que voc\u00ea deseja atualizar todos os roteadores neste pod para o template mais novo. +message.copy.iso.confirm=Confirme se voc\u00ea deseja copiar a ISO para +message.copy.template.confirm=Voc\u00ea tem certeza que deseja copiar o template ? +message.copy.template=Copiar template XXX da zona para +message.create.template.vm=Criar VM do template +message.create.template=Voc\u00ea tem certeza que deseja criar um template ? +message.create.template.volume=Especifique as seguintes informa\u00e7\u00f5es antes de criar o template a partir do disco\: . A cria\u00e7\u00e3o de um template a partir de um disco pode levar alguns minutos ou mais dependendo do tamnho do disco. +message.creating.cluster=Criando cluster +message.creating.guest.network=Criando rede guest +message.creating.physical.networks=Criando redes fisicas +message.creating.pod=Criando pod +message.creating.primary.storage=Criando storage prim\u00e1rio +message.creating.secondary.storage=Criando storage secund\u00e1rio +message.creating.systemVM=Criando VMs do sistema (isso pode levar algum tempo) +message.creating.zone=Criando zona. +message.decline.invitation=Voc\u00ea tem certeza que quer rejeitar este convite de projeto ? +message.dedicated.zone.released=Zona dedicada liberada +message.dedicate.zone=Zona dedicada +message.delete.account=Confirme se voc\u00ea deseja excluir esta conta. +message.delete.affinity.group=Por favor, confirme que voc\u00ea deseja remover este grupo de afinidade +message.delete.gateway=Favor confirmar que voc\u00ea deseja deleta o gateway +message.delete.project=Voc\u00ea tem certeza que deseja deletar este projeto ? +message.delete.user=Por favor confirme que voc\u00ea deseja deletar este usu\u00e1rio. +message.delete.VPN.connection=Favor confirmar que voc\u00ea deseja deletar esta conex\u00e3o VPN +message.delete.VPN.customer.gateway=Favor confirmar que voc\u00ea deseja deletar este gateway de VPN de usu\u00e1rio +message.delete.VPN.gateway=Favor confirmar que voc\u00ea deseja deletar este gateway de VPN +message.desc.advanced.zone=Para topologias de rede mais sofisticadas. Este modelo fornece maior flexibilidade na defini\u00e7\u00e3o de redes de clientes e fornece ofertas de rede personalizadas, tais como firewall, VPN ou de balanceamento de carga. +message.desc.basic.zone=Fornece uma \u00fanica rede onde em cada inst\u00e2ncia de VM \u00e9 atribu\u00eddo um IP diretamente na rede. O isolamento Guest podem ser fornecidos atrav\u00e9s de camada-3 da rede com grupos de seguran\u00e7a (filtragem da fonte de endere\u00e7os IP). +message.desc.cluster=Cada pod deve conter um ou mais clusters, e iremos adicionar o primeiro cluster agora. Um cluster fornece uma maneira de agrupamento de hosts. Os hosts de um cluster t\u00eam hardware id\u00eantico, executam o mesmo hypervisor, est\u00e3o na mesma sub-rede e acessam o mesmo storage compartilhado. Cada cluster \u00e9 constitu\u00eddo por um ou mais hosts e um ou mais servidores de storage prim\u00e1rio. +message.desc.host=Cada cluster deve conter pelo menos um host (computador) para as VMs guest serem executadas e iremos adicionar o primeira host agora. Para um host funcionar no CloudStack, voc\u00ea deve instalar um hypervisor no host, atribuir um endere\u00e7o IP e garantir que o host est\u00e1 conectado ao servidor de gerenciamento do CloudStack.

Forne\u00e7a o hostname ou o endere\u00e7o IP do host, o nome de usu\u00e1rio (geralmente root) e a senha e qualquer label que voc\u00ea utiliza para categorizar os hosts. +message.desc.primary.storage=Cada cluster deve conter um ou mais servidores de storage prim\u00e1rio e iremos adicionar o primeiro agora. Um storage prim\u00e1rio, cont\u00e9m os volumes de disco para todas as VMs em execu\u00e7\u00e3o nos hosts do cluster. Utiliza qualquer protocolo compat\u00edvel com os padr\u00f5es que \u00e9 suportado pelo hypervisor utilizado. +message.desc.secondary.storage=Cada zona deve ter pelo menos um NFS ou servidor de storage secund\u00e1rio e iremos adicionar o primeiro agora. Um storage secund\u00e1rios armazena templates de VM, imagens ISO e snapshots do volume de disco da VM. Esse servidor deve estar dispon\u00edvel para todos os hosts na zona.

Fornecer o endere\u00e7o IP e o caminho exportados. +message.desc.zone=Uma zona \u00e9 a maior unidade organizacional no CloudStack e normalmente corresponde \u00e0 um \u00fanico datacenter. As Zonas disponibilizam isolamento f\u00edsico e redund\u00e2ncia. Uma zona \u00e9 composta por um ou mais pods (cada um dos quais cont\u00e9m os hosts e servidores de storage prim\u00e1rio) e um servidor de storage secund\u00e1rio que \u00e9 compartilhado por todos os pods na zona. +message.detach.disk=Voc\u00ea tem certeza que deseja desconectar este disco ? +message.detach.iso.confirm=Confirme se voc\u00ea deseja desconectar o ISO da inst\u00e2ncia virtual. +message.disable.account=Por favor confirme que voc\u00ea deseja desabilitar esta conta. Ap\u00f3s desabilitar uma conta, todos os usu\u00e1rios desta conta n\u00e3o ir\u00e3o possuir mais acesso aos seus recursos da cloud. Todas as m\u00e1quinas virtuais ser\u00e3o automaticamente desligadas. +message.disable.snapshot.policy=Voc\u00ea desativou com sucesso sua pol\u00edtica de Snapshot. +message.disable.user=Por favor confirme que voc\u00ea deseja desabilitar este usu\u00e1rio. +message.disable.vpn.access=Confirme se voc\u00ea deseja desativar o acesso VPN. +message.disable.vpn=Voc\u00ea tem certeza que deseja desabilitar a VPN? +message.disabling.network.offering=Desabilita oferta de rede +message.disabling.vpc.offering=Desabilitando oferta VPC +message.disallowed.characters=Caracteres n\u00e3o-permitidos\: \\<\\,\\> +message.download.ISO=Por favor clique 00000 para baixar o ISO +message.download.template=Por favor clique 00000 para baixar o template +message.download.volume=Clique 00000 para baixar o disco +message.download.volume.confirm=Por favor confirme que voc\u00ea quer baixar este volume +message.edit.account=Editar ("-1" indica que n\u00e3o haver\u00e1 limites para a quantidade de recursos criado) +message.edit.confirm=Por favor confirme suas altera\u00e7\u00f5es antes de clicar em "Salvar". +message.edit.limits=Especifique os limites para os seguintes recursos. "-1" indica sem limite para o total de recursos criados. +message.edit.traffic.type=Favor especificar a etiqueta de tr\u00e1fego que voc\u00ea deseja associar com este tipo de tr\u00e1fego. +message.enable.account=Confirme se voc\u00ea deseja ativar a conta. +message.enabled.vpn.ip.sec=Sua chave IPSec (pre-shared) \u00e9 +message.enabled.vpn=Seu acesso VPN Est\u00e1 ativado e pode ser acessado atrav\u00e9s do IP +message.enable.user=Por favor confirme que voc\u00ea deseja habilitar este usu\u00e1rio. +message.enable.vpn.access=VPN Est\u00e1 desativada para este endere\u00e7o IP. Gostaria de ativar o acesso VPN? +message.enable.vpn=Por favor confirme que voc\u00ea deseja acesso VPN habilitado para este endere\u00e7o IP. +message.enabling.network.offering=Habilitando oferta de rede +message.enabling.security.group.provider=Habilitar provider de grupo de seguran\u00e7a +message.enabling.vpc.offering=Habilitando oferta VPC +message.enabling.zone.dots=Habilitando Zona.... +message.enabling.zone=Habilitando zona +message.enter.seperated.list.multiple.cidrs=Por favor entre a de CIDRs separadas por v\u00edrgula, se houver mais de uma +message.enter.token=Por favor entre o token que voc\u00ea recebeu no e-mail privado. +message.generate.keys=Por favor confirme que voc\u00ea deseja gerar novas chaves para este usu\u00e1rio. +message.gslb.delete.confirm=Confirme que voc\u00ea deseja apagar este GSLB +message.gslb.lb.remove.confirm=Confirme que voc\u00ea deseja remover o balanceamento de carga deste GSLB +message.guest.traffic.in.advanced.zone=O tr\u00e1fego de rede guest \u00e9 para comunica\u00e7\u00e3o entre m\u00e1quinas virtuais do usu\u00e1rio final. Especifique um intervalo de IDs de VLAN para transportar o tr\u00e1fego do guest para cada rede f\u00edsica. +message.guest.traffic.in.basic.zone=O tr\u00e1fego de rede guest \u00e9 para comunica\u00e7\u00e3o entre m\u00e1quinas virtuais do usu\u00e1rio final. Especifique um intervalo de endere\u00e7os IP para que CloudStack possa atribuir \u00e0s VMs. Certifique-se que este intervalo n\u00e3o se sobreponha o range de IPs reservados do sistema. +message.host.dedicated=Host dedicado +message.host.dedication.released=Host dedicado liberado +message.installWizard.click.retry=Click no bot\u00e3o para tentar executar novamente. +message.installWizard.copy.whatIsACluster=Um cluster prov\u00ea uma maneira de agrupar hosts. Os hosts em um cluster tem hardware id\u00eantico, rodam o mesmo hypervisor, est\u00e3o na mesma subnet, acessam o mesmo storage compartilhado. Inst\u00e2ncias de m\u00e1quinas virtuais (VMs) podem ser migradas a quente - live migration - de um host para outro host no mesmo cluster, sem interromper o servi\u00e7o para o usu\u00e1rio. Um Cluster \u00e9 a terceira maior unidade organizacional em uma instala\u00e7\u00e3o CloudStack&\#8482; . Clusters est\u00e3o contidos em pods e pods est\u00e3o contidos em zonas.

O CloudStack&\#8482; permite m\u00faltiplos clusters em uma mesma cloud, entretanto para a instala\u00e7\u00e3o b\u00e1sica, n\u00f3s iremos precisar apenas de um cluster. +message.installWizard.copy.whatIsAHost=Um host \u00e9 um \u00fanico computador. Os Hosts prov\u00eaem os recursos computacionais para executar as m\u00e1quinas virtuais. Cada host possu\u00ed o software do hypervisor instalado nele para gerenciar as guest VMs (Exceto os hosts bare metal, que s\u00e3o um caso especial discutido no Guia Avan\u00e7ado de Instala\u00e7\u00e3o). Por exemplo, um servidor Linux com KVM habilitado, um servidor Citrix XenServer e um servidor ESXi s\u00e3o hosts. Na Instala\u00e7\u00e3o B\u00e1sica, n\u00f3s utilizamos um \u00fanico host rodando XenServer ou KVM.

O host \u00e9 a menor unidade organizacional dentro de uma instala\u00e7\u00e3o CloudStack&\#8482; . Hosts est\u00e3o contidos dentro de Clusters, clusters est\u00e3o contidos dentro de pods e pods est\u00e3o contidos dentro de zonas. +message.installWizard.copy.whatIsAPod=Um pod normalmente representa um \u00fanico rack. Hosts no mesmo pod est\u00e3o na mesma subrede.

Um pod \u00e9 a segunda maior unidade organizacional de uma instala\u00e7\u00e3o CloudStack&\#8482; . Pods est\u00e3o contidos dentro de zonas. Cada zona, pode conter um ou mais pods; Na instala\u00e7\u00e3o b\u00e1sica, voc\u00ea ir\u00e1 ter apenas um pod na sua zona. +message.installWizard.copy.whatIsAZone=Uma zona \u00e9 a maior unidade organizacional em uma instala\u00e7\u00e3o CloudStack&\#8482; . Uma zona tipicamente corresponde a um \u00fanico datacenter, apesar de ser poss\u00edvel ter m\u00faltiplas zonas no mesmo datacenter. O benef\u00edcio de se organizar a infra-estrutura em zonas \u00e9 permitir o isolamento f\u00edsico e redund\u00e2ncia. Por exemplo, cada zona pode possuir sua pr\u00f3pria alimenta\u00e7\u00e3o de energia e link de sa\u00edda de internet e zonas podem estar geograficamente separadas (apesar de n\u00e3o ser obrigat\u00f3rio). +message.installWizard.copy.whatIsCloudStack=O CloudStack&\#8482 \u00e9 uma plataforma de software que agrega recursos computacionais para construir uma Cloud de Infra-estrutura como Servi\u00e7o (IaaS) p\u00fablica, privada ou h\u00edbrida. O CloudStack&\#8482 ger\u00eancia a rede, o storage e os recursos computacionais que comp\u00f5em a infra-estrutura de cloud. Utilize o CloudStack&\#8482 para instalar, gerenciar e configurar os ambientes de cloud computing.

Indo al\u00e9m de imagens de m\u00e1quinas virtuais individuais rodando em hardware commodity, CloudStack&\#8482 prov\u00ea uma solu\u00e7\u00e3o completa de software de infra-estrutura de cloud para entregar datacenters virtuais como um servi\u00e7o - possuindo todos os componentes essenciais para contruir, instalar e gerenciar aplica\u00e7\u00f5es na cloud multi-camadas e multi-tenant. Ambas as vers\u00f5es open-source e premium est\u00e3o dispon\u00edveis, com a vers\u00e3o opensource oferecendo praticamente os mesmos recursos. +message.installWizard.copy.whatIsPrimaryStorage=Uma infraestrutura de Cloud CloudStack; utiliza dois tipos de storage\: storage prim\u00e1rio e storage secund\u00e1rio. Ambos os tipos podem ser iSCSI, NFS servers, ou disco local.

O Storage prim\u00e1rio est\u00e1 associado com um cluster, e armazena os volumes de disco de cada guest VM para todas as VMs em execu\u00e7\u00e3o nos hosts deste cluster. O servidor de storage prim\u00e1rio tipicamente encontra-se localizado perto dos hosts. +message.installWizard.copy.whatIsSecondaryStorage=O storage secund\u00e1rio est\u00e1 associado a uma zona, ele \u00e9 respons\u00e1vel por armazenar o seguinte\:
  • Imagens de Templates do SO - que podem ser utilizadas para boot das VMs e podem incluir configura\u00e7\u00f5es adicionais, como por exemplo as aplica\u00e7\u00f5es instaladas
  • Imagens ISO - Imagens de sistema operacional que podem ser boot\u00e1veis ou n\u00e3o
  • Snapshots do volume de discos - c\u00f3pias salvas dos dados de uma VM que pode ser utilizada para recupera\u00e7\u00e3o de dados ou cria\u00e7\u00e3o de novos templates
+message.installWizard.now.building=Construindo sua cloud agora... +message.installWizard.tooltip.addCluster.name=Um nome para o cluster. Este nome pode ser um nome de sua escolha e n\u00e3o \u00e9 usado pelo CloudStack. +message.installWizard.tooltip.addHost.hostname=O nome DNS ou endere\u00e7o IP do host. +message.installWizard.tooltip.addHost.password=Este \u00e9 a senha do usu\u00e1rio especificado acima (da sua instala\u00e7\u00e3o do XenServer) +message.installWizard.tooltip.addHost.username=Usualmente root. +message.installWizard.tooltip.addPod.name=O nome para o pod +message.installWizard.tooltip.addPod.reservedSystemEndIp=Este \u00e9 o range de IP na rede privada que o CloudStack utiliza para gerenciar o storage secund\u00e1rio das VMs e Proxy Console das VMs. Estes endere\u00e7os IP s\u00e3o obtidos da mesma subrede dos servidores hosts. +message.installWizard.tooltip.addPod.reservedSystemGateway=O gateway para os hosts neste pod. +message.installWizard.tooltip.addPod.reservedSystemNetmask=A m\u00e1scara de rede est\u00e1 em uso na subrede que os guests ir\u00e3o utilizar. +message.installWizard.tooltip.addPod.reservedSystemStartIp=Este \u00e9 o range de IP na rede privada que o CloudStack utiliza para gerenciar o storage secund\u00e1rio das VMs e Proxy Console das VMs. Estes endere\u00e7os IP s\u00e3o obtidos da mesma subrede dos servidores hosts. +message.installWizard.tooltip.addPrimaryStorage.name=O Nome do dispositivo de storage. +message.installWizard.tooltip.addPrimaryStorage.path=(para NFS) No NFS este \u00e9 o path exportado pelo servidor. Path (para SharedMountPoint). Com o KVM este \u00e9 o path em cada host onde o storage prim\u00e1rio est\u00e1 montado. Por exemplo, "/mnt/primary". +message.installWizard.tooltip.addPrimaryStorage.server=(para NFS, iSCSI ou PreSetup) O Endere\u00e7o IP ou nome DNS do dispositivo de storage. +message.installWizard.tooltip.addSecondaryStorage.nfsServer=O endere\u00e7o IP do servidor NFS hospedando o storage secund\u00e1rio +message.installWizard.tooltip.addSecondaryStorage.path=Path exportado, localizado no servidor que voc\u00ea especificou acima +message.installWizard.tooltip.addZone.dns1=Estes s\u00e3o os servidores DNS utilizados pelas guest VMs na zona. Estes servidores DNS ser\u00e3o acessados pela interface de rede p\u00fablica que voc\u00ea ir\u00e1 adicionar posteriormente. O endere\u00e7o IP p\u00fablico da zona deve possuir uma rota para os servidores DNS configurados aqui. +message.installWizard.tooltip.addZone.dns2=Estes s\u00e3o os servidores DNS utilizados pelas guest VMs na zona. Estes servidores DNS ser\u00e3o acessados pela interface de rede p\u00fablica que voc\u00ea ir\u00e1 adicionar posteriormente. O endere\u00e7o IP p\u00fablico da zona deve possuir uma rota para os servidores DNS configurados aqui. +message.installWizard.tooltip.addZone.internaldns1=Estes s\u00e3o os servidores DNS utilizados pelas VMs de sistema nesta zona. Estes servidores DNS ser\u00e3o acessados atrav\u00e9s da interface de rede privada das VMs de sistema. O endere\u00e7o IP privado que voc\u00ea configurar para os pods deve possuir uma rota para os servidores DNS configurados aqui. +message.installWizard.tooltip.addZone.internaldns2=Estes s\u00e3o os servidores DNS utilizados pelas VMs de sistema nesta zona. Estes servidores DNS ser\u00e3o acessados atrav\u00e9s da interface de rede privada das VMs de sistema. O endere\u00e7o IP privado que voc\u00ea configurar para os pods deve possuir uma rota para os servidores DNS configurados aqui. +message.installWizard.tooltip.addZone.name=Um nome para a zona +message.installWizard.tooltip.configureGuestTraffic.description=Uma descri\u00e7\u00e3o da sua rede +message.installWizard.tooltip.configureGuestTraffic.guestEndIp=O range de endere\u00e7os IP que estar\u00e1 dispon\u00edvel para aloca\u00e7\u00e3o para os guests nesta zona. Caso uma Interface de Rede seja utilizada, estes IPs devem estar no mesmo CIDR que o CIDR do pod. +message.installWizard.tooltip.configureGuestTraffic.guestGateway=O gateway que os guests devem usar +message.installWizard.tooltip.configureGuestTraffic.guestNetmask=A m\u00e1scara de rede da subrede que os guests devem usar +message.installWizard.tooltip.configureGuestTraffic.guestStartIp=O range de endere\u00e7os IP que estar\u00e1 dispon\u00edvel para aloca\u00e7\u00e3o para os guests nesta zona. Caso uma Interface de Rede seja utilizada, estes IPs devem estar no mesmo CIDR que o CIDR do pod. +message.installWizard.tooltip.configureGuestTraffic.name=Um nome para sua rede +message.instance.scaled.up.confirm=Voc\u00ea realmente quer escalonar sua inst\u00e2ncia? +message.instanceWizard.noTemplates=Voc\u00ea n\u00e3o possui nenhum template dispon\u00edvel; por favor adicione um template compat\u00edvel e reinicie o wizard de inst\u00e2ncia. +message.ip.address.changed=Seu endere\u00e7o IP pode ter mudado; voc\u00ea gostaria de atualizar a listagem ? Note que neste caso o painel de detalhes ir\u00e1 fechar. +message.iso.desc=Imagem de disco contendo dados ou m\u00eddia de sistema operacional boot\u00e1vel +message.join.project=Voc\u00ea agora entrou em um projeto. Por favor troque para a vis\u00e3o de Projeto para visualizar o projeto. +message.launch.vm.on.private.network=Voc\u00ea deseja executar a sua inst\u00e2ncia na sua pr\u00f3pria rede privada dedicada? +message.launch.zone=A zona est\u00e1 pronta para ser executada; por favor, v\u00e1 para o pr\u00f3ximo passo. +message.listView.subselect.multi=(Ctrl/Cmd-click) +message.lock.account=Confirme se voc\u00ea deseja bloquear esta conta. Bloqueando a conta, todos os Usu\u00e1rios desta conta n\u00e3o estar\u00e3o mais habilitados a gerenciar os recursos na nuvem. Os recursos existentes (Cloud Server) ainda poder\u00e3o ser acessados. +message.migrate.instance.confirm=Confirme o host que voc\u00ea deseja migrar a inst\u00e2ncia virtual. +message.migrate.instance.to.host=Por favor confirme que voc\u00ea deseja migrar a inst\u00e2ncia para outro host. +message.migrate.instance.to.ps=Por favor confirme que voc\u00ea deseja migrar a inst\u00e2ncia para outro storage prim\u00e1rio. +message.migrate.router.confirm=Por favor confirme o host que voc\u00ea deseja migrar o roteador para\: +message.migrate.systemvm.confirm=Por favor confirme o host para o qual voc\u00ea deseja migrar a VM de sistema\: +message.migrate.volume=Por favor confirme que voc\u00ea deseja migrar o volume para outro storage prim\u00e1rio. +message.network.addVM.desc=Por favor especifique a rede onde voc\u00ea gostaria de adicionar esta VM. Uma nova NIC ser\u00e1 adicionada a esta rede. +message.network.addVMNIC=Por favor confirme que voc\u00ea gostaria de adicionar uma nova VM NIC para esta rede. +message.new.user=Especifique abaixo para adicionar novos usu\u00e1rios para a conta +message.no.affinity.groups=Voc\u00ea n\u00e3o tem nenhum grupo de afinidade. Por favor, v\u00e1 para o pr\u00f3ximo passo. +message.no.host.available=Sem hosts dispon\u00edveis para Migra\u00e7\u00e3o +message.no.network.support.configuration.not.true=Voc\u00ea n\u00e3o possui nenhuma zona com grupos de seguran\u00e7a habilitado. Assim sendo, n\u00e3o possui recursos adicionais de rede. Por favor continue para o passo 5. +message.no.network.support=O hypervisor escolhido, vSphere, n\u00e3o possui nenhum recurso de rede adicional. Por favor, v\u00e1 para o passo 5. +message.no.projects.adminOnly=Voc\u00ea n\u00e3o possui nenhum projeto.
Por favor solicite ao seu administrador a cria\u00e7\u00e3o de um novo projeto. +message.no.projects=Voc\u00ea n\u00e3o possui nenhum projeto.
Por favor crie um novo projeto \u00e0 partir da se\u00e7\u00e3o Projetos. +message.number.clusters=

Clusters

+message.number.hosts=

Hosts

+message.number.pods=

PODs

+message.number.storage=

Volumes do Storage Prim\u00e1rio

+message.number.zones=

Zonas

+message.pending.projects.1=Voc\u00ea possui convites de projetos pendentes\: +message.pending.projects.2=Para visualizar, por favor acesse a se\u00e7\u00e3o de projetos, depois selecione os convites no menu drop-down. +message.please.add.at.lease.one.traffic.range=Por favor adicione pelo menos um range de tr\u00e1fego. +message.please.proceed=Por favor, v\u00e1 para o pr\u00f3ximo passo. +message.please.select.a.configuration.for.your.zone=Por favor selecione uma configuracao para sua zona. +message.please.select.a.different.public.and.management.network.before.removing=Por favor selecione uma rede p\u00fablica e de gerenciamento diferente antes de remover +message.please.select.networks=Por favor selecione as redes para sua m\u00e1quina virtual. +message.please.wait.while.zone.is.being.created=Por favor, espere enquanto sua zona est\u00e1 sendo criada; isto pode demorar um pouco... +message.pod.dedication.released=Pod Dedicado liberado +message.portable.ip.delete.confirm=Favor confirmar que voc\u00ea deseja apagar esta Faixa de IPs Port\u00e1veis +message.project.invite.sent=Convite enviado para o usu\u00e1rio; Eles ser\u00e3o adicionados ao projeto ap\u00f3s aceitarem o convite +message.public.traffic.in.advanced.zone=O tr\u00e1fego p\u00fablico \u00e9 gerado quando as VMs na nuvem acessam a internet. Os IPs acess\u00edveis ao p\u00fablico devem ser alocados para essa finalidade. Os usu\u00e1rios finais podem usar a interface do usu\u00e1rio CloudStack para adquirir esses IPs afim de implementar NAT entre a sua rede de guests e sua rede p\u00fablica.

Forne\u00e7a pelo menos um intervalo de endere\u00e7os IP para o tr\u00e1fego de internet. +message.public.traffic.in.basic.zone=O tr\u00e1fego p\u00fablico \u00e9 gerado quando as VMs na nuvem acessam a Internet ou prestam servi\u00e7os aos clientes atrav\u00e9s da Internet. Os IPs acess\u00edveis ao p\u00fablico devem ser alocados para essa finalidade. Quando uma inst\u00e2ncia \u00e9 criada, um IP a partir deste conjunto de IPs p\u00fablicos ser\u00e3o destinados \u00e0 inst\u00e2ncia, al\u00e9m do endere\u00e7o IP guest. Um NAT est\u00e1tico 1-1 ser\u00e1 criada automaticamente entre o IP p\u00fablico e IP guest. Os usu\u00e1rios finais tamb\u00e9m podem usar a interface de usu\u00e1rio CloudStack para adquirir IPs adicionais afim de se implementar NAT est\u00e1tico entre suas inst\u00e2ncias e o IP p\u00fablico. +message.read.admin.guide.scaling.up=Por favor leia a sess\u00e3o sobre escalonamento din\u00e2mico no guia do administrador antes de escalonar. +message.recover.vm=Por favor, confirme a recupera\u00e7\u00e3o desta VM. +message.redirecting.region=Redirecionando para regi\u00e3o... +message.reinstall.vm=NOTA\: Proceda com cuidado. Isso far\u00e1 com que a m\u00e1quina virtual seja re-instalada a partir do Template. Todos os dados do disco ROOT ser\u00e3o perdidos. Volumes de Dados adicionais, se houver, n\u00e3o ser\u00e3o alterados. +message.remove.ldap=Voc\u00ea tem certeza que deseja deletar a configura\u00e7\u00e3o LDAP? +message.remove.region=Voc\u00ea tem certeza que deseja remover esta regi\u00e3o deste servidor de gerenciamento? +message.remove.vpc=Favor confirmar que voc\u00ea deseja remover a VPC +message.remove.vpn.access=Confirme se voc\u00ea deseja remover acesso VPN do seguinte Usu\u00e1rio. +message.reset.password.warning.notPasswordEnabled=O template desta inst\u00e2ncia foi criado sem uma senha habilitada +message.reset.password.warning.notStopped=Sua inst\u00e2ncia deve estar parada antes de tentar trocar sua senha atual +message.reset.VPN.connection=Favor confirmar que voc\u00ea deseja resetar a conex\u00e3o VPN +message.restart.mgmt.server=Reinicie o(s) servidor(es) de gerenciamento para que a nova configura\u00c3\u00a7\u00c3\u00a3o tenha efeito. +message.restart.mgmt.usage.server=Por favor reinicie seu servidor(es) de gerenciamento e seu servidor(es) de utiliza\u00e7\u00e3o para as mudan\u00e7as entrarem em efeito. +message.restart.network=Por favor confirme que voc\ufffd deseja reiniciar a rede +message.restart.vpc=Favor confirmar que voc\u00ea deseja reiniciar a VPC +message.restoreVM=Quer restaurar a VM? +message.security.group.usage=(Use Ctrl-clique para selecionar todos os Security Groups) +message.select.affinity.groups=Por favor, selecione quaisquer grupos de afinidade que voc\u00ea deseja que esta VM perten\u00e7a\: +message.select.a.zone=A zone tipicamente corresponde a um \u00fanico datacenter. M\u00faltiplas zonas auxiliam a cloud a ser mais confi\u00e1vel provendo isolamento f\u00edsico e redund\u00e2ncia. +message.select.instance=Por favor selecione uma inst\u00e2ncia. +message.select.iso=Por favor selecione um ISO para sua nova inst\u00e2ncia virtual +message.select.item=Por favor selecione um item. +message.select.security.groups=Por favor selecione o(s) grupo(s) de seguran\u00e7a para sua nova VM +message.select.template=Por favor selecione um template para sua nova inst\u00e2ncia virtual. +message.select.tier=Por favor, selecione um tier +message.set.default.NIC.manual=Por favor atualize manualmente o NIC padr\u00e3o desta VM agora. +message.set.default.NIC=Por favor confirme que voc\u00ea quer tornar este NIC o padr\u00e3o para esta VM, +message.setup.physical.network.during.zone.creation=Ao adicionar uma zona avan\u00e7ada, voc\u00ea precisa configurar uma ou mais redes f\u00edsicas. Cada rede corresponde \u00e0 uma Interface de Rede no hypervisor. Cada rede f\u00edsica pode ser utilizada para transportar um ou mais tipos de tr\u00e1fego, com certas restri\u00e7\u00f5es sobre como eles podem ser combinados.
Arraste e solte um ou mais tipos de tr\u00e1fego em cada rede f\u00edsica. +message.setup.physical.network.during.zone.creation.basic=Quando adicionar uma zona b\u00e1sica, voc\u00ea pode configurar uma rede f\u00edsica, que corresponde a uma Interface de Rede no hypervisor. A rede carrega diversos tipos de tr\u00e1fego.

Voc\u00ea pode adicionar e remover outros tipos de tr\u00e1fego na mesma interface de rede f\u00edsica. +message.setup.successful=Cloud configurada com sucesso\! +message.snapshot.schedule=Voc\u00ea pode configurar Snapshots recorrentes agendados selecionando as op\u00e7\u00f5es Dispon\u00edveis abaixo +message.specifiy.tag.key.value=Por favor especifique chave e valor da tag +message.specify.url=Por favor especifique a URL +message.step.1.continue=Selecione o template ou ISO para continuar +message.step.1.desc=Por favor, selecione um template para a sua nova inst\u00e2ncia virtual. Voc\u00ea pode tamb\u00e9m escolher um template limpo e instalar a partir de uma imagem ISO. +message.step.2.continue=Selecione o plano +message.step.2.desc= +message.step.3.continue=Seleciona a oferta de disco +message.step.3.desc= +message.step.4.continue=Selecione pelo menos uma rede para continuar +message.step.4.desc=Selecione a rede principal que a sua inst\u00e2ncia virtual estar\u00e1 conectada. +message.storage.traffic=Tr\u00e1fego entre os recursos internos do CloudStack, incluindo todos os componentes que se comunicam com o servidor de gerenciamento tais como hosts e m\u00e1quinas virtuais de sistema CloudStack. Por favor, configure o tr\u00e1fego do storage aqui. +message.suspend.project=Voc\u00ea tem certeza que deseja suspender este projeto ? +message.systems.vms.ready=VM de Sistema prontas. +message.template.copying=O template est\u00e1 sendo copiado. +message.template.desc=Imagem de SO que pode ser utilizada para bootar VMs +message.tier.required=Tier \u00e9 obrigat\u00f3rio +message.tooltip.dns.1=Endere\u00e7o de um servidor DNS que ser\u00e1 utilizado por todas as VMs da Zone. A faixa de IPs p\u00fablicos para essa Zone deve possuir uma rota para o servidor configurado. +message.tooltip.dns.2=Um servidor DNS secund\u00e1rio para ser utilizado pelas VMs nesta zona. Os endere\u00e7os IP p\u00fablicos nesta zona devem ter rota para este servidor. +message.tooltip.internal.dns.1=Nome de um servidor DNS que ser\u00e1 utilizado pelas VMs internas de sistema do CloudStack nesta zona. Os endere\u00e7os privados dos pods devem ter uma rota para este servidor. +message.tooltip.internal.dns.2=Nome de um servidor DNS que ser\u00e1 utilizado pelas VMs internas de sistema do CloudStack nesta zona. Os endere\u00e7os privados dos pods devem ter uma rota para este servidor. +message.tooltip.network.domain=Um sufixo DNS que ir\u00e1 criar um nome de dom\u00ednio customizado para a rede que \u00e9 acessada pelas guest VMs. +message.tooltip.pod.name=Um nome para este pod. +message.tooltip.reserved.system.gateway=O gateway para os hosts neste pod. +message.tooltip.reserved.system.netmask=O prefixo de rede que define a subrede deste pod. Utilize a nota\u00e7\u00e3o CIDR. +message.tooltip.zone.name=Um nome para a zona. +message.update.os.preference=Escolha o SO de preferencia para este host. Todas Inst\u00e2ncias com preferencias similares ser\u00e3o alocadas neste host antes de tentar em outro. +message.update.resource.count=Por favor confirme que voc\u00ea quer atualizar a contagem de recursos para esta conta. +message.update.ssl=Envie o novo certificado SSL X.509 para ser atualizado em cada console proxy\: +message.update.ssl.failed=Atualiza\u00e7\u00e3o do Certificado SSL falhou +message.update.ssl.succeeded=Atualiza\u00e7\u00e3o do Certificado SSL feita com sucesso +message.validate.accept=Por favor entre com uma extens\u00e3o v\u00e1lida. +message.validate.creditcard=Por favor entre um n\u00famero de cart\u00e3o de cr\u00e9dito v\u00e1lido. +message.validate.date.ISO=Por favor entre com uma data v\u00e1lida (ISO). +message.validate.date=Por favor entre com uma data v\u00e1lida. +message.validate.digits=Por favor entre com d\u00edgitos apenas. +message.validate.email.address=Por favor entre um email v\u00e1lido. +message.validate.equalto=Por favor entre com o mesmo valor novamente. +message.validate.fieldrequired=Este campo \u00e9 obrigat\u00f3rio. +message.validate.fixfield=Por favor, arrume este campo. +message.validate.instance.name=Nomes de inst\u00e2ncias n\u00e3o podem ter mais de 63 caracteres. Somente letras ASCII a~z, A~Z, d\u00edgitos 0~9 e h\u00edfen s\u00e3o permitidos. Deve come\u00e7ar com uma letra e terminar com uma letra ou d\u00edgito. +message.validate.invalid.characters=Caracteres inv\u00e1lidos encontrados, por favor corrija. +message.validate.maxlength=Por favor entre com mais de {0} caracteres. +message.validate.max=Por favor entre com um valor menor que ou igual a {0}. +message.validate.minlength=Por favor entre com pelo menos {0} caracteres. +message.validate.number=Por favor entre um n\u00famero v\u00e1lido. +message.validate.range.length=Por favor entre com um valor com tamanho entre {0} e {1} caracteres. +message.validate.range=Por favor entre com um valor com valor entre {0} e {1}. +message.validate.URL=Por favor entre uma URL v\u00e1lida. +message.virtual.network.desc=Rede virtual dedicado para sua conta. O Dom\u00ednio de broadcast Est\u00e1 na VLAN e todo acesso a internet \u00e9 roteado atrav\u00e9s do virtual router. +message.vm.create.template.confirm=Criar Template reiniciar\u00e1 a VM automaticamente. +message.vm.review.launch=Por favor revise a informa\u00e7\u00e3o abaixo e confirme que sua inst\u00e2ncia virtual est\u00e1 correta antes de executa-la. +message.vnmc.available.list=VNMC n\u00e3o est\u00e1 dispon\u00edvel na lista de provedores. +message.vnmc.not.available.list=VNMC n\u00e3o est\u00e1 dispon\u00edvel na lista de provedores. +message.volume.create.template.confirm=Confirme se voc\u00ea deseja criar um template a partir deste disco. A cria\u00e7\u00e3o do template pode levar alguns minutos ou mais dependendo do tamanho do disco. +message.waiting.for.builtin.templates.to.load=Aguardando a carga dos templates integrados... +message.XSTools61plus.update.failed=A atualiza\u00e7\u00e3o do campo Original XS Version is 6.1\\+ falhou. Erro\: +message.you.must.have.at.least.one.physical.network=Voc\u00ea deve ter pelo menos uma rede f\u00edsica +message.your.cloudstack.is.ready=Seu CLoudStack est\u00e1 pronto\! +message.Zone.creation.complete=Cria\u00e7\u00e3o de zona completa +message.zone.creation.complete.would.you.like.to.enable.this.zone=Cria\u00e7\u00e3o de zona completa. Voc\u00ea gostaria de habilitar esta zona? +message.zone.no.network.selection=A zona que voc\u00ea selecionou n\u00e3o possui nenhuma rede para ser escolhida. +message.zone.step.1.desc=Seleciona o modelo de rede para a zona. +message.zone.step.2.desc=Entre a informa\u00e7\u00e3o a seguir para adicionar uma nova zona +message.zone.step.3.desc=Entre a informa\u00e7\u00e3o a seguir para adicionar um novo pod +message.zoneWizard.enable.local.storage=AVISO\: se voc\u00ea habilitar storage local para esta zona, voc\u00ea deve fazer o seguinte, dependendo se voc\u00ea quiser que suas m\u00e1quinas virtuais de sistema inicializem\:

1. Se m\u00e1quinas virtuais de sistema precisam ser iniciadas em storage prim\u00e1ria, storage prim\u00e1ria precisa ser adicionada \u00e0 zona ap\u00f3s a cria\u00e7\u00e3o. Voc\u00ea tamb\u00e9m deve ativar a zona em um estado desabilitado.

2. Se m\u00e1quinas virtuais de sistema precisam ser iniciadas em storage local, system.vm.use.local.storage precisa ser estabelecida como verdadeira antes de voc\u00ea habilitar a zona.


Voc\u00ea quer continuar? +messgae.validate.min=Por favor entre com um valor maior que ou igual a {0}. +mode=Modo +network.rate=Taxa de Transfer\u00eancia +notification.reboot.instance=Reiniciar inst\u00e2ncia +notification.start.instance=Iniciar inst\u00e3ncia +notification.stop.instance=Parar inst\u00e2ncia +side.by.side=Lado a Lado +state.Accepted=Aceito +state.Active=Ativo +state.Allocated=Alocado +state.Allocating=Alocando +state.BackedUp=Back up realizado com sucesso +state.BackingUp=Realizando Back up +state.Completed=Completo +state.Creating=Criando +state.Declined=Recusado +state.Destroyed=Destru\u00eddo +state.detached=Desanexado +state.Disabled=Desativado +state.enabled=Habilitado +state.Enabled=Habilitado +state.Error=Erro +state.Expunging=Removendo +state.Migrating=Migrando +state.Pending=Pendente +state.ready=Pronto +state.Ready=Pronto +state.Running=Executando +state.Starting=Iniciando +state.Stopped=Parado +state.Stopping=Parando +state.Suspended=Suspendido +ui.listView.filters.all=Todos +ui.listView.filters.mine=Meus +label.max.hosts.supported=M\u00E1ximo de hosts suportados diff --git a/client/pom.xml b/client/pom.xml index 696cadec1e12..ce34a9e36226 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -19,14 +19,6 @@ cloudstack 4.11.1.0 - - - - juniper-contrail - http://juniper.github.io/contrail-maven/snapshots - - - javax.servlet @@ -137,6 +129,11 @@ cloud-plugin-user-authenticator-saml2 ${project.version} + + org.apache.cloudstack + cloud-plugin-user-authenticator-oauth2 + ${project.version} + org.apache.cloudstack cloud-plugin-user-authenticator-sha256salted @@ -458,6 +455,16 @@ cloud-plugin-network-globodns ${project.version} + + org.apache.cloudstack + cloud-plugin-network-globonetwork + ${project.version} + + + org.apache.cloudstack + cloud-plugin-network-globoaclapi + ${project.version} + org.apache.cloudstack cloud-plugin-database-quota @@ -473,6 +480,11 @@ cloud-plugin-integrations-prometheus-exporter ${project.version} + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + @@ -752,6 +764,9 @@ + + META-INF/services/org.xmlpull.v1.XmlPullParserFactory + @@ -781,6 +796,13 @@ + + + org.apache.cloudstack + cloud-client-ui + ${project.version} + + diff --git a/client/src/org/apache/cloudstack/ServerDaemon.java b/client/src/org/apache/cloudstack/ServerDaemon.java index 985b67b755ae..4936c4254ca1 100644 --- a/client/src/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/org/apache/cloudstack/ServerDaemon.java @@ -235,8 +235,9 @@ private HandlerCollection createHandlers() { } // Request log handler - final RequestLogHandler log = new RequestLogHandler(); - log.setRequestLog(createRequestLog()); + // Removed because it's not necessary. Nginx already does. + //final RequestLogHandler log = new RequestLogHandler(); + //log.setRequestLog(createRequestLog()); // Redirect root context handler MovedContextHandler rootRedirect = new MovedContextHandler(); @@ -245,7 +246,7 @@ private HandlerCollection createHandlers() { rootRedirect.setPermanent(true); // Put rootRedirect at the end! - return new HandlerCollection(log, gzipHandler, rootRedirect); + return new HandlerCollection(/*log, */gzipHandler, rootRedirect); } private RequestLog createRequestLog() { diff --git a/client/src/org/apache/cloudstack/maven/plugins/ServicesResourceTransformerCustom.java b/client/src/org/apache/cloudstack/maven/plugins/ServicesResourceTransformerCustom.java new file mode 100644 index 000000000000..459ffe8b45af --- /dev/null +++ b/client/src/org/apache/cloudstack/maven/plugins/ServicesResourceTransformerCustom.java @@ -0,0 +1,125 @@ +package org.apache.cloudstack.maven.plugins; + +import com.google.common.io.LineReader; +import org.apache.commons.io.IOUtils; +import org.apache.maven.plugins.shade.relocation.Relocator; +import org.apache.maven.plugins.shade.resource.ResourceTransformer; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +public class ServicesResourceTransformerCustom implements ResourceTransformer +{ + + //configuration + private String resource; + + private Map serviceEntries = new HashMap<>(); + + public String getResource() { + return resource; + } + + public void setResource(String resource) { + this.resource = resource; + } + + + public boolean canTransformResource( String resource ) + { + if ( this.resource.startsWith( resource ) ) + { + return true; + } + + return false; + } + + public void processResource(String resource, InputStream is, final List relocators ) + throws IOException + { + if (getResource().equals(resource)) { + ServiceStream out = serviceEntries.get( resource ); + if ( out == null ) + { + out = new ServiceStream(); + serviceEntries.put( resource, out ); + } + + final ServiceStream fout = out; + + final String content = IOUtils.toString( is ); + StringReader reader = new StringReader( content ); + LineReader lineReader = new LineReader( reader ); + String line; + while ( ( line = lineReader.readLine() ) != null ) + { + fout.append( line ); + } + } + + } + public boolean hasTransformedResource() + { + return serviceEntries.size() > 0; + } + + public void modifyOutputStream( JarOutputStream jos ) throws IOException + { + for ( Map.Entry entry : serviceEntries.entrySet() ) + { + String key = entry.getKey(); + ServiceStream data = entry.getValue(); + + jos.putNextEntry( new JarEntry( key ) ); + + PrintWriter writer = new PrintWriter( jos ); + InputStreamReader streamReader = new InputStreamReader( data.toInputStream() ); + BufferedReader reader = new BufferedReader( streamReader ); + String className; + + while ( ( className = reader.readLine() ) != null ) + { + writer.println( className ); + writer.flush(); + } + + reader.close(); + data.reset(); + } + } + + static class ServiceStream extends ByteArrayOutputStream + { + + public ServiceStream() + { + super( 1024 ); + } + + public void append( String content ) throws IOException + { + write( ',' ); + byte[] contentBytes = content.getBytes( "UTF-8" ); + this.write( contentBytes ); + } + + public InputStream toInputStream() + { + return new ByteArrayInputStream( buf, 0, count ); + } + + } + +} \ No newline at end of file diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in new file mode 100644 index 000000000000..ba2ece3c8f7b --- /dev/null +++ b/client/tomcatconf/commands.properties.in @@ -0,0 +1,812 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +### bitmap of permissions at the end of each classname, 1 = ADMIN, 2 = RESOURCE_DOMAIN_ADMIN, 4 = DOMAIN_ADMIN, 8 = USER +### Please standardize naming conventions to camel-case (even for acronyms). + +### CloudStack authentication commands +login=15 +logout=15 + +### SAML SSO/SLO commands +samlSso=15 +samlSlo=15 +getSPMetadata=15 +listIdps=15 +authorizeSamlSso=7 +listSamlAuthorization=7 +listAndSwitchSamlAccount=15 + +### Account commands +createAccount=7 +deleteAccount=7 +updateAccount=7 +disableAccount=7 +enableAccount=7 +lockAccount=7 +listAccounts=15 +markDefaultZoneForAccount=1 + +#### User commands +createUser=7 +deleteUser=7 +updateUser=15 +listUsers=7 +lockUser=7 +disableUser=7 +enableUser=7 +getUser=1 + +#### Domain commands +createDomain=1 +updateDomain=1 +deleteDomain=1 +listDomains=7 +listDomainChildren=7 + +####Cloud Identifier commands +getCloudIdentifier=15 + +#### Limit commands +updateResourceLimit=7 +updateResourceCount=7 +listResourceLimits=15 + +#### VM commands +deployVirtualMachine=15 +destroyVirtualMachine=15 +rebootVirtualMachine=15 +startVirtualMachine=15 +stopVirtualMachine=15 +resetPasswordForVirtualMachine=15 +resetSSHKeyForVirtualMachine=15 +updateVirtualMachine=15 +listVirtualMachines=15 +getVMPassword=15 +restoreVirtualMachine=15 +changeServiceForVirtualMachine=15 +scaleVirtualMachine=15 +assignVirtualMachine=7 +migrateVirtualMachine=1 +migrateVirtualMachineWithVolume=1 +recoverVirtualMachine=7 +expungeVirtualMachine=7 +getVirtualMachineUserData=15 + +#### snapshot commands +createSnapshot=15 +listSnapshots=15 +deleteSnapshot=15 +createSnapshotPolicy=15 +updateSnapshotPolicy=15 +deleteSnapshotPolicies=15 +listSnapshotPolicies=15 +revertSnapshot=15 + +#### template commands +createTemplate=15 +registerTemplate=15 +updateTemplate=15 +copyTemplate=15 +deleteTemplate=15 +listTemplates=15 +updateTemplatePermissions=15 +listTemplatePermissions=15 +extractTemplate=15 +prepareTemplate=1 + +#### iso commands +attachIso=15 +detachIso=15 +listIsos=15 +registerIso=15 +updateIso=15 +deleteIso=15 +copyIso=15 +updateIsoPermissions=15 +listIsoPermissions=15 +extractIso=15 + +#### guest OS commands +listOsTypes=15 +listOsCategories=15 +addGuestOs=1 +updateGuestOs=1 +removeGuestOs=1 + +#### guest OS mapping commands +listGuestOsMapping=1 +addGuestOsMapping=1 +updateGuestOsMapping=1 +removeGuestOsMapping=1 + +#### service offering commands +createServiceOffering=1 +deleteServiceOffering=1 +updateServiceOffering=1 +listServiceOfferings=15 + +#### disk offering commands +createDiskOffering=1 +updateDiskOffering=1 +deleteDiskOffering=1 +listDiskOfferings=15 + +#### vlan commands +createVlanIpRange=1 +deleteVlanIpRange=1 +listVlanIpRanges=1 +dedicatePublicIpRange=1 +releasePublicIpRange=1 +dedicateGuestVlanRange=1 +releaseDedicatedGuestVlanRange=1 +listDedicatedGuestVlanRanges=1 + +#### address commands +associateIpAddress=15 +disassociateIpAddress=15 +listPublicIpAddresses=15 +updateIpAddress=15 + +#### firewall commands +listPortForwardingRules=15 +createPortForwardingRule=15 +deletePortForwardingRule=15 +updatePortForwardingRule=15 + +#### NAT commands +enableStaticNat=15 +createIpForwardingRule=15 +deleteIpForwardingRule=15 +listIpForwardingRules=15 +disableStaticNat=15 + +#### load balancer commands +listGloboLbNetworks=15 +createLoadBalancerRule=15 +createGloboLoadBalancer=15 +deleteGloboLoadBalancer=15 +listGloboLinkableLoadBalancers=15 +linkGloboLoadBalancer=15 +unlinkGloboLoadBalancer=15 +deleteLoadBalancerRule=15 +removeFromLoadBalancerRule=15 +assignToLoadBalancerRule=15 +assignNetworksToLoadBalancerRule=15 +removeNetworksFromLoadBalancerRule=15 +createLBStickinessPolicy=15 +updateLBStickinessPolicy=15 +deleteLBStickinessPolicy=15 +listLoadBalancerRules=15 +listLBStickinessPolicies=15 +listLBHealthCheckPolicies=15 +createLBHealthCheckPolicy=15 +updateLBHealthCheckPolicy=15 +deleteLBHealthCheckPolicy=15 +listLoadBalancerRuleInstances=15 +updateLoadBalancerRule=15 +registerDnsForResource=15 +getGloboResourceConfiguration=15 + +##### SSL offload commands + +uploadSslCert=15 +deleteSslCert=15 +listSslCerts=15 +assignCertToLoadBalancer=15 +removeCertFromLoadBalancer=15 + +#### autoscale commands +createCounter=1 +createCondition=15 +createAutoScalePolicy=15 +createAutoScaleVmProfile=15 +createAutoScaleVmGroup=15 +deleteCounter=1 +deleteCondition=15 +deleteAutoScalePolicy=15 +deleteAutoScaleVmProfile=15 +deleteAutoScaleVmGroup=15 +listCounters=15 +listConditions=15 +listAutoScalePolicies=15 +listAutoScaleVmProfiles=15 +listAutoScaleVmGroups=15 +enableAutoScaleVmGroup=15 +disableAutoScaleVmGroup=15 +updateAutoScalePolicy=15 +updateAutoScaleVmProfile=15 +updateAutoScaleVmGroup=15 + +#### router commands +startRouter=7 +rebootRouter=7 +stopRouter=7 +destroyRouter=7 +changeServiceForRouter=7 +listRouters=7 +listVirtualRouterElements=7 +configureVirtualRouterElement=7 +createVirtualRouterElement=7 +upgradeRouterTemplate=1 + +#### system vm commands +startSystemVm=1 +rebootSystemVm=1 +stopSystemVm=1 +destroySystemVm=1 +listSystemVms=3 +migrateSystemVm=1 +changeServiceForSystemVm=1 +scaleSystemVm=1 + +#### configuration commands +updateConfiguration=1 +listConfigurations=1 +listCapabilities=15 +listDeploymentPlanners=1 +cleanVMReservations=1 + +#### pod commands +createPod=1 +updatePod=1 +deletePod=1 +listPods=3 + +#### zone commands +createZone=1 +updateZone=1 +deleteZone=1 +listZones=15 + +#### events commands +listEvents=15 +listEventTypes=15 +archiveEvents=15 +deleteEvents=15 + +#### alerts commands +listAlerts=3 +archiveAlerts=1 +deleteAlerts=1 +generateAlert=1 + +#### system capacity commands +listCapacity=3 + +#### swift commands +addSwift=1 +listSwifts=1 + +#### s3 commands +addS3=1 +listS3s=1 + +#### image store commands +addImageStore=1 +listImageStores=1 +deleteImageStore=1 +createSecondaryStagingStore=1 +listSecondaryStagingStores=1 +deleteSecondaryStagingStore=1 +updateCloudToUseObjectStore=1 + +#### host commands +addHost=3 +addCluster=1 +deleteCluster=1 +updateCluster=1 +reconnectHost=1 +updateHost=1 +deleteHost=3 +prepareHostForMaintenance=1 +cancelHostMaintenance=1 +listHosts=3 +findHostsForMigration=1 +addSecondaryStorage=1 +updateHostPassword=1 +releaseHostReservation=1 + +#### VmWare DC +addVmwareDc=1 +removeVmwareDc=1 +listVmwareDcs=1 + +#### volume commands +attachVolume=15 +uploadVolume=15 +detachVolume=15 +createVolume=15 +deleteVolume=15 +listVolumes=15 +extractVolume=15 +migrateVolume=15 +resizeVolume=15 +updateVolume=1 + +#### registration command: FIXME -- this really should be something in management server that +#### generates a new key for the user and they just have to +#### use that key...the key is stored in the db associated w/ +#### the userId...every request to the developer API should be +#### checked against the key +registerUserKeys=15 + +### async-query command +queryAsyncJobResult=15 +listAsyncJobs=15 + +#### storage pools commands +listStoragePools=3 +listStorageProviders=3 +createStoragePool=1 +updateStoragePool=1 +deleteStoragePool=1 +listClusters=3 +enableStorageMaintenance=1 +cancelStorageMaintenance=1 +findStoragePoolsForMigration=1 + +#### security group commands +createSecurityGroup=15 +deleteSecurityGroup=15 +authorizeSecurityGroupIngress=15 +revokeSecurityGroupIngress=15 +authorizeSecurityGroupEgress=15 +revokeSecurityGroupEgress=15 +listSecurityGroups=15 + +#### vm group commands +createInstanceGroup=15 +deleteInstanceGroup=15 +updateInstanceGroup=15 +listInstanceGroups=15 + +### Certificate commands +uploadCustomCertificate=1 + +### other commands +listHypervisors=15 + +### VPN +createRemoteAccessVpn=15 +deleteRemoteAccessVpn=15 +listRemoteAccessVpns=15 +updateRemoteAccessVpn=15 + + +addVpnUser=15 +removeVpnUser=15 +listVpnUsers=15 + +#### network offering commands +createNetworkOffering=1 +updateNetworkOffering=1 +deleteNetworkOffering=1 +listNetworkOfferings=15 + +#### network commands +createNetwork=15 +deleteNetwork=15 +listNetworks=15 +restartNetwork=15 +updateNetwork=15 + +#### nic commands #### +addNicToVirtualMachine=15 +removeNicFromVirtualMachine=15 +updateDefaultNicForVirtualMachine=15 + +#### +addIpToNic=15 +removeIpFromNic=15 +listNics=15 + +#### SSH key pair commands +registerSSHKeyPair=15 +createSSHKeyPair=15 +deleteSSHKeyPair=15 +listSSHKeyPairs=15 + +#### Projects commands +createProject=15 +deleteProject=15 +updateProject=15 +activateProject=15 +suspendProject=15 +listProjects=15 +addAccountToProject=15 +deleteAccountFromProject=15 +listProjectAccounts=15 +listProjectInvitations=15 +updateProjectInvitation=15 +deleteProjectInvitation=15 + +#### +createFirewallRule=15 +deleteFirewallRule=15 +listFirewallRules=15 +updateFirewallRule=15 + +#### +createEgressFirewallRule=15 +deleteEgressFirewallRule=15 +listEgressFirewallRules=15 +updateEgressFirewallRule=15 + +#### hypervisor capabilities commands +updateHypervisorCapabilities=1 +listHypervisorCapabilities=1 + +#### Physical Network commands +createPhysicalNetwork=1 +deletePhysicalNetwork=1 +listPhysicalNetworks=1 +updatePhysicalNetwork=1 + +#### Physical Network Service Provider commands +listSupportedNetworkServices=1 +addNetworkServiceProvider=1 +deleteNetworkServiceProvider=1 +listNetworkServiceProviders=1 +updateNetworkServiceProvider=1 + +#### Physical Network Traffic Type commands +addTrafficType=1 +deleteTrafficType=1 +listTrafficTypes=1 +updateTrafficType=1 +listTrafficTypeImplementors=1 + +#### Storage Network commands +createStorageNetworkIpRange=1 +deleteStorageNetworkIpRange=1 +listStorageNetworkIpRange=1 +updateStorageNetworkIpRange=1 + +### Network Devices commands +addNetworkDevice=1 +listNetworkDevice=1 +deleteNetworkDevice=1 + +### VPC commands +createVPC=15 +listVPCs=15 +deleteVPC=15 +updateVPC=15 +restartVPC=15 + +#### VPC offering commands +createVPCOffering=1 +updateVPCOffering=1 +deleteVPCOffering=1 +listVPCOfferings=15 + +#### Private gateway commands +createPrivateGateway=1 +listPrivateGateways=15 +deletePrivateGateway=1 + +#### Network ACL commands +createNetworkACL=15 +updateNetworkACLItem=15 +deleteNetworkACL=15 +listNetworkACLs=15 +createNetworkACLList=15 +deleteNetworkACLList=15 +replaceNetworkACLList=15 +listNetworkACLLists=15 +updateNetworkACLList=15 + + +#### Static route commands +createStaticRoute=15 +deleteStaticRoute=15 +listStaticRoutes=15 + +#### Tags commands +createTags=15 +deleteTags=15 +listTags=15 + +#### Meta Data commands +addResourceDetail=1 +removeResourceDetail=1 +listResourceDetails=15 + +### Site-to-site VPN commands +createVpnCustomerGateway=15 +createVpnGateway=15 +createVpnConnection=15 +deleteVpnCustomerGateway=15 +deleteVpnGateway=15 +deleteVpnConnection=15 +updateVpnCustomerGateway=15 +resetVpnConnection=15 +listVpnCustomerGateways=15 +listVpnGateways=15 +listVpnConnections=15 +updateVpnConnection=15 +updateVpnGateway=15 + +#### router commands +createVirtualRouterElement=7 +configureVirtualRouterElement=7 +listVirtualRouterElements=7 + +#### ovs commands +createOvsElement=7 +configureOvsElement=7 +listOvsElements=7 + +#### usage commands +generateUsageRecords=1 +listUsageRecords=7 +listUsageTypes=1 + +#### traffic monitor commands +addTrafficMonitor=1 +deleteTrafficMonitor=1 +listTrafficMonitors=1 + +#### Cisco Nexus 1000v Virtual Supervisor Module (VSM) commands +deleteCiscoNexusVSM=1 +enableCiscoNexusVSM=1 +disableCiscoNexusVSM=1 +listCiscoNexusVSMs=1 + +#### f5 big ip load balancer commands + +#Deprecated commands +addExternalLoadBalancer=1 +deleteExternalLoadBalancer=1 +listExternalLoadBalancers=1 + +addF5LoadBalancer=1 +configureF5LoadBalancer=1 +deleteF5LoadBalancer=1 +listF5LoadBalancers=1 +listF5LoadBalancerNetworks=1 + +#### juniper srx firewall commands +addExternalFirewall=1 +deleteExternalFirewall=1 +listExternalFirewalls=1 + +addSrxFirewall=1 +deleteSrxFirewall=1 +configureSrxFirewall=1 +listSrxFirewalls=1 +listSrxFirewallNetworks=1 + +#### Palo Alto firewall commands +addPaloAltoFirewall=1 +deletePaloAltoFirewall=1 +configurePaloAltoFirewall=1 +listPaloAltoFirewalls=1 +listPaloAltoFirewallNetworks=1 + +####Netapp integration commands +createVolumeOnFiler=15 +destroyVolumeOnFiler=15 +listVolumesOnFiler=15 +createLunOnFiler=15 +destroyLunOnFiler=15 +listLunsOnFiler=15 +associateLun=15 +dissociateLun=15 +createPool=15 +deletePool=15 +modifyPool=15 +listPools=15 + +#### netscaler load balancer commands +addNetscalerLoadBalancer=1 +deleteNetscalerLoadBalancer=1 +configureNetscalerLoadBalancer=1 +listNetscalerLoadBalancers=1 +listNetscalerLoadBalancerNetworks=1 + +#### nicira nvp commands + +addNiciraNvpDevice=1 +deleteNiciraNvpDevice=1 +listNiciraNvpDevices=1 +listNiciraNvpDeviceNetworks=1 + +# Not implemented (yet) +#configureNiciraNvpDevice=1 + +#### bigswitch vns commands + +addBigSwitchVnsDevice=1 +deleteBigSwitchVnsDevice=1 +listBigSwitchVnsDevices=1 + +#### stratosphere ssp commands + +addStratosphereSsp=1 +deleteStratoshereSsp=1 + +#### host simulator commands + +configureSimulator=1 +querySimulatorMock=1 +cleanupSimulatorMock=1 + +#### api discovery commands + +listApis=15 + +#### API Rate Limit service command + +getApiLimit=15 +resetApiLimit=1 + +#### Region commands +addRegion=1 +updateRegion=1 +removeRegion=1 +listRegions=15 + +#### GSLB (Global Server Load Balancing) commands +createGlobalLoadBalancerRule=15 +deleteGlobalLoadBalancerRule=15 +updateGlobalLoadBalancerRule=15 +listGlobalLoadBalancerRules=15 +assignToGlobalLoadBalancerRule=15 +removeFromGlobalLoadBalancerRule=15 + +### VM Snapshot commands +listVMSnapshot=15 +createVMSnapshot=15 +deleteVMSnapshot=15 +revertToVMSnapshot=15 + +#### Baremetal commands +addBaremetalHost=1 +addBaremetalPxeKickStartServer=1 +addBaremetalPxePingServer=1 +addBaremetalDhcp=1 +listBaremetalDhcp=1 +listBaremetalPxeServers=1 + +#### UCS commands +addUcsManager=1 +listUcsManagers=1 +listUcsProfiles=1 +listUcsBlades=1 +associateUcsProfileToBlade=1 +removedeleteUcsManager=1 + +#### New Load Balancer commands +createLoadBalancer=15 +listLoadBalancers=15 +deleteLoadBalancer=15 +updateLoadBalancer=15 + +#Internal Load Balancer Element commands +configureInternalLoadBalancerElement=7 +createInternalLoadBalancerElement=7 +listInternalLoadBalancerElements=7 + + +#### Affinity group commands +createAffinityGroup=15 +deleteAffinityGroup=15 +listAffinityGroups=15 +updateVMAffinityGroup=15 +listAffinityGroupTypes=15 + +#### Cisco Vnmc commands +addCiscoVnmcResource=1 +deleteCiscoVnmcResource=1 +listCiscoVnmcResources=1 + +#### Cisco Asa1000v commands +addCiscoAsa1000vResource=1 +deleteCiscoAsa1000vResource=1 +listCiscoAsa1000vResources=1 + +#### portable public IP commands +createPortableIpRange=1 +deletePortableIpRange=1 +listPortableIpRanges=1 + +#### Internal LB VM commands +stopInternalLoadBalancerVM=1 +startInternalLoadBalancerVM=1 +listInternalLoadBalancerVMs=1 + +### Network Isolation methods listing +listNetworkIsolationMethods=1 + +#### Dedicated Resource commands +dedicateZone=1 +dedicatePod=1 +dedicateCluster=1 +dedicateHost=1 +releaseDedicatedZone=1 +releaseDedicatedPod=1 +releaseDedicatedCluster=1 +releaseDedicatedHost=1 +listDedicatedZones=1 +listDedicatedPods=1 +listDedicatedClusters=1 +listDedicatedHosts=1 + +### LDAP +listLdapConfigurations=15 +addLdapConfiguration=3 +deleteLdapConfiguration=3 +listLdapUsers=3 +ldapCreateAccount=3 +importLdapUsers=3 + + +#### juniper-contrail commands +createServiceInstance=1 + +### GloboDNS commands +addGloboDnsHost=1 + +###GloboACLAPI commands +addGloboAclApiHost=1 +createGloboACLRule=1 +listGloboACLRules=1 +removeGloboACLRule=1 + +### OpenDaylight plugin commands +addOpenDaylightController=1 +deleteOpenDaylightController=1 +listOpenDaylightControllers=1 + +### GloboNetwork commands +acquireNewLBIp=15 +addGloboNetworkEnvironment=1 +addGloboNetworkHost=1 +addGloboNetworkLBEnvironment=1 +addGloboNetworkVlan=15 +addNetworkViaGloboNetwork=15 +disassociateIpAddressFromGloboNetwork=15 +generateUrlForEditingVip=15 +importGloboNetworkLoadBalancer=15 +listAllEnvironmentsFromGloboNetwork=15 +listGloboNetworkEnvironments=15 +deleteNetworkInGloboNetwork=15 +listGloboNetworkReals=15 +listGloboNetworkPoolOptions=15 +listGloboNetworkLBEnvironments=15 +listGloboNetworkVips=15 +removeGloboNetworkEnvironment=1 +listAllEnvironmentsFromGloboNetwork=1 +listGloboNetworkCapabilities=15 +removeGloboNetworkLBEnvironment=1 +listGloboNetworkLBCacheGroups=15 +listGloboNetworkPools=15 +listGloboNetworkExpectedHealthchecks=15 +getGloboNetworkPool=15 +updateGloboNetworkPool=15 +createGloboNetworkPool=15 +deleteGloboNetworkPool=15 +listGloboVirtualMachines=15 + +### GloboDictionary commands +listBusinessServices=15 +listClients=15 +listComponents=15 +listSubComponents=15 +listProducts=15 \ No newline at end of file diff --git a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml index 1f70e5261473..c48d7ddd8d78 100644 --- a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml +++ b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml @@ -43,6 +43,13 @@ + + diff --git a/core/test/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java b/core/test/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java index dbbdc8eeb048..908564a6554e 100644 --- a/core/test/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java +++ b/core/test/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java @@ -223,8 +223,8 @@ public void testDeleteIpAlias() { protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() { final List lbs = new ArrayList<>(); final List dests = new ArrayList<>(); - dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); - dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", -1, -1, false)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", -1, -1, true)); lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; diff --git a/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index 200f266b9251..5a7220c3c96f 100644 --- a/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java +++ b/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java @@ -777,8 +777,8 @@ public void testLoadBalancerConfigCommand() { protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand1() { final List lbs = new ArrayList<>(); final List dests = new ArrayList<>(); - dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); - dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); + dests.add(new LbDestination(80, 8080, "10.1.10.2",-1, -1, false)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", -1, -1,true)); lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; lbs.toArray(arrayLbs); @@ -792,8 +792,8 @@ protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand1() { protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand2() { final List lbs = new ArrayList<>(); final List dests = new ArrayList<>(); - dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); - dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", -1, -1,false)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", -1, -1,true)); lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; lbs.toArray(arrayLbs); diff --git a/core/test/com/cloud/network/HAProxyConfiguratorTest.java b/core/test/com/cloud/network/HAProxyConfiguratorTest.java index d899d4db4aef..1f229379d19f 100644 --- a/core/test/com/cloud/network/HAProxyConfiguratorTest.java +++ b/core/test/com/cloud/network/HAProxyConfiguratorTest.java @@ -97,8 +97,8 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() { @Test public void testGenerateConfigurationLoadBalancerProxyProtocolConfigCommand() { final List dests = new ArrayList<>(); - dests.add(new LbDestination(443, 8443, "10.1.10.2", false)); - dests.add(new LbDestination(443, 8443, "10.1.10.2", true)); + dests.add(new LbDestination(443, 8443, "10.1.10.2",-1, -1, false)); + dests.add(new LbDestination(443, 8443, "10.1.10.2",-1, -1, true)); LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 443, "tcp", "http", false, false, false, dests); lb.setLbProtocol("tcp-proxy"); LoadBalancerTO[] lba = new LoadBalancerTO[1]; diff --git a/cs b/cs new file mode 100755 index 000000000000..a3283aea0ff8 --- /dev/null +++ b/cs @@ -0,0 +1,186 @@ +#!/bin/bash + +BASEDIR=$(pwd) +STATIC_TARGET=client/target/classes/META-INF/webapp/scripts + +pkg_path(){ + [ -f /etc/redhat-release ] || return 1 + package=${1} + package_name=$(rpm -qa | grep ${package}) + [[ -z ${package_name} ]] && echo "${1} not found! Please install it before continue!" >&2 && return 1 + echo $(rpm -ql ${package_name} | grep \/bin$ | sed 's/\/bin//g') +} + +[[ -z $M2_HOME ]] && export M2_HOME=$(pkg_path apache-maven) +[[ -z $JAVA_HOME ]] && export JAVA_HOME=$(pkg_path java-1.7.0-openjdk-1.7) +[[ -z $CATALINA_HOME ]] && export CATALINA_HOME=/usr/share/tomcat6/ +export PATH=${PATH}:${M2_HOME}/bin + +gen_version(){ + #ddcs_version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep '^[0-9]\.') + cs_version="4.11.1.0" + tag_version=$(date +%Y%m%d%H%M) + echo "${cs_version}-${tag_version}" +} + +gen_tag(){ + branch=${1} + [[ -z ${branch} ]] && branch='develop' + echo "Changing to branch '${branch}'" + git checkout -q ${branch} + echo "Getting last changes from git..." + git pull -q + tag=$(gen_version) + git tag $tag + remote=$(cat .git/config | awk -F\" '/\[remote/ {print $2}') + git push --tags + echo "RELEASE/TAG: ${tag}" +} + + +gen_package(){ + tag=${1} + REPOPATH=${2} + BUILDDIR="${BASEDIR}/dist/rpmbuild" + + [[ ! -f /etc/redhat-release ]] && echo "Opss... run this option only in RedHat OS. Exiting..." && return 1 + [[ ! -d ${REPOPATH} ]] && echo "The directory ${REPOPATH} does not exist... exiting." && return 1 + + # export some shell environments variables + export MAVEN_OPTS="-XX:MaxPermSize=800m -Xmx2g" + + (cd packaging/centos63; ./package.sh -t ${tag}) + + [[ $? -ne 0 ]] && echo "Failed to compile package. Please, fix errors." && return 1 + + # keep last 3 builds + echo "Removing old packages from yum repo ${REPOPATH}" + rpms='agent awsapi baremetal-agent cli common management usage' + for f in ${rpms}; do rm -f $(ls -1t ${REPOPATH}/cloudstack-${f}* 2>/dev/null | awk 'NR>5 {print}') ; done + + echo -n "Copying files ${BUILDDIR}/RPMS/x86_64/cloudstack-[a-z]*-${tag}.el6.x86_64.rpm to $REPOPATH..." + if mv ${BUILDDIR}/RPMS/x86_64/cloudstack-[a-z]*-${tag}.el6.x86_64.rpm $REPOPATH; then + echo "done" + else + echo -e "\nFailed to copy rpm file" + exit 1 + fi + + # Create yum repo + echo "Creating yum repo..." + whoami=$(whoami) + [[ ${whoami} == 'root' ]] && createrepo -q ${REPOPATH} || sudo createrepo -q ${REPOPATH} + [[ $? -ne 0 ]] && echo "Failed to create yum repo... Please, fix errors." && return 1 +} + +continuos_delivery(){ + # Gen tag + repo_path=${1} + if [ -z "${TAG}" ]; + then + echo "Creating a new tag..." + TAG=$(gen_tag | awk '/RELEASE\/TAG/ {print $2}') + fi + # Build package + echo "Building tag ${TAG}" + gen_package ${TAG} ${repo_path} +} + +case "$1" in + run) + rm -f *.log + MAVEN_OPTS="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" mvn -Dnonoss -Djava.awt.headless=true -pl :cloud-client-ui jetty:run + ;; + run-simulator) + rm -f *.log + export RABBIT_MQ_HOST="localhost" + export RABBIT_MQ_PORT="5672" + export RABBIT_MQ_USER="guest" + export RABBIT_MQ_PASSWORD="guest" + MAVEN_OPTS="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" mvn -Dnonoss -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dsimulator -pl :cloud-client-ui jetty:run + ;; + compile) + mvn -Dnonoss -Pdeveloper,systemvm -Djava.awt.headless=true -Dsimulator clean install -DskipTests + ;; + compile-with-tests) + mvn -Dnonoss -Pdeveloper,systemvm -Djava.awt.headless=true -Dsimulator clean install + ;; + compile-changes) + mvn -Dnonoss -Pdeveloper,systemvm -Djava.awt.headless=true -Dsimulator install -DskipTests + ;; + compile-quick) + mvn -Dnonoss -Pdeveloper,systemvm -Djava.awt.headless=true -Dsimulator -pl :cloud-framework-config,:cloud-utils,:cloud-engine-schema,:cloud-engine,:cloud-server,:cloud-api,:cloud-engine-components-api,:cloud-plugin-network-globonetwork,:cloud-plugin-network-globodns,:cloud-plugin-network-globoaclapi,:cloud-plugin-user-authenticator-oauth2,:cloud-client-ui install -DskipTests + ;; + update-js) + rm -R $STATIC_TARGET/*.js.gz + rm -R $STATIC_TARGET/../css/*.css.gz + rm -R $STATIC_TARGET/loadbalancer/*.js.gz + rm -R $STATIC_TARGET/ui/*.js.gz + rm -R $STATIC_TARGET/ui-custom/*.js.gz + rm -R $STATIC_TARGET/ui/widgets/*.js.gz + rm -R $STATIC_TARGET/../plugins/globoNetworkVipPlugin/*.js.gz + + cp -R ui/scripts $STATIC_TARGET/../ + cp -R ui/css $STATIC_TARGET/../ + cp -R ui/plugins $STATIC_TARGET/../ + + ;; + deploydb) + mvn -Dnonoss -Pdeveloper -pl developer,tools/devcloud -Ddeploydb + ;; + deploydb-simulator) + mvn -Dnonoss -Pdeveloper -pl developer -Ddeploydb + mvn -Dnonoss -Pdeveloper -pl developer -Ddeploydb-simulator + ;; + db-migrate) + [[ -z $WORKON_HOME ]] && WORKON_HOME=$HOME/.virtualenvs + source $WORKON_HOME/cloudstack/bin/activate + (cd setup/dbmigrate && db-migrate --env=localhost) + ;; + tag) + gen_tag ${2} + ;; + gen_version) + gen_version + ;; + package) + [[ $# -ne 3 ]] && echo "Use: $0 package " && exit 1 + tag=$2 + repopath=$3 + gen_package ${tag} ${repopath} + ;; + cd) + [[ $# -ne 2 ]] && echo "Use: $0 cd " && exit 1 + repo_path=$2 + starttime=$(date +%s) + continuos_delivery ${repo_path} + echo "Building package and yum repo in $(($(date +%s)-starttime)) seconds." + ;; + *) + echo "Usage: $0 [action] +Actions: + +RUN + run Run Jetty server in DEBUG mode + run-simulator Run Jetty w/ hypervisor simulator + +COMPILE + compile Clean and Compile entire cloudstack + compile-changes Compile only changes in all cloudstack + compile-quick Compile only globo elements + update-js Update Javascript only + +DB + deploydb Create Required SQL Schema + deploydb-simulator Create Required SQL Schema to use with simulator + db-migrate SQL migrations + + tag Create a git TAG, branch name is optional (develop is default) + package Build RPM packages for cloudstack (management, usage, awsapi, common, etc) and create yum repo + cd tag + package + create yum repo +" + exit 2 + ;; +esac + + diff --git a/engine/components-api/resources/META-INF/cloudstack/core/spring-engine-components-api-core-context.xml b/engine/components-api/resources/META-INF/cloudstack/core/spring-engine-components-api-core-context.xml index b644176565fb..066bf1199880 100644 --- a/engine/components-api/resources/META-INF/cloudstack/core/spring-engine-components-api-core-context.xml +++ b/engine/components-api/resources/META-INF/cloudstack/core/spring-engine-components-api-core-context.xml @@ -27,4 +27,14 @@ http://www.springframework.org/schema/context/spring-context.xsd" > + + + + + + + + + + diff --git a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java index 235b241264c8..4c7e6c63fa1b 100644 --- a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java @@ -19,6 +19,8 @@ import java.util.Map; import java.util.Set; +import org.apache.cloudstack.region.PortableIpRangeVO; + import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -217,6 +219,8 @@ Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetwor String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException; + PortableIpRangeVO createPortableIpRange(Integer regionId, String startIP, String endIP, String gateway, String netmask, String vlanId); + void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException; /** diff --git a/engine/components-api/src/com/cloud/network/IpAddressManager.java b/engine/components-api/src/com/cloud/network/IpAddressManager.java index 256f02666354..906212ae3a9c 100644 --- a/engine/components-api/src/com/cloud/network/IpAddressManager.java +++ b/engine/components-api/src/com/cloud/network/IpAddressManager.java @@ -138,6 +138,10 @@ IPAddressVO associateIPToGuestNetwork(long ipAddrId, long networkId, boolean rel IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; + IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID, String requestedIp) throws ConcurrentOperationException, + ResourceAllocationException, + InsufficientAddressCapacityException; + boolean releasePortableIpAddress(long addrId); IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, 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 945ee9783b53..523b8bb41c23 100644 --- a/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java +++ b/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.lb; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.utils.net.Ip; import java.util.List; import org.apache.cloudstack.context.CallContext; @@ -30,11 +32,13 @@ import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.user.Account; +import com.cloud.vm.Nic; 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, String lbProtocol, Boolean forDisplay) throws NetworkRuleConflictException; + boolean openFirewall, CallContext caller, String lbProtocol, Boolean forDisplay, List additionalPortMap, String cache, + String serviceDownAction, String healthCheckDestination, String expectedHealthcheck, String healthcheckType, boolean forcedns, boolean dsr) throws NetworkRuleConflictException; boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId); @@ -70,4 +74,9 @@ LoadBalancer createPublicLoadBalancer(String xId, String name, String descriptio void removeLBRule(LoadBalancer rule); void isLbServiceSupportedInNetwork(long networkId, Scheme scheme); + Nic getLbInstanceNic(long lbid, long vmid); + + public Ip getSourceIp(LoadBalancer lb); + + LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb); } diff --git a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java index 9626385c0fd1..472d46003659 100644 --- a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -998,7 +998,7 @@ public boolean reconnect(final long hostId) { host = _hostDao.findById(hostId); if (host == null || host.getRemoved() != null) { - s_logger.warn("Unable to find host " + hostId); + s_logger.error("Unable to find host=" + hostId); return false; } @@ -1008,13 +1008,13 @@ public boolean reconnect(final long hostId) { } if (host.getStatus() != Status.Up && host.getStatus() != Status.Alert && host.getStatus() != Status.Rebalancing) { - s_logger.info("Unable to disconnect host because it is not in the correct state: host=" + hostId + "; Status=" + host.getStatus()); + s_logger.error("Unable to disconnect host because it is not in the correct state: host=" + hostId + "; Status=" + host.getStatus()); return false; } final AgentAttache attache = findAttache(hostId); if (attache == null) { - s_logger.info("Unable to disconnect host because it is not connected to this server: " + hostId); + s_logger.error("Unable to disconnect host because it is not connected to this server: host=" + hostId); return false; } diff --git a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java index be4d029a0000..dc5b1bad5ca1 100644 --- a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java +++ b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java @@ -265,7 +265,7 @@ protected void runInContext() { answers.add(answer); if (!answer.getResult() && stopOnError) { if (i < cmds.length - 1 && s_logger.isDebugEnabled()) { - s_logger.debug(log(seq, "Cancelling because one of the answers is false and it is stop on error.")); + s_logger.warn(log(seq, "Cancelling because one of the answers is false and it is stop on error.")); } break; } @@ -322,13 +322,13 @@ protected void runInContext() { } } catch (Throwable t) { // Catch Throwable as all exceptions will otherwise be eaten by the executor framework - s_logger.warn(log(seq, "Throwable caught while executing command"), t); + s_logger.error(log(seq, "Throwable caught while executing command"), t); answer = new Answer(cmds[i], false, t.toString()); } answers.add(answer); if (!answer.getResult() && stopOnError) { if (i < cmds.length - 1 && s_logger.isDebugEnabled()) { - s_logger.debug(log(seq, "Cancelling because one of the answers is false and it is stop on error.")); + s_logger.warn(log(seq, "Cancelling because one of the answers is false and it is stop on error.")); } break; } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index b5aca5d9f10e..31ba6df23e00 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2703,6 +2703,24 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { return success; } + // Make sure that there are no user vms in the network that are not Expunged/Error before delete network, else throws CloudRuntimeException + public void validateUserVMsInNetwork(Long networkId) throws CloudRuntimeException { + List userVms = _userVmDao.listByNetworkIdAndStates(networkId); + + boolean error = false; + StringBuilder builder = new StringBuilder(""); + for (UserVmVO vm : userVms) { + if (!(vm.getState() == VirtualMachine.State.Expunging && vm.getRemoved() != null)) { + error = true; + builder.append("Vm " + vm.getDisplayName() + " is in " + vm.getState() + " state. "); + } + } + if (error) { + String msg = "Can't delete the network, not all user vms are expunged. " + builder.toString(); + s_logger.warn(msg); + throw new CloudRuntimeException(msg); + } + } @Override public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) { 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 e5e0bbfed595..3a653675ad00 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -743,11 +743,7 @@ public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering // Create event and update resource count for volumes if vm is a user vm if (vm.getType() == VirtualMachine.Type.User) { - Long offeringId = null; - - if (offering.getType() == DiskOffering.Type.Disk) { - offeringId = offering.getId(); - } + Long offeringId = offering.getId(); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, vol.getTemplateId(), size, Volume.class.getName(), vol.getUuid(), vol.isDisplayVolume()); diff --git a/engine/orchestration/test/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/test/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index b0283f35c1b3..40e1c22b937d 100644 --- a/engine/orchestration/test/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/test/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -22,6 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.utils.exception.CloudRuntimeException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -49,15 +52,18 @@ import com.cloud.network.guru.NetworkGuru; import com.cloud.vm.Nic; import com.cloud.vm.NicVO; +import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicSecondaryIpDao; - +import com.cloud.vm.dao.UserVmDaoImpl; import junit.framework.TestCase; +import java.util.Date; + /** * NetworkManagerImpl implements NetworkManager. */ @@ -174,6 +180,54 @@ public void testDontRemoveDhcpServiceWhenNotProvided() { } @Test + public void testValidateUserVMsInNetwork() { + List list = new ArrayList(); + list.add(newUserVm(1l, "vm 01", State.Expunging, new Date())); + + UserVmDaoImpl dao = mock(UserVmDaoImpl.class); + when(dao.listByNetworkIdAndStates(1l)).thenReturn(list); + + testOrchastrator._userVmDao = dao; + testOrchastrator.validateUserVMsInNetwork(1l); + + } + + @Test + public void testValidateUserVMsInNetwork_fail() { + List list = new ArrayList(); + list.add(newUserVm(123l, "vm-0123", State.Stopped, null)); + list.add(newUserVm(555l, "vm-555", State.Running, null)); + + try { + UserVmDaoImpl dao = mock(UserVmDaoImpl.class); + when(dao.listByNetworkIdAndStates(1l)).thenReturn(list); + + testOrchastrator._userVmDao = dao; + testOrchastrator.validateUserVMsInNetwork(1l); + } catch (CloudRuntimeException e ) { + assertTrue(e.getMessage().contains("vm-0123")); + assertTrue(e.getMessage().contains("vm-555")); + } catch (Exception e) { + fail("should be CloudRuntimeException with vm name. " + e.getClass()+" "+ e.getMessage()); + } + + } + + + public UserVm newUserVm(Long id, String name, State state, final Date removedTemp) { + UserVmVO vm = new UserVmVO(id, name, null, 0l, null, + 0l, false, false, 0l, 0l, + 0l, 1l, null, null, 0l) { + public Date getRemoved() { + return removedTemp; + } + }; + + vm.setState(state); + + + return vm; + } public void testCheckL2OfferingServicesEmptyServices() { when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(new ArrayList<>()); when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false); 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 84c27583925b..9a84f450c8df 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 @@ -128,6 +128,7 @@ + @@ -198,6 +199,9 @@ + + + diff --git a/engine/schema/resources/META-INF/db/create-default-role-api-mappings.sql b/engine/schema/resources/META-INF/db/create-default-role-api-mappings.sql index 57601fbde17f..a8eb73380f87 100644 --- a/engine/schema/resources/META-INF/db/create-default-role-api-mappings.sql +++ b/engine/schema/resources/META-INF/db/create-default-role-api-mappings.sql @@ -900,3 +900,7 @@ INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'updateVpnGateway', 'ALLOW', 257) ON DUPLICATE KEY UPDATE rule=rule; INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'uploadSslCert', 'ALLOW', 258) ON DUPLICATE KEY UPDATE rule=rule; INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'uploadVolume', 'ALLOW', 259) ON DUPLICATE KEY UPDATE rule=rule; + +INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'listconfigurations', 'ALLOW', 260) ON DUPLICATE KEY UPDATE rule=rule; +INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'listIdps', 'ALLOW', 261) ON DUPLICATE KEY UPDATE rule=rule; +INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'listZones', 'ALLOW', 262) ON DUPLICATE KEY UPDATE rule=rule; diff --git a/engine/schema/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java b/engine/schema/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java index b4d2f6f785f3..7c3ee527ad07 100644 --- a/engine/schema/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java +++ b/engine/schema/src/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java @@ -99,6 +99,12 @@ public Integer getMaxHostsPerCluster(HypervisorType hypervisorType, String hyper @Override public Boolean isVmSnapshotEnabled(HypervisorType hypervisorType, String hypervisorVersion) { HypervisorCapabilitiesVO result = getCapabilities(hypervisorType, hypervisorVersion); - return result.getVmSnapshotEnabled(); + // if default capability profile not present for any hypervisor type result will be null. + // So returning vm snapshot not supported if there is no default capability profile for hypervisor. + if (result != null) { + return result.getVmSnapshotEnabled(); + } else { + return false; + } } } diff --git a/engine/schema/src/com/cloud/network/as/AutoScalePolicyVO.java b/engine/schema/src/com/cloud/network/as/AutoScalePolicyVO.java index 1842533000fd..6622b1d913d9 100644 --- a/engine/schema/src/com/cloud/network/as/AutoScalePolicyVO.java +++ b/engine/schema/src/com/cloud/network/as/AutoScalePolicyVO.java @@ -21,6 +21,8 @@ import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -63,9 +65,16 @@ public class AutoScalePolicyVO implements AutoScalePolicy, InternalIdentity { @Temporal(value = TemporalType.TIMESTAMP) private Date lastQuiteTime; + @Column(name = "logical_operator", updatable = true, nullable = true) + @Enumerated(value = EnumType.STRING) + private LogicalOperator logicalOperator = LogicalOperator.AND; + @Column(name = "action", updatable = false, nullable = false) private String action; + @Column(name = "step", updatable = true, nullable = false) + private Integer step = 1; + @Column(name = GenericDao.REMOVED_COLUMN) protected Date removed; @@ -76,7 +85,7 @@ public AutoScalePolicyVO() { } public AutoScalePolicyVO(long domainId, long accountId, int duration, - int quietTime, Date lastQuiteTime, String action) { + int quietTime, Date lastQuiteTime, String action, Integer step, LogicalOperator logicalOperator) { uuid = UUID.randomUUID().toString(); this.domainId = domainId; this.accountId = accountId; @@ -84,6 +93,8 @@ public AutoScalePolicyVO(long domainId, long accountId, int duration, this.quietTime = quietTime; this.lastQuiteTime = lastQuiteTime; this.action = action; + this.step = step; + this.logicalOperator = logicalOperator; } @Override @@ -131,6 +142,19 @@ public String getAction() { return action; } + @Override + public LogicalOperator getLogicalOperator() { + if(logicalOperator == null){ + return LogicalOperator.AND; + } + return logicalOperator; + } + + @Override + public Integer getStep() { + return step; + } + public Date getRemoved() { return removed; } @@ -151,6 +175,14 @@ public void setLastQuiteTime(Date lastQuiteTime) { this.lastQuiteTime = lastQuiteTime; } + public void setStep(Integer step) { + this.step = step; + } + + public void setLogicalOperator(LogicalOperator logicalOperator) { + this.logicalOperator = logicalOperator; + } + @Override public Class getEntityType() { return AutoScalePolicy.class; diff --git a/engine/schema/src/com/cloud/network/as/AutoScaleVmGroupVO.java b/engine/schema/src/com/cloud/network/as/AutoScaleVmGroupVO.java index d32e7f873a0b..bbcbe7c24445 100644 --- a/engine/schema/src/com/cloud/network/as/AutoScaleVmGroupVO.java +++ b/engine/schema/src/com/cloud/network/as/AutoScaleVmGroupVO.java @@ -90,12 +90,18 @@ public class AutoScaleVmGroupVO implements AutoScaleVmGroup, InternalIdentity { @Column(name = "display", updatable = true, nullable = false) protected boolean display = true; + @Column(name = "locked", updatable = true, nullable = false) + protected boolean locked = false; + + @Column(name = "vm_prefix_name") + private String vmPrefixName; + public AutoScaleVmGroupVO() { } public AutoScaleVmGroupVO(long lbRuleId, long zoneId, long domainId, long accountId, int minMembers, int maxMembers, int memberPort, - int interval, Date lastInterval, long profileId, String state) { + int interval, Date lastInterval, long profileId, String state,String vmPrefixName) { uuid = UUID.randomUUID().toString(); loadBalancerId = lbRuleId; @@ -109,6 +115,7 @@ public AutoScaleVmGroupVO(long lbRuleId, long zoneId, long domainId, this.state = state; this.interval = interval; this.lastInterval = lastInterval; + this.vmPrefixName = vmPrefixName; } @Override @@ -225,8 +232,26 @@ public boolean isDisplay() { return display; } + @Override + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + @Override public Class getEntityType() { return AutoScaleVmGroup.class; } + + public void setVmPrefixName(String vmPrefixName){ + this.vmPrefixName = vmPrefixName; + } + + @Override + public String getVmPrefixName() { + return this.vmPrefixName; + } } diff --git a/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileNetworkMapVO.java b/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileNetworkMapVO.java new file mode 100644 index 000000000000..4a3502916d43 --- /dev/null +++ b/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileNetworkMapVO.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 com.cloud.network.as; + +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; + +@Entity +@Table(name = ("autoscale_vmprofile_network_map")) +public class AutoScaleVmProfileNetworkMapVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "profile_id") + private long profileId; + + @Column(name = "network_id") + private long networkId; + + public AutoScaleVmProfileNetworkMapVO() { + } + + public AutoScaleVmProfileNetworkMapVO(long profileId, long conditionId) { + this.profileId = profileId; + this.networkId = conditionId; + } + + @Override + public long getId() { + return id; + } + + public long getProfileId() { + return profileId; + } + + public long getNetworkId() { + return networkId; + } +} diff --git a/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileVO.java b/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileVO.java index 69e4c8190f4f..3e8e10eda83a 100644 --- a/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileVO.java +++ b/engine/schema/src/com/cloud/network/as/AutoScaleVmProfileVO.java @@ -80,6 +80,9 @@ public class AutoScaleVmProfileVO implements AutoScaleVmProfile, Identity, Inter @Column(name = "counter_params", updatable = true) private String counterParams; + @Column(name = "user_data", updatable = true, length = 32768) + private String userData; + @Column(name = GenericDao.REMOVED_COLUMN) protected Date removed; @@ -93,7 +96,7 @@ public AutoScaleVmProfileVO() { } public AutoScaleVmProfileVO(long zoneId, long domainId, long accountId, long serviceOfferingId, long templateId, String otherDeployParams, Map counterParamList, - Integer destroyVmGraceperiod, long autoscaleUserId) { + Integer destroyVmGraceperiod, long autoscaleUserId, String userData) { uuid = UUID.randomUUID().toString(); this.zoneId = zoneId; this.domainId = domainId; @@ -102,6 +105,7 @@ public AutoScaleVmProfileVO(long zoneId, long domainId, long accountId, long ser this.templateId = templateId; this.otherDeployParams = otherDeployParams; this.autoscaleUserId = autoscaleUserId; + this.userData = userData; if (destroyVmGraceperiod != null) { this.destroyVmGraceperiod = destroyVmGraceperiod; } @@ -206,6 +210,11 @@ public long getId() { return id; } + @Override + public String getUserData() { + return userData; + } + @Override public Integer getDestroyVmGraceperiod() { return destroyVmGraceperiod; @@ -228,6 +237,10 @@ public void setDisplay(boolean display) { this.display = display; } + public void setUserData(String userData) { + this.userData = userData; + } + @Override public boolean isDisplay() { return display; diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDao.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDao.java index 16d110410288..22b1aeac4e0e 100644 --- a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDao.java +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDao.java @@ -22,8 +22,13 @@ import com.cloud.utils.db.GenericDao; public interface AutoScaleVmGroupDao extends GenericDao { + List listByAll(Long loadBalancerId, Long profileId); + List listAllNotLocked(); + + List listAllEnabled(); + boolean isProfileInUse(long profileId); boolean isAutoScaleLoadBalancer(Long loadBalancerId); diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDaoImpl.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDaoImpl.java index eb4716529c9c..9e840ae9d64b 100644 --- a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDaoImpl.java +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupDaoImpl.java @@ -43,6 +43,20 @@ public List listByAll(Long loadBalancerId, Long profileId) { return listBy(sc); } + @Override + public List listAllNotLocked() { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("locked", SearchCriteria.Op.EQ, false); + return listBy(sc); + } + + @Override + public List listAllEnabled() { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("state", SearchCriteria.Op.EQ, AutoScaleVmGroupVO.State_Enabled); + return listBy(sc); + } + @Override public boolean isProfileInUse(long profileId) { SearchCriteria sc = createSearchCriteria(); diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java index 1a8acf3c00a8..e8e304d795e6 100644 --- a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java @@ -26,6 +26,8 @@ public interface AutoScaleVmGroupVmMapDao extends GenericDao listByGroup(long vmGroupId); + public AutoScaleVmGroupVmMapVO findByVmId(long vmGroupId); + public int remove(long vmGroupId, long vmId); } diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java index 24a3c1013cfe..760bbadc37df 100644 --- a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java @@ -43,6 +43,13 @@ public List listByGroup(long vmGroupId) { return listBy(sc); } + @Override + public AutoScaleVmGroupVmMapVO findByVmId(long vmId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, vmId); + return findOneBy(sc); + } + @Override public int remove(long vmGroupId, long vmId) { SearchCriteria sc = createSearchCriteria(); diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDao.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDao.java new file mode 100644 index 000000000000..03e176b99087 --- /dev/null +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.as.dao; + +import com.cloud.network.as.AutoScaleVmProfileNetworkMapVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface AutoScaleVmProfileNetworkMapDao extends GenericDao { + + boolean removeByVmProfileId(long vmProfileId); + + List listByVmProfileId(long vmProfileId); + +} diff --git a/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDaoImpl.java b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDaoImpl.java new file mode 100644 index 000000000000..e055eeb29908 --- /dev/null +++ b/engine/schema/src/com/cloud/network/as/dao/AutoScaleVmProfileNetworkMapDaoImpl.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.as.dao; + +import com.cloud.network.as.AutoScaleVmProfileNetworkMapVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class AutoScaleVmProfileNetworkMapDaoImpl extends GenericDaoBase implements AutoScaleVmProfileNetworkMapDao { + + @Override + public boolean removeByVmProfileId(long vmProfileId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("profileId", SearchCriteria.Op.EQ, vmProfileId); + return expunge(sc) > 0; + } + + @Override + public List listByVmProfileId(long vmProfileId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("profileId", SearchCriteria.Op.EQ, vmProfileId); + return listBy(sc); + } +} diff --git a/engine/schema/src/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/com/cloud/network/dao/IPAddressDaoImpl.java index 43345b916fed..76aaa25481c8 100644 --- a/engine/schema/src/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/IPAddressDaoImpl.java @@ -153,6 +153,9 @@ public void init() { DeleteAllExceptGivenIp.and("ip", DeleteAllExceptGivenIp.entity().getAddress(), Op.NEQ); } + public IPAddressVO persist(final IPAddressVO entity) { + return super.persist(entity); + } @Override public boolean mark(long dcId, Ip ip) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerDao.java index c639ef786325..5dff6a4974a1 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerDao.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerDao.java @@ -29,6 +29,11 @@ public interface LoadBalancerDao extends GenericDao { List listInTransitionStateByNetworkIdAndScheme(long networkId, Scheme scheme); + /* + @param lbUUId - lb uuid that will be linked will others, this will exclude from result list + */ + List listLinkables(String lbUUId, long networkEnvId, long accountId); + boolean isLoadBalancerRulesMappedToVmGuestIp(long instanceId, String instanceIp, long networkId); } diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerDaoImpl.java index 932c5c68a2d2..eb65983f2d19 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerDaoImpl.java @@ -16,8 +16,15 @@ // under the License. package com.cloud.network.dao; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; import java.util.List; +import org.apache.log4j.Logger; + import javax.inject.Inject; import com.cloud.network.rules.FirewallRule; @@ -33,6 +40,8 @@ @Component public class LoadBalancerDaoImpl extends GenericDaoBase implements LoadBalancerDao { + public static final Logger s_logger = Logger.getLogger(LoadBalancerDaoImpl.class); + private final SearchBuilder ListByIp; protected final SearchBuilder TransitionStateSearch; @@ -113,6 +122,37 @@ public boolean isLoadBalancerRulesMappedToVmGuestIp(long instanceId, String inst return false; } + @Override + public List listLinkables(String uuid, long networkEnvId, long accountId) { + try { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + String sql = "SELECT f.uuid, l.name, ref.globonetwork_environment_id, f.* FROM firewall_rules f " + + " INNER JOIN load_balancing_rules l on f.id = l.id " + + " INNER JOIN globonetwork_network_ref ref on f.network_id = ref.network_id " + + " WHERE f.uuid != ? " + + " AND f.state in ('Active', 'Add') " + + " AND ref.globonetwork_environment_id = ? " + + " AND f.account_id = ?; "; + PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setString(1, uuid); + pstmt.setLong(2, networkEnvId); + pstmt.setLong(3, accountId); + + List result = new ArrayList<>(); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setUuid(rs.getString(1)); + lb.setName(rs.getString(2)); + result.add(lb); + } + return result; + } catch (Exception e) { + s_logger.error("Error trying list linkable load balancers, uuid: " + uuid + ", networkEnvId: " + networkEnvId + ", accountId: ", e); + throw new CloudRuntimeException("Unexpected error during list linkable load balalncers.", e); + } + } } diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDao.java new file mode 100644 index 000000000000..1bf31da5074b --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDao.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; + +public interface LoadBalancerNetworkMapDao extends GenericDao { + void removeByLoadBalancer(long loadBalancerId); + List listByLoadBalancerId(long loadBalancerId); + LoadBalancerNetworkMapVO findByLoadBalancerIdAndNetworkId(long loadBalancerId, long networkId); + List listByNetworkId(long networkId); +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDaoImpl.java new file mode 100644 index 000000000000..18b8d14014ed --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapDaoImpl.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 java.util.List; + +import org.springframework.stereotype.Component; + +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; + +@Component +public class LoadBalancerNetworkMapDaoImpl extends GenericDaoBase implements LoadBalancerNetworkMapDao { + + final SearchBuilder allFieldsSearch; + + public LoadBalancerNetworkMapDaoImpl() { + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("loadbalancerId", allFieldsSearch.entity().getLoadBalancerId(), Op.EQ); + allFieldsSearch.and("networkid", allFieldsSearch.entity().getNetworkId(), Op.EQ); + allFieldsSearch.done(); + } + + @Override + public void removeByLoadBalancer(long loadBalancerId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("loadbalancerId", loadBalancerId); + expunge(sc); + } + + @Override + public List listByLoadBalancerId(long loadBalancerId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("loadbalancerId", loadBalancerId); + return listBy(sc); + } + + @Override + public LoadBalancerNetworkMapVO findByLoadBalancerIdAndNetworkId(long loadBalancerId, long networkId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("loadbalancerId", loadBalancerId); + sc.setParameters("networkid", networkId); + return findOneBy(sc); + } + + @Override + public List listByNetworkId(long networkId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("networkid", networkId); + return listBy(sc); + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapVO.java new file mode 100644 index 000000000000..dc83cd570f99 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerNetworkMapVO.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name=("load_balancer_network_map")) +public class LoadBalancerNetworkMapVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="load_balancer_id") + private long loadBalancerId; + + @Column(name="network_id") + private long networkId; + + public LoadBalancerNetworkMapVO() { + } + + public LoadBalancerNetworkMapVO(long loadBalancerId, long networkId) { + this.loadBalancerId = loadBalancerId; + this.networkId = networkId; + } + + public long getId() { + return id; + } + + public long getLoadBalancerId() { + return loadBalancerId; + } + + public long getNetworkId() { + return networkId; + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDao.java new file mode 100644 index 000000000000..6d9b980f6492 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface LoadBalancerOptionsDao extends GenericDao { + List listByLoadBalancerId(long loadBalancerId); +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDaoImpl.java new file mode 100644 index 000000000000..6d11c1ed97df --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsDaoImpl.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.utils.db.DB; +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 org.springframework.stereotype.Component; + + +import java.util.List; + +@Component +@DB +public class LoadBalancerOptionsDaoImpl extends GenericDaoBase implements LoadBalancerOptionsDao { + + final SearchBuilder LoadBalancerSearch; + + protected LoadBalancerOptionsDaoImpl() { + super(); + + LoadBalancerSearch = createSearchBuilder(); + LoadBalancerSearch.and("lbId", LoadBalancerSearch.entity().getLoadBalancerId(), Op.EQ); + LoadBalancerSearch.done(); + } + + @Override + public List listByLoadBalancerId(long loadBalancerId) { + SearchCriteria sc = LoadBalancerSearch.create(); + sc.setParameters("lbId", loadBalancerId); + return listBy(sc); + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsVO.java new file mode 100644 index 000000000000..531185fcbde3 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerOptionsVO.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.network.dao; + +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; + +@Entity +@Table(name=("load_balancer_options")) +public class LoadBalancerOptionsVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="lb_id") + private long lbId; + + @Column(name="cache") + private String cache; + + @Column(name="service_down_action") + private String serviceDownAction; + + @Column(name="health_check_destination") + private String healthCheckDestination; + + @Column(name="health_check_type") + private String healthCheckType; + + @Column(name="expected_health_check") + private String expectedHealthCheck; + + public LoadBalancerOptionsVO() { + } + + public LoadBalancerOptionsVO(long lbId, String cache, String serviceDownAction, String healthCheckDestination) { + this.lbId = lbId; + this.cache = cache; + this.serviceDownAction = serviceDownAction; + this.healthCheckDestination = healthCheckDestination; + } + + + public String getHealthCheckType() { + return healthCheckType; + } + + public void setHealthCheckType(String healthCheckType) { + this.healthCheckType = healthCheckType; + } + + public String getExpectedHealthCheck() { + return expectedHealthCheck; + } + + public void setExpectedHealthCheck(String expectedHealthCheck) { + this.expectedHealthCheck = expectedHealthCheck; + } + + @Override + public long getId() { + return id; + } + + public long getLoadBalancerId() { + return lbId; + } + + public String getCache() { return cache; } + + public String getServiceDownAction() { + return serviceDownAction; + } + + public String getHealthCheckDestination() { + return healthCheckDestination; + } + + public void setHealthCheckDestination(String healthCheckDestination) { + this.healthCheckDestination = healthCheckDestination; + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDao.java new file mode 100644 index 000000000000..c4156488d4c6 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDao.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface LoadBalancerPortMapDao extends GenericDao { + List listByLoadBalancerId(long loadBalancerId); +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDaoImpl.java new file mode 100644 index 000000000000..99e8a8ee6212 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapDaoImpl.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@DB +public class LoadBalancerPortMapDaoImpl extends GenericDaoBase implements LoadBalancerPortMapDao { + + @Override + public List listByLoadBalancerId(long loadBalancerId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("lbId", SearchCriteria.Op.EQ, loadBalancerId); + return listBy(sc); + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapVO.java new file mode 100644 index 000000000000..5f6c0b9e0166 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerPortMapVO.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 com.cloud.network.dao; + +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; + +@Entity +@Table(name=("load_balancer_port_map")) +public class LoadBalancerPortMapVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id; + + @Column(name="lb_id") + private long lbId; + + @Column(name="public_port") + private int publicPort; + + @Column(name="private_port") + private int privatePort; + + public LoadBalancerPortMapVO() { + } + + public LoadBalancerPortMapVO(long lbId, int publicPort, int privatePort) { + this.lbId = lbId; + this.publicPort = publicPort; + this.privatePort = privatePort; + } + + @Override + public long getId() { + return id; + } + + public long getLoadBalancerId() { + return lbId; + } + + public int getPublicPort() { + return publicPort; + } + + public int getPrivatePort() { + return privatePort; + } + + public void setLbId(long lbId) { + this.lbId = lbId; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java index 865e7d2c365e..2ba32fde9550 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java @@ -28,6 +28,8 @@ import com.cloud.network.rules.LoadBalancer; import com.cloud.utils.net.NetUtils; +import java.util.Objects; + /** * This VO represent Public Load Balancer * It references source ip address by its Id. @@ -127,4 +129,32 @@ public void setDescription(String description) { public Scheme getScheme() { return scheme; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LoadBalancerVO that = (LoadBalancerVO) o; + return defaultPortStart == that.defaultPortStart && + defaultPortEnd == that.defaultPortEnd && + Objects.equals(name, that.name) && + Objects.equals(description, that.description) && + Objects.equals(algorithm, that.algorithm) && + scheme == that.scheme && + Objects.equals(lbProtocol, that.lbProtocol); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, algorithm, defaultPortStart, defaultPortEnd, scheme, lbProtocol); + } + + + public void setDefaultPortStart(int defaultPortStart) { + this.defaultPortStart = defaultPortStart; + } + + public void setDefaultPortEnd(int defaultPortEnd) { + this.defaultPortEnd = defaultPortEnd; + } } diff --git a/engine/schema/src/com/cloud/network/rules/FirewallRuleVO.java b/engine/schema/src/com/cloud/network/rules/FirewallRuleVO.java index 282fa7403e73..97beec6c7792 100644 --- a/engine/schema/src/com/cloud/network/rules/FirewallRuleVO.java +++ b/engine/schema/src/com/cloud/network/rules/FirewallRuleVO.java @@ -308,4 +308,20 @@ public boolean isDisplay() { public Class getEntityType() { return FirewallRule.class; } + + public void setId(long id) { + this.id = id; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public void setSourcePortStart(Integer sourcePortStart) { + this.sourcePortStart = sourcePortStart; + } + + public void setSourcePortEnd(Integer sourcePortEnd) { + this.sourcePortEnd = sourcePortEnd; + } } diff --git a/engine/schema/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java b/engine/schema/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java index b89d04ad15a0..adfa19cb7abc 100644 --- a/engine/schema/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java +++ b/engine/schema/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java @@ -47,4 +47,4 @@ public interface PortForwardingRulesDao extends GenericDao listByNetworkAndDestIpAddr(String ip4Address, long networkId); -} +} \ No newline at end of file diff --git a/engine/schema/src/com/cloud/projects/ProjectVO.java b/engine/schema/src/com/cloud/projects/ProjectVO.java index 77eed40d7bad..e5510c0c1f46 100644 --- a/engine/schema/src/com/cloud/projects/ProjectVO.java +++ b/engine/schema/src/com/cloud/projects/ProjectVO.java @@ -54,6 +54,24 @@ public class ProjectVO implements Project, Identity, InternalIdentity { @Column(name = "project_account_id") long projectAccountId; + @Column(name = "business_service_id") + private String businessServiceId; + + @Column(name = "client_id") + private String clientId; + + @Column(name = "component_id") + private String componentId; + + @Column(name = "sub_component_id") + private String subComponentId; + + @Column(name = "product_id") + private String productId; + + @Column(name = "detailed_usage") + private boolean detailedUsage; + @Column(name = "state") @Enumerated(value = EnumType.STRING) private State state; @@ -80,6 +98,16 @@ public ProjectVO(String name, String displayText, long domainId, long projectAcc this.uuid = UUID.randomUUID().toString(); } + public ProjectVO(String name, String displayText, long domainId, long projectAccountId, String businessServiceId, String clientId, String componentId, String subComponentId, String productId, Boolean detailedUsage) { + this(name, displayText, domainId, projectAccountId); + this.businessServiceId = businessServiceId; + this.clientId = clientId; + this.componentId = componentId; + this.subComponentId = subComponentId; + this.productId = productId; + this.detailedUsage = detailedUsage; + } + @Override public String getName() { return name; @@ -139,6 +167,54 @@ public long getProjectAccountId() { return projectAccountId; } + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getBusinessServiceId() { + return businessServiceId; + } + + public void setBusinessServiceId(String businessServiceId) { + this.businessServiceId = businessServiceId; + } + + public String getComponentId() { + return componentId; + } + + public void setComponentId(String componentId) { + this.componentId = componentId; + } + + public String getSubComponentId() { + return subComponentId; + } + + public void setSubComponentId(String subComponentId) { + this.subComponentId = subComponentId; + } + + public Boolean isDetailedUsage() { + return detailedUsage; + } + + public void setDetailedUsage(Boolean detailedUsage) { + this.detailedUsage = detailedUsage; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + public void setName(String name) { this.name = name; } diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java index aae61a120943..debbc9c5553c 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java @@ -16,10 +16,10 @@ // under the License. package com.cloud.service.dao; +import com.cloud.service.ServiceOfferingVO; import java.util.List; import java.util.Map; -import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.db.GenericDao; import com.cloud.vm.VirtualMachine; diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java index f54c55863ca9..78bda9f55fee 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -24,6 +24,7 @@ import javax.inject.Inject; import javax.persistence.EntityExistsException; +import com.cloud.vm.VirtualMachine; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -36,7 +37,6 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.UserVmDetailsDao; @Component diff --git a/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java b/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java index bacb09b98793..0b58e497564e 100644 --- a/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java +++ b/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java @@ -60,4 +60,6 @@ public interface ResourceTagDao extends GenericDao { void removeByResourceIdAndKey(long resourceId, ResourceObjectType resourceType, String key); List listByResourceUuid(String resourceUuid); + + ResourceTag findByResourceIdAndResourceTypeAndKey(long resourceId, ResourceObjectType resourceType, String key); } diff --git a/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java index cc9d99e6ab16..a82dcc94b2bc 100644 --- a/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java +++ b/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java @@ -120,4 +120,13 @@ public List listByResourceUuid(String resourceUuid) { sc.setParameters("resourceUuid", resourceUuid); return listBy(sc); } + + @Override + public ResourceTag findByResourceIdAndResourceTypeAndKey(long resourceId, ResourceObjectType resourceType, String key) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key); + return findOneBy(sc); + } } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java index eb0492cd288b..706d9a8853f3 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade302to40.java @@ -520,6 +520,7 @@ private void addVpcProvider(Connection conn) { pstmt.setLong(1, pNtwkId); pstmt.executeUpdate(); + pstmt.close(); //get provider id pstmt = conn.prepareStatement("SELECT id FROM `cloud`.`physical_network_service_providers` " diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade430to431.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade430to431.java new file mode 100644 index 000000000000..9d41f1aeb51a --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade430to431.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 com.cloud.upgrade.dao; + +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.log4j.Logger; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class Upgrade430to431 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade430to431.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.3.0", "4.3.1"}; + } + + @Override + public String getUpgradedVersion() { + return "4.3.1"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + return null; + } + + @Override + public void performDataMigration(Connection conn) { + updateVlanUris(conn); + } + + private void updateVlanUris(Connection conn) { + s_logger.debug("updating vlan URIs"); + CloudRuntimeException thrown = null; + PreparedStatement selectstatement = null; + ResultSet results = null; + try{ + selectstatement = conn.prepareStatement("SELECT id, vlan_id FROM `cloud`.`vlan` where vlan_id not like '%:%'"); + results = selectstatement.executeQuery(); + + while (results.next()) { + long id = results.getLong(1); + String vlan = results.getString(2); + if (vlan == null || "".equals(vlan)) { + continue; + } + String vlanUri = BroadcastDomainType.Vlan.toUri(vlan).toString(); + PreparedStatement updatestatement = conn.prepareStatement("update `cloud`.`vlan` set vlan_id=? where id=?"); + try { + updatestatement.setString(1, vlanUri); + updatestatement.setLong(2, id); + updatestatement.executeUpdate(); + } catch (SQLException e) { + thrown = new CloudRuntimeException("Unable to update vlan URI " + vlanUri + " for vlan record " + id, e); + } finally { + try { + updatestatement.close(); + } catch (Exception e) { + if(thrown == null) { + thrown = new CloudRuntimeException("Unable to close update statement vlan URI " + vlanUri + " for vlan record " + id, e); + } //else don't obfuscate the original exception + } + } + } + } catch (SQLException e) { + if(thrown == null) { + thrown = new CloudRuntimeException("Unable to update vlan URIs ", e); + } //else don't obfuscate the original exception + } + finally + { + try { + if(results != null) + results.close(); + } catch (SQLException e) { + if(thrown == null) { + thrown = new CloudRuntimeException("Unable to update vlan URIs ", e); + } //else don't obfuscate the original exception + } + try { + if (selectstatement != null) + selectstatement.close(); + } catch (SQLException e) { + if(thrown == null) { + thrown = new CloudRuntimeException("Unable to update vlan URIs ", e); + } //else don't obfuscate the original exception + } + } + if (thrown != null) { + throw thrown; + } + s_logger.debug("Done updateing vlan URIs"); + } + + @Override + public InputStream[] getCleanupScripts() { + return null; + } + +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~4.5.2 b/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~4.5.2 new file mode 100644 index 000000000000..98b52ac0aa96 --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~4.5.2 @@ -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 com.cloud.upgrade.dao; + +import org.apache.log4j.Logger; + +public class Upgrade431to440 extends Upgrade430to440 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade431to440.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.3.1", "4.4.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.4.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~HEAD b/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~HEAD new file mode 100644 index 000000000000..98b52ac0aa96 --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade431to440.java~HEAD @@ -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 com.cloud.upgrade.dao; + +import org.apache.log4j.Logger; + +public class Upgrade431to440 extends Upgrade430to440 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade431to440.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.3.1", "4.4.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.4.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~4.5.2 b/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~4.5.2 new file mode 100644 index 000000000000..ded0db474d5e --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~4.5.2 @@ -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 com.cloud.upgrade.dao; + +import org.apache.log4j.Logger; + +public class Upgrade432to440 extends Upgrade431to440 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade432to440.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.3.2", "4.4.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.4.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~HEAD b/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~HEAD new file mode 100644 index 000000000000..ded0db474d5e --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade432to440.java~HEAD @@ -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 com.cloud.upgrade.dao; + +import org.apache.log4j.Logger; + +public class Upgrade432to440 extends Upgrade431to440 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade432to440.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.3.2", "4.4.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.4.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } +} diff --git a/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java b/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java index 278c80d32475..7db3f594cb55 100644 --- a/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java +++ b/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java @@ -41,8 +41,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.StorageCacheManager; import org.apache.cloudstack.framework.async.AsyncCallFuture; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index d2979f7415dd..82b00e515ba2 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -1457,10 +1457,21 @@ protected Void copyVolumeCallBack(AsyncCallbackDispatcher destroyFuture = expungeVolumeAsync(destVolume); destroyFuture.get(); future.complete(res); + + if (canVolumeBeRemoved(destVolume.getId())) { + volDao.remove(destVolume.getId()); + } + return null; } srcVolume.processEvent(Event.OperationSuccessed); diff --git a/framework/config/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml b/framework/config/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml index be116fc5e775..84c17e3c2c51 100644 --- a/framework/config/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml +++ b/framework/config/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml @@ -36,6 +36,9 @@ + + { + Map getConfiguration(long resourceId); + + List getConfiguration(GloboResourceType resourceType, String resourceUuid); + + List getConfiguration(GloboResourceType resourceType, String resourceUuid, GloboResourceKey key); + + public GloboResourceConfigurationVO getFirst(GloboResourceType resourceType, + String resourceUuid, GloboResourceKey key); + + public void removeConfigurations(String uuid, GloboResourceType loadBalancer); + + public boolean updateValue(GloboResourceConfiguration config); + + + public List getConfigsByValue(GloboResourceType type, GloboResourceKey key, String value); +} diff --git a/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationDaoImpl.java b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationDaoImpl.java new file mode 100644 index 000000000000..6b71d2409ce8 --- /dev/null +++ b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationDaoImpl.java @@ -0,0 +1,121 @@ +package org.apache.cloudstack.globoconfig; + +import com.cloud.utils.db.DB; +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 com.cloud.utils.exception.CloudRuntimeException; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * Created by sinval.neto on 7/15/16. + */ +@Component +@DB() +public class GloboResourceConfigurationDaoImpl extends GenericDaoBase implements GloboResourceConfigurationDao { + private static final Logger s_logger = Logger.getLogger(GloboResourceConfigurationDaoImpl.class); + + private final SearchBuilder ListByResourceId; + + + + public GloboResourceConfigurationDaoImpl() { + ListByResourceId = createSearchBuilder(); + ListByResourceId.and("resourceUuid", ListByResourceId.entity().getResourceUuid(), SearchCriteria.Op.EQ); + ListByResourceId.and("resourceType", ListByResourceId.entity().getResourceType(), SearchCriteria.Op.EQ); + ListByResourceId.and("value", ListByResourceId.entity().getValue(), SearchCriteria.Op.EQ); + ListByResourceId.and("key", ListByResourceId.entity().getKey(), SearchCriteria.Op.EQ); + + ListByResourceId.done(); + } + + @Override + public Map getConfiguration(long resourceId) { + return null; + } + + @Override + public List getConfiguration(GloboResourceType resourceType, String resourceUuid) { + SearchCriteria sc = ListByResourceId.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("resourceUuid", resourceUuid); + return listBy(sc); + } + + @Override + public List getConfiguration(GloboResourceType resourceType, + String resourceUuid, GloboResourceKey key) { + SearchCriteria sc = ListByResourceId.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("resourceUuid", resourceUuid); + sc.setParameters("key", key); + return listBy(sc); + } + + @Override + public GloboResourceConfigurationVO getFirst(GloboResourceType resourceType, + String resourceUuid, GloboResourceKey key) { + SearchCriteria sc = ListByResourceId.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("resourceUuid", resourceUuid); + sc.setParameters("key", key); + + List configs = listBy(sc); + if (configs.size() > 0) + return configs.get(0); + return null; + } + + @Override + public void removeConfigurations(String uuid, GloboResourceType loadBalancer) { + SearchCriteria sc = ListByResourceId.create(); + sc.setParameters("resourceType", loadBalancer); + sc.setParameters("resourceUuid", uuid); + remove(sc); + } + + @Override + public List getConfigsByValue(GloboResourceType type, GloboResourceKey key, String value) { + SearchCriteria sc = ListByResourceId.create(); + sc.setParameters("resourceType", type); + sc.setParameters("value", value); + sc.setParameters("key", key); + + List configs = listBy(sc); + return configs; + } + + + @Override + @Deprecated + public boolean updateValue(GloboResourceConfiguration config) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement stmt = null; + try { + stmt = txn.prepareStatement("UPDATE globo_resource_configuration SET value=? WHERE resource_uuid=?;"); + stmt.setString(1, config.getValue()); + stmt.setString(2, config.getResourceUuid()); + stmt.executeUpdate(); + return true; + } catch (Exception e) { + s_logger.warn("Unable to update GloboResourceConfiguration Value", e); + throw new CloudRuntimeException("Error during pudate globo resource configuration value", e); + } finally { + try { + if (stmt != null) + stmt.close(); + } catch (SQLException e) { + s_logger.warn("error closing stmt", e); + } + } + } + + +} \ No newline at end of file diff --git a/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationVO.java b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationVO.java new file mode 100644 index 000000000000..062c8d744aa7 --- /dev/null +++ b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceConfigurationVO.java @@ -0,0 +1,111 @@ +package org.apache.cloudstack.globoconfig; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import java.util.Objects; + + +/** + * Created by sinval.neto on 7/15/16. + */ +@Entity +@Table(name = "globo_resource_configuration") +public class GloboResourceConfigurationVO implements GloboResourceConfiguration { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Enumerated(value = EnumType.STRING) + @Column(name = "resource_type") + private GloboResourceType resourceType; + + @Column(name = "resource_uuid") + private String resourceUuid; + + @Enumerated(value = EnumType.STRING) + @Column(name = "key") + private GloboResourceKey key; + + @Column(name = "value", length = 255) + private String value; + + public GloboResourceConfigurationVO(){} + + public GloboResourceConfigurationVO(GloboResourceType resourceType, String resourceUuid, GloboResourceKey key, String value){ + this.resourceType = resourceType; + this.resourceUuid = resourceUuid; + this.key = key; + this.value = value; + } + + @Override + public String getInstance() { + return null; + } + + @Override + public Long getId() { + return this.id; + } + + @Override + public GloboResourceType getResourceType() { + return this.resourceType; + } + + @Override + public String getResourceUuid() { + return this.resourceUuid; + } + + @Override + public GloboResourceKey getKey() { + return this.key; + } + + @Override + public String getValue() { + return this.value; + } + + @Override + public void setValue(String value) {this.value = value; } + + public boolean getBooleanValue() { + return Boolean.valueOf(this.getValue()); + } + + public void setBoolValue(boolean value) { + this.setValue(Boolean.toString(value)); + } + + public void setId(Long id) { + this.id = id; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GloboResourceConfigurationVO that = (GloboResourceConfigurationVO) o; + return Objects.equals(id, that.id) && + resourceType == that.resourceType && + Objects.equals(resourceUuid, that.resourceUuid) && + key == that.key && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(id, resourceType, resourceUuid, key, value); + } +} diff --git a/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceKey.java b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceKey.java new file mode 100644 index 000000000000..b9cf17c41b99 --- /dev/null +++ b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceKey.java @@ -0,0 +1,8 @@ +package org.apache.cloudstack.globoconfig; + +/** + * Created by sinval.neto on 7/15/16. + */ +public enum GloboResourceKey { + isDNSRegistered, skipDnsError, dsr, linkedLoadBalancer +} diff --git a/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceType.java b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceType.java new file mode 100644 index 000000000000..e8e0814e5c9f --- /dev/null +++ b/framework/config/src/org/apache/cloudstack/globoconfig/GloboResourceType.java @@ -0,0 +1,8 @@ +package org.apache.cloudstack.globoconfig; + +/** + * Created by sinval.neto on 7/15/16. + */ +public enum GloboResourceType { + LOAD_BALANCER, VM_NIC +} diff --git a/framework/db/src/com/cloud/utils/db/GlobalLock.java b/framework/db/src/com/cloud/utils/db/GlobalLock.java index 662ba921ce90..1650b4491f76 100644 --- a/framework/db/src/com/cloud/utils/db/GlobalLock.java +++ b/framework/db/src/com/cloud/utils/db/GlobalLock.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.locks.ReentrantLock; import org.apache.log4j.Logger; @@ -53,6 +54,7 @@ public class GlobalLock { private long holdingStartTick = 0; private static Map s_lockMap = new HashMap(); + private static Map s_reentrantLockMap = new HashMap(); private GlobalLock(String name) { this.name = name; @@ -101,6 +103,19 @@ public static GlobalLock getInternLock(String name) { } } + public static ReentrantLock getReentrantLock(String name) { + synchronized (s_reentrantLockMap) { + if (s_reentrantLockMap.containsKey(name)) { + ReentrantLock lock = s_reentrantLockMap.get(name); + return lock; + } else { + ReentrantLock lock = new ReentrantLock(true); + s_reentrantLockMap.put(name, lock); + return lock; + } + } + } + private static void releaseInternLock(String name) { synchronized (s_lockMap) { GlobalLock lock = s_lockMap.get(name); @@ -113,6 +128,18 @@ private static void releaseInternLock(String name) { } } + public static void releaseReentrantLock(String name) { + synchronized (s_reentrantLockMap) { + ReentrantLock lock = s_reentrantLockMap.get(name); + if (lock != null) { + if (lock.getQueueLength() == 0) + s_reentrantLockMap.remove(name); + } else { + s_logger.warn("Releasing " + name + ", but it is already released."); + } + } + } + public boolean lock(int timeoutSeconds) { int remainingMilliSeconds = timeoutSeconds * 1000; Profiler profiler = new Profiler(); diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index 3ce96a677fb4..654f014cd6ba 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -252,15 +252,14 @@ public Long doInTransaction(TransactionStatus status) { @DB public void completeAsyncJob(final long jobId, final Status jobStatus, final int resultCode, final String resultObject) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Complete async job-" + jobId + ", jobStatus: " + jobStatus + ", resultCode: " + resultCode + ", result: " + resultObject); + s_logger.info("Complete async job-" + jobId + ", jobStatus: " + jobStatus + ", resultCode: " + resultCode + ", result: " + resultObject); } final AsyncJobVO job = _jobDao.findById(jobId); if (job == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("job-" + jobId + " no longer exists, we just log completion info here. " + jobStatus + ", resultCode: " + resultCode + ", result: " + - resultObject); - } + s_logger.warn("job-" + jobId + " no longer exists, we just log completion info here. " + jobStatus + ", resultCode: " + resultCode + ", result: " + + resultObject); + // still purge item from queue to avoid any blocking _queueMgr.purgeAsyncJobQueueItemId(jobId); return; @@ -344,9 +343,7 @@ public void updateAsyncJobStatus(final long jobId, final int processStatus, fina final AsyncJobVO job = _jobDao.findById(jobId); if (job == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("job-" + jobId + " no longer exists, we just log progress info here. progress status: " + processStatus); - } + s_logger.warn("job-" + jobId + " no longer exists, we just log progress info here. progress status: " + processStatus); return; } @@ -833,7 +830,7 @@ protected void runInContext() { public void reallyRun() { try { - s_logger.info("Begin cleanup expired async-jobs"); + s_logger.debug("Begin cleanup expired async-jobs"); // forcefully cancel blocking queue items if they've been staying there for too long List blockItems = _queueMgr.getBlockedQueueItems(JobCancelThresholdMinutes.value() * 60000, false); @@ -883,7 +880,7 @@ public void reallyRun() { } } - s_logger.info("End cleanup expired async-jobs"); + s_logger.debug("End cleanup expired async-jobs"); } catch (Throwable e) { s_logger.error("Unexpected exception when trying to execute queue item, ", e); } @@ -1027,9 +1024,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) { // reset job status for all jobs running on this ms node List jobs = _jobDao.getResetJobs(msid); for (AsyncJobVO job : jobs) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cancel left-over job-" + job.getId()); - } + + s_logger.warn("Cancel left-over job-" + job.getId()); + job.setStatus(JobInfo.Status.FAILED); job.setResultCode(ApiErrorCode.INTERNAL_ERROR.getHttpCode()); job.setResult("job cancelled because of management server restart or shutdown"); diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml index 8ae292aeaf6a..11fcad06cfb1 100644 --- a/framework/managed-context/pom.xml +++ b/framework/managed-context/pom.xml @@ -32,5 +32,10 @@ org.slf4j slf4j-api + + com.newrelic.agent.java + newrelic-api + 3.18.0 + diff --git a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java index b4a9758420be..ad3ddd146c98 100644 --- a/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java +++ b/framework/managed-context/src/main/java/org/apache/cloudstack/managed/context/ManagedContextRunnable.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.managed.context; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +44,10 @@ public static ManagedContext initializeGlobalContext(ManagedContext context) { } @Override + @Trace(dispatcher=true) public void run() { + NewRelic.setTransactionName(null, "/job/" + this.getClass().getName()); + getContext().runWithContext(new Runnable() { @Override public void run() { diff --git a/packaging/centos63/cloud-usage.rc b/packaging/centos63/cloud-usage.rc index 774113745d75..4dc7ada8221c 100755 --- a/packaging/centos63/cloud-usage.rc +++ b/packaging/centos63/cloud-usage.rc @@ -96,7 +96,7 @@ start() { echo -n "Starting $PROGNAME" "$SHORTNAME" - if daemon --pidfile $PIDFILE $DAEMON -home "$JAVA_HOME" -cp "$CLASSPATH" -pidfile "$PIDFILE" -user "$USER" \ + if daemon --pidfile $PIDFILE $DAEMON -home "$JAVA_HOME" -Xss1280k -cp "$CLASSPATH" -pidfile "$PIDFILE" -user "$USER" \ -errfile $LOGDIR/cloudstack-usage.err -outfile $LOGDIR/cloudstack-usage.out -Dpid=$$ $CLASS RETVAL=$? then diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 68e2c56f5bfb..aa2a57acd056 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -218,7 +218,7 @@ if [ "%{_ossnoss}" == "NOREDIST" -o "%{_ossnoss}" == "noredist" ] ; then mvn -Psystemvm,developer -Dnoredist -Dsimulator clean package else echo "Executing mvn noredist packaging without simulator..." - mvn -Psystemvm,developer -Dnoredist clean package + mvn -Psystemvm,developer -DskipTests -Dnoredist clean package fi else if [ "%{_sim}" == "SIMULATOR" -o "%{_sim}" == "simulator" ] ; then @@ -226,7 +226,10 @@ else mvn -Psystemvm,developer -Dsimulator clean package else echo "Executing mvn default packaging without simulator ..." - mvn -Psystemvm,developer clean package + mvn -version + java -version + javac -version + mvn -Psystemvm,developer -DskipTests clean package fi fi @@ -268,6 +271,7 @@ mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}-management ln -sf /etc/%{name}/management ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/conf ln -sf /var/log/%{name}/management ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/logs @@ -287,6 +291,17 @@ cp -r client/target/classes/META-INF/webapp ${RPM_BUILD_ROOT}%{_datadir}/%{name} cp client/target/cloud-client-ui-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/cloudstack-%{_maventag}.jar cp client/target/lib/*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/ +# Append a release number into version info +if [ -f "${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/lib/cloud-server-%{_ver}.jar" ] ; then + unzip -q ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/lib/cloud-server-%{_ver}.jar META-INF/MANIFEST.MF + sed -i 's/Implementation-Version: %{_ver}/Implementation-Version: %{_ver}-%{_rel}/g' META-INF/MANIFEST.MF + zip -qrum ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/lib/cloud-server-%{_ver}.jar META-INF/MANIFEST.MF + rmdir META-INF +else + echo "File ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/lib/cloud-server-%{_ver}.jar not found. I can't append the release number into version" +fi + + # Don't package the scripts in the management webapp rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/scripts rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms @@ -312,6 +327,7 @@ chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/management chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}-management # KVM Agent mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent @@ -564,6 +580,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %attr(0755,root,root) %{_bindir}/%{name}-external-ipallocator.py %attr(0755,root,root) %{_initrddir}/%{name}-ipallocator %dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/ipallocator +%dir %attr(0770,root,root) %{_localstatedir}/log/%{name}-management %{_defaultdocdir}/%{name}-management-%{version}/LICENSE %{_defaultdocdir}/%{name}-management-%{version}/NOTICE #%attr(0644,root,root) %{_sysconfdir}/logrotate.d/%{name}-catalina diff --git a/packaging/centos63/default/macros.spec b/packaging/centos63/default/macros.spec new file mode 100644 index 000000000000..c485b0519013 --- /dev/null +++ b/packaging/centos63/default/macros.spec @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# %define _javaversion jdk-1.7.0_80-fcs + +%define _pythonparamiko Requires: python-paramiko +%define _javaversion java-1.7.0-openjdk +%define _tomcatversion tomcat6 +%define _vlanconfigtool vconfig +%define _tomcatpathname tomcat6 +%define _managementstartscriptpath %{_initrddir} +%define _managementservice %{nil} +%define _managementserviceattribute %{nil} +%define _iptablesservice %{nil} +%define _serverxmlname server +%define _cloudstackmanagementconf %{nil} +%define _cloudstackmanagementconfattr %{nil} diff --git a/packaging/centos63/package.sh b/packaging/centos63/package.sh new file mode 100755 index 000000000000..c77988d8b5db --- /dev/null +++ b/packaging/centos63/package.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +virtualenv_wrapper_script='/opt/generic/python27/bin/virtualenvwrapper.sh' +virtualenv_python='/opt/generic/python27/bin/python2.7' + +function usage() { + echo "" + echo "usage: ./package.sh [-t|--tag] [-h|--help] [ARGS]" + echo "" + + echo "Examples: ./package.sh -t|--tag 4.2.0-201402261200" + exit 1 +} + +function activateVirtualEnv() { + virtualenv_name=${1} + echo "Switching to '${virtualenv_name}' virtualenv" + if [ -f ${virtualenv_wrapper_script} ]; then + echo "Using ${virtualenv_wrapper_script}" + source ${virtualenv_wrapper_script} + fi + [[ -z ${WORKON_HOME} ]] && WORKON_HOME=~/.virtualenvs + + echo "WORKON_HOME: ${WORKON_HOME}" + + export VIRTUALENVWRAPPER_PYTHON=${virtualenv_python} + [[ ! -d $WORKON_HOME/${virtualenv_name} ]] && mkvirtualenv -p ${virtualenv_python} ${virtualenv_name} + source $WORKON_HOME/${virtualenv_name}/bin/activate +} + +function packaging() { + tag_from_arg=$1 + + echo "Getting new tags from remote..." + if ! git fetch --tags ; then + echo "Failed to get tags from remote repository" + exit 1 + fi + + echo "Cheking out to tag: ${tag_from_arg}" + if ! git checkout $tag_from_arg > /dev/null 2>&1; then + echo "Failed to checkout to tag ${tag_from_arg}" + exit 1 + fi + + [[ $? -ne 0 ]] && echo -e "\nInvalid tag, plese check it (${tag_from_arg})\n" && exit 1 + [[ $tag_from_arg =~ ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\-([0-9]+) ]] && tag_version=${BASH_REMATCH[1]} tag_release=${BASH_REMATCH[2]} + + activateVirtualEnv cloudstack + + echo "Getting version..." + VERSION=`(cd ../../; mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version) | grep '^[0-9]\.'` + [[ "$tag_version" != "$VERSION" ]] && echo "Tag parameter version (${tag_version}) is not the same as git tag version (${VERSION}), fix it!" && exit 1 + + CWD=`pwd` + RPMDIR=$CWD/../../dist/rpmbuild + [[ ! -d ${RPMDIR} ]] && mkdir -p ${RPMDIR} + PACK_PROJECT=cloudstack + + if echo $VERSION | grep SNAPSHOT ; then + REALVER=`echo $VERSION | cut -d '-' -f 1` + DEFVER="-D_ver $REALVER" + DEFPRE="-D_prerelease 1" + DEFREL="-D_rel SNAPSHOT" + else + REALVER=$VERSION + DEFVER="-D_ver $REALVER" + DEFREL="-D_rel $tag_release" + fi + + echo "Cleaning $RPMDIR just in case..." + rm -rf $RPMDIR/* > /dev/null 2>&1 + + echo Preparing to package Apache CloudStack ${VERSION} + mkdir -p $RPMDIR/SPECS + mkdir -p $RPMDIR/BUILD + mkdir -p $RPMDIR/SRPMS + mkdir -p $RPMDIR/RPMS + mkdir -p $RPMDIR/SOURCES/$PACK_PROJECT-$VERSION + + echo ". preparing source tarball" + + (cd ../../; tar -c --exclude .git --exclude dist . | tar -C $RPMDIR/SOURCES/$PACK_PROJECT-$VERSION -x ) + (cd $RPMDIR/SOURCES/; tar -czf $PACK_PROJECT-$VERSION.tgz $PACK_PROJECT-$VERSION) + + echo ". executing rpmbuild" + + cp cloud.spec $RPMDIR/SPECS + cp -rf default $RPMDIR/SPECS + cp -rf rhel7 $RPMDIR/SPECS + + (cd $RPMDIR; rpmbuild --define "_topdir $RPMDIR" "${DEFVER}" "${DEFREL}" ${DEFPRE+${DEFPRE}} -ba SPECS/cloud.spec) + + if [ $? -ne 0 ]; then + echo "RPM Build Failed " + exit 1 + fi + + echo "Done" + + exit +} + +if [ $# -lt 1 ] ; then + usage +elif [ $# -gt 0 ] ; then + + SHORTOPTS="ht:" + LONGOPTS="help,tag:" + + ARGS=$(getopt -s bash -u -a --options $SHORTOPTS --longoptions $LONGOPTS --name $0 -- "$@" ) + eval set -- "$ARGS" + + while [ $# -gt 0 ] ; do + case "$1" in + -h | --help) + usage + exit 0 + ;; + -t | --tag) + echo "Doing CloudStack Packaging tag: $2..." + packaging $2 + ;; + *) + shift + ;; + esac + done + +else + echo "Incorrect choice. Nothing to do." >&2 + echo "Please, execute ./package.sh --help for more help" +fi diff --git a/packaging/debian/init/cloud-usage b/packaging/debian/init/cloud-usage new file mode 100755 index 000000000000..6829fb8b5e69 --- /dev/null +++ b/packaging/debian/init/cloud-usage @@ -0,0 +1,154 @@ +#!/bin/bash + +### BEGIN INIT INFO +# Provides: cloudstack-usage +# Required-Start: $network $local_fs +# Required-Stop: $network $local_fs +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Short-Description: Start/stop Apache CloudStack Usage Monitor +# Description: This scripts Starts/Stops the Apache CloudStack Usage Monitor +## The CloudStack Usage Monitor is a part of the Apache CloudStack project and is used +## for storing usage statistics from instances. +## JSVC (Java daemonizing) is used for starting and stopping the usage monitor. +### END INIT INFO + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +. /lib/lsb/init-functions + +SHORTNAME="cloudstack-usage" +PIDFILE=/var/run/"$SHORTNAME".pid +PROGNAME="CloudStack Usage Monitor" +CLASS="com.cloud.usage.UsageServer" +PROG="jsvc" +DAEMON="/usr/bin/jsvc" + +unset OPTIONS +[ -r /etc/default/"$SHORTNAME" ] && source /etc/default/"$SHORTNAME" + +setJavaHome() { + # use $JAVA_HOME if defined + if [ -n "$JAVA_HOME" ] ; then + return + fi + + # try java first + java=$(which java 2>/dev/null || :) + + # try javac if java is not found + if [ -z "$java" ] ; then + java=$(which javac 2>/dev/null || :) + fi + + if [ -n "$java" ] ; then + JAVA_HOME=$(dirname $(dirname $(readlink -e $java))) + export JAVA_HOME + return + fi + + # If everything has failed, try hardcoded paths + # The first existing directory is used for JAVA_HOME (if JAVA_HOME is not defined in $DEFAULT) + JDK_DIRS="/usr/lib/jvm/java-7-openjdk-amd64 /usr/lib/jvm/java-7-openjdk-i386 /usr/lib/jvm/java-7-oracle /usr/lib/jvm/java-7-openjdk /usr/lib/jvm/java-7-sun" + for jdir in $JDK_DIRS; do + if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then + JAVA_HOME="$jdir" + export JAVA_HOME + return + fi + done + + # didnt find java home. exiting with error + exit 1 +} + +setJavaHome + +UCP=`ls /usr/share/cloudstack-usage/lib/*.jar | tr '\n' ':' | sed s'/.$//'` +PCP=`ls /usr/share/cloudstack-usage/plugins/*.jar 2>/dev/null | tr '\n' ':' | sed s'/.$//'` + +# We need to append the JSVC daemon JAR to the classpath +# AgentShell implements the JSVC daemon methods +export CLASSPATH="/usr/share/java/commons-daemon.jar:/usr/share/java/mysql-connector-java.jar:$UCP:$PCP:/etc/cloudstack/usage" + +start() { + if [ -s "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + log_daemon_msg "$PROGNAME apparently already running" + log_end_msg 0 + exit 0 + fi + + log_daemon_msg "Starting $PROGNAME" "$SHORTNAME" + if hostname --fqdn >/dev/null 2>&1 ; then + true + else + log_failure_msg "The host name does not resolve properly to an IP address. Cannot start $PROGNAME" + log_end_msg 1 + exit 1 + fi + + if start_daemon -p $PIDFILE $DAEMON -home "$JAVA_HOME" -Xss1280k -cp "$CLASSPATH" -pidfile "$PIDFILE" -outfile SYSLOG -errfile SYSLOG -Dpid=$$ $CLASS + RETVAL=$? + then + rc=0 + sleep 1 + if ! kill -0 $(cat "$PIDFILE") >/dev/null 2>&1; then + log_failure_msg "$PROG failed to start" + rc=1 + fi + else + rc=1 + fi + + if [ $rc -eq 0 ]; then + log_end_msg 0 + else + log_end_msg 1 + rm -f "$PIDFILE" + fi +} + +stop() { + log_daemon_msg "Stopping $PROGNAME" "$SHORTNAME" + killproc -p $PIDFILE $DAEMON + log_end_msg $? + rm -f "$PIDFILE" +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status_of_proc -p "$PIDFILE" "$PROG" "$SHORTNAME" + RETVAL=$? + ;; + restart | force-reload) + stop + sleep 3 + start + ;; + *) + echo "Usage: $0 {start|stop|restart|force-reload|status}" + RETVAL=3 +esac + +exit $RETVAL diff --git a/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java b/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java index d10c191151f2..133b089d898b 100644 --- a/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java +++ b/plugins/acl/dynamic-role-based/src/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java @@ -75,7 +75,6 @@ public boolean checkAccess(User user, String commandName) throws PermissionDenie if (accountRole == null || accountRole.getId() < 1L) { denyApiAccess(commandName); } - // Allow all APIs for root admins if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) { return true; diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer620SP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer620SP1Resource.java new file mode 100644 index 000000000000..3e50ef22b90f --- /dev/null +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer620SP1Resource.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.hypervisor.xen.resource; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; + +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.GPUGroup; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.PGPU; +import com.xensource.xenapi.Types.XenAPIException; +import com.xensource.xenapi.VGPU; +import com.xensource.xenapi.VGPUType; +import com.xensource.xenapi.VGPUType.Record; +import com.xensource.xenapi.VM; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.GetGPUStatsAnswer; +import com.cloud.agent.api.GetGPUStatsCommand; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.agent.api.to.GPUDeviceTO; +import com.cloud.resource.ServerResource; + +@Local(value=ServerResource.class) +public class XenServer620SP1Resource extends XenServer620Resource { + private static final Logger s_logger = Logger.getLogger(XenServer620SP1Resource.class); + + public XenServer620SP1Resource() { + super(); + } + + @Override + public Answer executeRequest(Command cmd) { + Class clazz = cmd.getClass(); + if (clazz == GetGPUStatsCommand.class) { + return execute((GetGPUStatsCommand) cmd); + } else { + return super.executeRequest(cmd); + } + } + + protected GetGPUStatsAnswer execute(GetGPUStatsCommand cmd) { + Connection conn = getConnection(); + HashMap> groupDetails = new HashMap>(); + try { + groupDetails = getGPUGroupDetails(conn); + } catch (Exception e) { + String msg = "Unable to get GPU stats" + e.toString(); + s_logger.warn(msg, e); + } + return new GetGPUStatsAnswer(cmd, groupDetails); + } + + @Override + protected void fillHostInfo(Connection conn, StartupRoutingCommand cmd) { + super.fillHostInfo(conn, cmd); + try { + HashMap> groupDetails = getGPUGroupDetails(conn); + cmd.setGpuGroupDetails(groupDetails); + } catch (Exception e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Error while getting GPU device info from host " + cmd.getName(), e); + } + } + } + + @Override + protected HashMap> getGPUGroupDetails(Connection conn) throws XenAPIException, XmlRpcException { + HashMap> groupDetails = new HashMap>(); + Host host = Host.getByUuid(conn, _host.uuid); + Set pgpus = host.getPGPUs(conn); + Iterator iter = pgpus.iterator(); + while (iter.hasNext()) { + PGPU pgpu = iter.next(); + GPUGroup gpuGroup = pgpu.getGPUGroup(conn); + Set enabledVGPUTypes = gpuGroup.getEnabledVGPUTypes(conn); + String groupName = gpuGroup.getNameLabel(conn); + HashMap gpuCapacity = new HashMap(); + if (groupDetails.get(groupName) != null) { + gpuCapacity = groupDetails.get(groupName); + } + // Get remaining capacity of all the enabled VGPU in a PGPU + if(enabledVGPUTypes != null) { + Iterator it = enabledVGPUTypes.iterator(); + while (it.hasNext()) { + VGPUType type = it.next(); + Record record = type.getRecord(conn); + Long remainingCapacity = pgpu.getRemainingCapacity(conn, type); + Long maxCapacity = pgpu.getSupportedVGPUMaxCapacities(conn).get(type); + VgpuTypesInfo entry; + if ((entry = gpuCapacity.get(record.modelName)) != null) { + remainingCapacity += entry.getRemainingCapacity(); + maxCapacity += entry.getMaxCapacity(); + entry.setRemainingCapacity(remainingCapacity); + entry.setMaxVmCapacity(maxCapacity); + gpuCapacity.put(record.modelName, entry); + } else { + VgpuTypesInfo vgpuTypeRecord = new VgpuTypesInfo(null, record.modelName, record.framebufferSize, record.maxHeads, + record.maxResolutionX, record.maxResolutionY, maxCapacity, remainingCapacity, maxCapacity); + gpuCapacity.put(record.modelName, vgpuTypeRecord); + } + } + } + groupDetails.put(groupName, gpuCapacity); + } + return groupDetails; + } + + @Override + protected void createVGPU(Connection conn, StartCommand cmd, VM vm, GPUDeviceTO gpuDevice) throws XenAPIException, XmlRpcException { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating VGPU of VGPU type [ " + gpuDevice.getVgpuType() + " ] in gpu group" + gpuDevice.getGpuGroup() + + " for VM " + cmd.getVirtualMachine().getName()); + } + + Set groups = GPUGroup.getByNameLabel(conn, gpuDevice.getGpuGroup()); + assert groups.size() == 1 : "Should only have 1 group but found " + groups.size(); + GPUGroup gpuGroup = groups.iterator().next(); + + Set vgpuTypes = gpuGroup.getEnabledVGPUTypes(conn); + Iterator iter = vgpuTypes.iterator(); + VGPUType vgpuType = null; + while (iter.hasNext()) { + VGPUType entry = iter.next(); + if (entry.getModelName(conn).equals(gpuDevice.getVgpuType())) { + vgpuType = entry; + } + } + String device = "0"; // Only allow device = "0" for now, as XenServer supports just a single vGPU per VM. + Map other_config = new HashMap(); + VGPU.create(conn, vm, gpuGroup, device, other_config, vgpuType); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Created VGPU of VGPU type [ " + gpuDevice.getVgpuType() + " ] for VM " + cmd.getVirtualMachine().getName()); + } + // Calculate and set remaining GPU capacity in the host. + cmd.getVirtualMachine().getGpuDevice().setGroupDetails(getGPUGroupDetails(conn)); + } + + @Override + protected String getGuestOsType(String stdType, String platformEmulator, boolean bootFromCD) { + if (platformEmulator == null) { + if (!bootFromCD) { + s_logger.debug("Can't find the guest os: " + stdType + " mapping into XenServer 6.2.0 guestOS type, start it as HVM guest"); + platformEmulator = "Other install media"; + } else { + String msg = "XenServer 6.2.0 DOES NOT support Guest OS type " + stdType; + s_logger.warn(msg); + } + } + return platformEmulator; + } + + @Override + public long getStaticMax(String os, boolean b, long dynamicMinRam, long dynamicMaxRam){ + long recommendedValue = CitrixHelper.getXenServer620SP1StaticMax(os, b); + if(recommendedValue == 0){ + s_logger.warn("No recommended value found for dynamic max, setting static max and dynamic max equal"); + return dynamicMaxRam; + } + long staticMax = Math.min(recommendedValue, 4l * dynamicMinRam); // XS constraint for stability + if (dynamicMaxRam > staticMax){ // XS contraint that dynamic max <= static max + s_logger.warn("dynamixMax " + dynamicMaxRam + " cant be greater than static max " + staticMax + ", can lead to stability issues. Setting static max as much as dynamic max "); + return dynamicMaxRam; + } + return staticMax; + } + + @Override + public long getStaticMin(String os, boolean b, long dynamicMinRam, long dynamicMaxRam){ + long recommendedValue = CitrixHelper.getXenServer620SP1StaticMin(os, b); + if(recommendedValue == 0){ + s_logger.warn("No recommended value found for dynamic min"); + return dynamicMinRam; + } + + if(dynamicMinRam < recommendedValue){ // XS contraint that dynamic min > static min + s_logger.warn("Vm is set to dynamixMin " + dynamicMinRam + " less than the recommended static min " + recommendedValue + ", could lead to stability issues"); + } + return dynamicMinRam; + } +} diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java new file mode 100644 index 000000000000..bec474018952 --- /dev/null +++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenserverConfigs.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.hypervisor.xenserver; + +public final class XenserverConfigs { + public static final String XS620HotFix = "xs620hotfix"; + public static final String XSHotFix62ESP1 = "0850b186-4d47-11e3-a720-001b2151a503"; + public static final String XSHotFix62ESP1004 = "996dd2e7-ad95-49cc-a0be-2c9adc4dfb0b"; +} diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java index 76c09dca8fa6..01865afb4390 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java @@ -568,13 +568,13 @@ public Answer deleteVolume(final DeleteCommand cmd) { deleteVDI(conn, vdi); return new Answer(null); } catch (final BadServerResponse e) { - s_logger.debug("Failed to delete volume", e); + s_logger.warn("Failed to delete volume", e); errorMsg = e.toString(); } catch (final XenAPIException e) { - s_logger.debug("Failed to delete volume", e); + s_logger.warn("Failed to delete volume", e); errorMsg = e.toString(); } catch (final XmlRpcException e) { - s_logger.debug("Failed to delete volume", e); + s_logger.warn("Failed to delete volume", e); errorMsg = e.toString(); } return new Answer(null, false, errorMsg); diff --git a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java index ee56f699bda2..edbc761adcf9 100644 --- a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java +++ b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java @@ -311,8 +311,6 @@ private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachine // Initiate migration of a virtual machine with its volumes. try { - verifyNoSnapshotsOnManagedStorageVolumes(volumeToPool); - List> volumeToStorageUuid = new ArrayList<>(); for (Map.Entry entry : volumeToPool.entrySet()) { @@ -334,7 +332,6 @@ private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachine // 1. Send a migrate receive command to the destination host so that it is ready to receive a vm. // 2. Send a migrate send command to the source host. This actually migrates the vm to the destination. // 3. Complete the process. Update the volume details. - MigrateWithStorageReceiveCommand receiveCmd = new MigrateWithStorageReceiveCommand(to, volumeToStorageUuid); MigrateWithStorageReceiveAnswer receiveAnswer = (MigrateWithStorageReceiveAnswer)agentMgr.send(destHost.getId(), receiveCmd); @@ -388,15 +385,15 @@ private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachine // Initiate migration of a virtual machine with its volumes. try { - List> volumeToFilerto = new ArrayList>(); + List> volumeToFilerTO = new ArrayList<>(); for (Map.Entry entry : volumeToPool.entrySet()) { VolumeInfo volume = entry.getKey(); VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId())); StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue()); - volumeToFilerto.add(new Pair(volumeTo, filerTo)); + volumeToFilerTO.add(new Pair(volumeTo, filerTo)); } - MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto); + MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerTO, destHost.getGuid()); MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer)agentMgr.send(destHost.getId(), command); if (answer == null) { s_logger.error("Migration with storage of vm " + vm + " failed."); diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java index dc5f0ab6ceec..5d25f0fcf553 100644 --- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java +++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java @@ -27,12 +27,14 @@ import javax.inject.Inject; +import com.cloud.configuration.ConfigurationManagerImpl; +import com.cloud.offering.ServiceOffering; +import com.cloud.service.dao.ServiceOfferingDao; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerRuleCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.log4j.Logger; -import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; @@ -71,10 +73,8 @@ import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.rules.LoadBalancer; import com.cloud.offering.NetworkOffering; -import com.cloud.offering.ServiceOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.service.ServiceOfferingVO; -import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; @@ -358,7 +358,7 @@ private LoadBalancer handleCreateLoadBalancerRuleWithLock(final CreateLoadBalanc lb.setSourceIpAddressId(ipId); result = _lbMgr.createPublicLoadBalancer(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourcePortStart(), lb.getDefaultPortStart(), ipId.longValue(), - lb.getProtocol(), lb.getAlgorithm(), false, CallContext.current(), lb.getLbProtocol(), true); + lb.getProtocol(), lb.getAlgorithm(), false, CallContext.current(), lb.getLbProtocol(), true, null, lb.getCache(), lb.getServiceDownAction(), lb.getHealthCheckDestination(), lb.getExpectedHealthCheck(), lb.getHealthCheckType(), true, lb.isDsr()); } catch (final NetworkRuleConflictException e) { s_logger.warn("Failed to create LB rule, not continuing with ELB deployment"); if (newIp) { diff --git a/plugins/network-elements/globoaclapi/pom.xml b/plugins/network-elements/globoaclapi/pom.xml new file mode 100644 index 000000000000..1483a9642348 --- /dev/null +++ b/plugins/network-elements/globoaclapi/pom.xml @@ -0,0 +1,58 @@ + + + + + 4.0.0 + cloud-plugin-network-globoaclapi + Apache CloudStack Plugin - Globo ACL API + + org.apache.cloudstack + cloudstack-plugins + 4.11.1.0 + ../../pom.xml + + + + org.apache.cloudstack + cloud-plugin-network-globonetwork + ${project.version} + + + com.globo.aclapi + aclapi-client + 0.0.4 + + + org.slf4j + slf4j-log4j12 + + + net.kencochrane.raven + raven + 4.2 + + + net.kencochrane.raven + raven-log4j + 4.2 + + + + diff --git a/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/module.properties b/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/module.properties new file mode 100644 index 000000000000..fa141469977a --- /dev/null +++ b/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/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=globoaclapi +parent=globonetwork \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/spring-globoaclapi-context.xml b/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/spring-globoaclapi-context.xml new file mode 100644 index 000000000000..7f5fc85eaed4 --- /dev/null +++ b/plugins/network-elements/globoaclapi/resources/META-INF/cloudstack/globoaclapi/spring-globoaclapi-context.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/AddGloboAclApiHostCmd.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/AddGloboAclApiHostCmd.java new file mode 100644 index 000000000000..60dea9a6aa0d --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/AddGloboAclApiHostCmd.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 com.globo.globoaclapi.cloudstack.api; + +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.host.Host; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globoaclapi.cloudstack.manager.GloboACLService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand(name = "addGloboAclApiHost", responseObject = SuccessResponse.class, description = "Adds the Globo ACL API external host") +public class AddGloboAclApiHostCmd extends BaseAsyncCmd { + + private static final String s_name = "addgloboaclapihostresponse"; + @Inject + GloboACLService _globoACLService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = BaseCmd.CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.URL, type = BaseCmd.CommandType.STRING, required = true, description = "Globo ACL API url") + private String url; + + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username for Globo ACL API") + private String username; + + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password for Globo ACL API") + private String password; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + Host host = _globoACLService.addGloboAclApiHost(physicalNetworkId, url, username, password); + + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess((host != null)); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_CREATE; + } + + @Override + public String getEventDescription() { + return "Add Globo ACL API provider"; + } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/CreateGloboACLRuleCmd.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/CreateGloboACLRuleCmd.java new file mode 100644 index 000000000000..de73ee25a29b --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/CreateGloboACLRuleCmd.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 com.globo.globoaclapi.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globoaclapi.cloudstack.manager.GloboACLService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "createGloboACLRule", description = "Creates an ACL rule given a network", responseObject = FirewallResponse.class, entityType = {FirewallRule.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class CreateGloboACLRuleCmd extends BaseAsyncCmd { + + + private static final String s_name = "creategloboaclrulereponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the Network id of the ACL rule") + private Long networkId; + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + private String protocol; + + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + private Integer publicStartPort; + + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + private Integer publicEndPort; + + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from") + private List cidrlist; + + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + private Integer icmpType; + + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + private Integer icmpCode; + + @Inject + protected GloboACLService globoACLService; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Network network = _networkService.getNetwork(getNetworkId()); + FirewallRuleVO rule = createFirewallRuleVO(); + globoACLService.createACLRule(network, rule); + + FirewallResponse fwResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(fwResponse); + fwResponse.setResponseName(getCommandName()); + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + private FirewallRuleVO createFirewallRuleVO() { + return new FirewallRuleVO( + null, null, this.getPortStart(), this.getEndPort(), this.getProtocol(), + this.getNetworkId(), 0, 0, null, this.getCidrlist(), this.getIcmpCode(), + this.getIcmpType(), null, null + ); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_ACL_CREATE; + } + + @Override + public String getEventDescription() { + return ("Creating firewall rule for Network ID: " + getNetworkId() + " for protocol:" + getProtocol()); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + public String getProtocol() { + return protocol; + } + + public Integer getPortStart() { + return publicStartPort; + } + + public Integer getEndPort() { + return publicEndPort; + } + + public List getCidrlist() { + return cidrlist; + } + + public Integer getIcmpType() { + return icmpType; + } + + public Integer getIcmpCode() { + return icmpCode; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/ListGloboACLRulesCmd.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/ListGloboACLRulesCmd.java new file mode 100644 index 000000000000..376f13143bc7 --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/ListGloboACLRulesCmd.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 com.globo.globoaclapi.cloudstack.api; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.utils.StringUtils; +import com.globo.globoaclapi.cloudstack.manager.GloboACLService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listGloboACLRules", description = "Lists all ACL rules for an network address.", responseObject = FirewallResponse.class, entityType = {FirewallRule.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListGloboACLRulesCmd extends BaseCmd{ + + private static final String s_name = "listgloboaclrulesresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + description = "list firewall rules for ceratin network", + since = "4.3") + private Long networkId; + + @Inject + protected GloboACLService globoACLService; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + Network network = _networkService.getNetwork(networkId); + List firewallRules = globoACLService.listACLRules(network); + ListResponse response = new ListResponse(); + List fwResponses = new ArrayList(); + + for (FirewallRule fwRule : firewallRules) { + FirewallResponse ruleData = _responseGenerator.createFirewallResponse(fwRule); + ruleData.setId(fwRule.getXid()); + ruleData.setCidrList(StringUtils.join(fwRule.getSourceCidrList(), ",")); + ruleData.setObjectName("firewallrule"); + fwResponses.add(ruleData); + } + response.setResponses(fwResponses, firewallRules.size()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/RemoveGloboACLRuleCmd.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/RemoveGloboACLRuleCmd.java new file mode 100644 index 000000000000..30db090c0b5e --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/api/RemoveGloboACLRuleCmd.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 com.globo.globoaclapi.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globoaclapi.cloudstack.manager.GloboACLService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand(name = "removeGloboACLRule", description = "Deletes a ACL rule", responseObject = SuccessResponse.class, entityType = {FirewallRule.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class RemoveGloboACLRuleCmd extends BaseAsyncCmd{ + + private static final String s_name = "removegloboaclruleresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.STRING, entityType = SuccessResponse.class, required = true, description = "the ID of the ACL rule") + private String id; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the Network id of the ACL rule") + private Long networkId; + + @Inject + protected GloboACLService globoACLService; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try{ + Network network = _networkService.getNetwork(networkId); + globoACLService.removeACLRule(network, new Long(id)); + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } catch(CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_ACL_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting firewall rule id=" + id; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/CreateACLRuleCommand.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/CreateACLRuleCommand.java new file mode 100644 index 000000000000..b8bcb0e1e4a1 --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/CreateACLRuleCommand.java @@ -0,0 +1,141 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globoaclapi.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class CreateACLRuleCommand extends Command { + + private String destinationCidr; + + private String sourceCidr; + + private Integer startPort; + + private Integer endPort; + + private String protocol; + + private Integer icmpCode; + + private Integer icmpType; + + private Long vlanNumber; + + private Long environmentId; + + private String aclOwner; + + public String getDestinationCidr() { + return destinationCidr; + } + + public void setDestinationCidr(String destinationCidr) { + this.destinationCidr = destinationCidr; + } + + public String getSourceCidr() { + return sourceCidr; + } + + public void setSourceCidr(String sourceCidr) { + this.sourceCidr = sourceCidr; + } + + public Integer getStartPort() { + return startPort; + } + + public void setStartPort(Integer startPort) { + this.startPort = startPort; + } + + public Integer getEndPort() { + return endPort; + } + + public void setEndPort(Integer endPort) { + this.endPort = endPort; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public void setVlanNumber(Long vlanNumber) { + this.vlanNumber = vlanNumber; + } + + public Long getVlanNumber() { + return vlanNumber; + } + + public void setEnvironmentId(Long environmentId) { + this.environmentId = environmentId; + } + + public Long getEnvironmentId() { + return environmentId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public void setAclOwner(String aclOwner) { + this.aclOwner = aclOwner; + } + + public String getAclOwner() { + return aclOwner; + } + + public String getAclRuleDescription() { + return "CreateACLRuleCommand{" + + "destinationCidr='" + destinationCidr + '\'' + + ", sourceCidr='" + sourceCidr + '\'' + + ", startPort=" + startPort + + ", endPort=" + endPort + + ", protocol='" + protocol + '\'' + + ", icmpCode=" + icmpCode + + ", icmpType=" + icmpType + + ", vlanNumber=" + vlanNumber + + ", environmentId=" + environmentId + + '}'; + } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/ListACLRulesCommand.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/ListACLRulesCommand.java new file mode 100644 index 000000000000..cfff50393c1c --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/ListACLRulesCommand.java @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globoaclapi.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListACLRulesCommand extends Command { + + protected Long vlanNumber; + + protected Long environmentId; + + private Long networkId; + + public ListACLRulesCommand(Long environmentId, Long vlanNumber, Long networkId) { + this.environmentId = environmentId; + this.vlanNumber = vlanNumber; + this.networkId = networkId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVlanNumber() { + return vlanNumber; + } + + public Long getEnvironmentId() { + return environmentId; + } + + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/RemoveACLRuleCommand.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/RemoveACLRuleCommand.java new file mode 100644 index 000000000000..ca637c7b8795 --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/commands/RemoveACLRuleCommand.java @@ -0,0 +1,58 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globoaclapi.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class RemoveACLRuleCommand extends Command { + + private Long ruleId; + + private Long environmentId; + + private Long vlanNumber; + + private String aclOwner; + + public RemoveACLRuleCommand(Long ruleId, Long environmentId, Long vlanNumber, String aclOwner) { + this.ruleId = ruleId; + this.environmentId = environmentId; + this.vlanNumber = vlanNumber; + this.aclOwner = aclOwner; + } + + public Long getRuleId() { + return ruleId; + } + + public Long getEnvironmentId() { + return environmentId; + } + + public Long getVlanNumber() { + return vlanNumber; + } + + public String getAclOwner() { + return aclOwner; + } + + @Override + public boolean executeInSequence() { + return false; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/element/GloboACLElement.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/element/GloboACLElement.java new file mode 100644 index 000000000000..9dd7324fbd96 --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/element/GloboACLElement.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 com.globo.globoaclapi.cloudstack.element; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.Network; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.AdapterBase; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachineProfile; +import com.globo.globoaclapi.cloudstack.resource.GloboAclApiResource; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Component +public class GloboACLElement extends AdapterBase implements NetworkElement, ResourceStateAdapter{ + + @Inject + protected ResourceManager _resourceMgr; + + private static final Map> capabilities = setCapabilities(); + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap<>(); + + Map firewallCapabilities = new HashMap<>(); + firewallCapabilities.put(Network.Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Network.Capability.SupportedEgressProtocols, "tcp,udp,icmp,all"); + firewallCapabilities.put(Network.Capability.MultipleIps, "true"); + firewallCapabilities.put(Network.Capability.TrafficStatistics, "per public ip"); + firewallCapabilities.put(Network.Capability.SupportedTrafficDirection, "ingress,egress"); + capabilities.put(Network.Service.Firewall, firewallCapabilities); + + return capabilities; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + _resourceMgr.registerResourceStateAdapter(name, this); + return true; + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, List hostTags) { + if (!(startup[0] != null && resource instanceof GloboAclApiResource)) { + return null; + } + host.setType(Host.Type.L2Networking); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + if (host.getType() != Host.Type.L2Networking) { + return null; + } + return new DeleteHostAnswer(true); + } + + @Override + public Network.Provider getProvider() { + return Network.Provider.GloboAclApi; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + return true; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return true; + } + + @Override + public boolean verifyServicesCombination(Set services) { + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLManager.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLManager.java new file mode 100644 index 000000000000..dbbdeea0209c --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLManager.java @@ -0,0 +1,284 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globoaclapi.cloudstack.manager; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.resource.ResourceManager; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackWithException; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globoaclapi.cloudstack.api.AddGloboAclApiHostCmd; +import com.globo.globoaclapi.cloudstack.api.CreateGloboACLRuleCmd; +import com.globo.globoaclapi.cloudstack.api.ListGloboACLRulesCmd; +import com.globo.globoaclapi.cloudstack.api.RemoveGloboACLRuleCmd; +import com.globo.globoaclapi.cloudstack.commands.CreateACLRuleCommand; +import com.globo.globoaclapi.cloudstack.commands.ListACLRulesCommand; +import com.globo.globoaclapi.cloudstack.commands.RemoveACLRuleCommand; +import com.globo.globoaclapi.cloudstack.resource.GloboAclApiResource; +import com.globo.globoaclapi.cloudstack.response.GloboACLRulesResponse; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkNetworkDao; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class GloboACLManager implements GloboACLService, Configurable, PluggableService { + + @Inject + protected HostDao _hostDao; + @Inject + protected AgentManager _agentMgr; + @Inject + protected GloboNetworkService _globoNetworkService; + @Inject + protected GloboNetworkNetworkDao _globoNetworkDao; + @Inject + protected ResourceManager _resourceMgr; + @Inject + protected PhysicalNetworkDao _physicalNetworkDao; + + public static final String VLAN_NOT_ACTIVATED_MESSAGE = "3006:ACL nao cadastrada na ACL::API!"; + + private static final ConfigKey GloboAclTrustSSL = new ConfigKey<>("ACL", Boolean.class, "globoaclapi.trust.ssl", "true", + "Set true to trust ACL API SSL certificate", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboAclTimeout = new ConfigKey<>("ACL", Integer.class, "globoaclapi.timeout", "60000", + "Globo ACL API connection timeout in", true, ConfigKey.Scope.Global); + + private static final Logger s_logger = Logger.getLogger(GloboACLManager.class); + + @Override + public List listACLRules(Network network) { + Long environmentId = this.getEnvironmentId(network); + Vlan vlan = this.getVlan(network); + + s_logger.debug("Listing ACL rules vlan: " + vlan.getVlanNum() + " env:" + environmentId); + + List rules = new ArrayList<>(); + try { + ListACLRulesCommand cmd = new ListACLRulesCommand(environmentId, vlan.getVlanNum(), network.getId()); + GloboACLRulesResponse response = (GloboACLRulesResponse) callCommand(cmd, network.getDataCenterId()); + for(GloboACLRulesResponse.ACLRule r : response.getRules()){ + rules.add((createFirewallRuleVO(network.getId(), network.getCidr(), r))); + } + }catch(CloudRuntimeException ex){ + // ACL API returns an error when VLAN is inactive + if(!VLAN_NOT_ACTIVATED_MESSAGE.equals(ex.getMessage())){ + throw ex; + } + } + return rules; + } + + private FirewallRuleVO createFirewallRuleVO(Long networkId,String cidr, GloboACLRulesResponse.ACLRule rule) { + List destinations = Arrays.asList(rule.getDestination()); + List sources = Arrays.asList(cidr); //TODO check this data + return new FirewallRuleVO( + rule.getId(), null, rule.getPortStart(), rule.getPortEnd(), rule.getProtocol(), networkId, + CallContext.current().getCallingAccountId(), 0L, + FirewallRule.Purpose.Firewall, sources, destinations, rule.getIcmpCode(), rule.getIcmpType(), null, + FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User + ); + } + + @Override + public void createACLRule(Network network, FirewallRule rule) { + String destinationCidr = rule.getSourceCidrList().get(0); + Integer portStart = rule.getSourcePortStart(); + Integer portEnd = rule.getSourcePortEnd(); + String protocol = rule.getProtocol(); + Integer icmpCode = rule.getIcmpCode(); + Integer icmpType = rule.getIcmpType(); + + if (destinationCidr == null || destinationCidr.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid source CIDR, value should not be empty."); + } + + if (protocol.equals("tcp") || protocol.equals("udp")) { + if (portStart == null) { + throw new InvalidParameterValueException("Start port should not be empty."); + } + if (portEnd != null && portEnd < portStart) { + throw new InvalidParameterValueException("End port should not be greater than start port."); + } + } else if (protocol.equals("icmp")) { + if (icmpCode == null || icmpType == null) { + throw new InvalidParameterValueException("ICMP type and code should not be empty"); + } + } + + CreateACLRuleCommand cmd = createACLRuleCommand(network, rule); + + s_logger.debug("Creating ACL rule" + cmd.getAclRuleDescription()); + + callCommand(cmd, network.getDataCenterId()); + } + + protected CreateACLRuleCommand createACLRuleCommand(Network network, FirewallRule rule) { + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol(rule.getProtocol()); + cmd.setDestinationCidr(rule.getSourceCidrList().get(0)); + cmd.setSourceCidr(network.getCidr()); + cmd.setStartPort(rule.getSourcePortStart()); + cmd.setEndPort(rule.getSourcePortEnd()); + cmd.setIcmpCode(rule.getIcmpCode()); + cmd.setIcmpType(rule.getIcmpType()); + cmd.setVlanNumber(getVlan(network).getVlanNum()); + cmd.setEnvironmentId(getEnvironmentId(network)); + cmd.setAclOwner(getCallingUser()); + return cmd; + } + + @Override + public void removeACLRule(Network network, Long ruleId) { + RemoveACLRuleCommand cmd = new RemoveACLRuleCommand(ruleId, getEnvironmentId(network), getVlan(network).getVlanNum(), getCallingUser()); + s_logger.debug("Removing ACL rule" + ruleId); + callCommand(cmd, network.getDataCenterId()); + } + + protected String getCallingUser() { + return CallContext.current().getCallingUser().getUsername().split("@")[0]; + } + + private Vlan getVlan(Network network) { + return _globoNetworkService.getVlanInfoFromGloboNetwork(network); + } + + private Long getEnvironmentId(Network network) { + return _globoNetworkDao.findByNetworkId(network.getId()).getGloboNetworkEnvironmentId(); + } + + private Answer callCommand(Command cmd, Long zoneId) { + Answer answer = _agentMgr.easySend(getHost(zoneId).getId(), cmd); + if (answer == null || !answer.getResult()) { + String msg = "Error executing command " + cmd; + throw new CloudRuntimeException(answer == null ? msg : answer.getDetails()); + } + return answer; + } + + private HostVO getHost(Long zoneId) { + HostVO aclApiHost = _hostDao.findByTypeNameAndZoneId(zoneId, Network.Provider.GloboAclApi.getName(), Host.Type.L2Networking); + if (aclApiHost == null) { + throw new CloudRuntimeException("Could not find the Globo ACL API resource"); + } + return aclApiHost; + } + + public Host addGloboAclApiHost(Long physicalNetworkId, String url, String username, String password) { + if (url == null || url.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid ACL API URL"); + } + if (username == null || username.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid ACL API username"); + } + if (password == null || password.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid ACL API password"); + } + + PhysicalNetwork physicalNetwork; + if (physicalNetworkId != null) { + physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); + if (physicalNetwork == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("Invalid physicalNetworkId"); + } + + final Map params = new HashMap<>(); + final long zoneId = physicalNetwork.getDataCenterId(); + params.put("guid", "globoaclapi-" + String.valueOf(zoneId)); + params.put("zoneId", String.valueOf(zoneId)); + params.put("name", Network.Provider.GloboAclApi.getName()); + params.put("url", url); + params.put("username", username); + params.put("password", password); + params.put("trustssl", GloboAclTrustSSL.value().toString()); + params.put("timeout", GloboAclTimeout.value().toString()); + + final Map hostDetails = new HashMap<>(); + hostDetails.putAll(params); + + return Transaction.execute(new TransactionCallbackWithException() { + + @Override + public Host doInTransaction(TransactionStatus status) throws CloudRuntimeException { + try { + GloboAclApiResource resource = new GloboAclApiResource(); + resource.configure(Network.Provider.GloboAclApi.getName(), hostDetails); + + Host host = _resourceMgr.addHost(zoneId, resource, resource.getType(), params); + + if (host == null) { + throw new CloudRuntimeException("Failed to add Globo ACL API host"); + } + + return host; + } catch (ConfigurationException e) { + s_logger.error("Error configuring Globo ACL API resource", e); + throw new CloudRuntimeException(e); + } + } + }); + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList<>(); + cmdList.add(AddGloboAclApiHostCmd.class); + cmdList.add(ListGloboACLRulesCmd.class); + cmdList.add(CreateGloboACLRuleCmd.class); + cmdList.add(RemoveGloboACLRuleCmd.class); + return cmdList; + } + + @Override + public String getConfigComponentName() { + return this.getClass().getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ GloboAclTrustSSL, GloboAclTimeout }; + } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLService.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLService.java new file mode 100644 index 000000000000..8a6941fe769b --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/manager/GloboACLService.java @@ -0,0 +1,19 @@ +package com.globo.globoaclapi.cloudstack.manager; + +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; + +import java.util.List; + +public interface GloboACLService { + + List listACLRules(Network network); + + void createACLRule(Network network, FirewallRule rule); + + void removeACLRule(Network network, Long ruleId); + + Host addGloboAclApiHost(Long physicalNetworkId, String url, String username, String password); +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResource.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResource.java new file mode 100644 index 000000000000..df772a3b9fd0 --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResource.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 com.globo.globoaclapi.cloudstack.resource; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.ManagerBase; +import com.globo.aclapi.client.AclAPIException; +import com.globo.aclapi.client.ClientAclAPI; +import com.globo.aclapi.client.model.ICMPOption; +import com.globo.aclapi.client.model.L4Option; +import com.globo.aclapi.client.model.Rule; +import com.globo.globoaclapi.cloudstack.commands.CreateACLRuleCommand; +import com.globo.globoaclapi.cloudstack.commands.ListACLRulesCommand; +import com.globo.globoaclapi.cloudstack.commands.RemoveACLRuleCommand; +import com.globo.globoaclapi.cloudstack.response.GloboACLRulesResponse; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.log4j.Logger; + +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class GloboAclApiResource extends ManagerBase implements ServerResource { + + private String _zoneId; + + private String _guid; + + private String _name; + + protected ClientAclAPI _aclApiClient; + + private static final Logger s_logger = Logger.getLogger(GloboAclApiResource.class); + + @Override + public StartupCommand[] initialize() { + s_logger.trace("initialize called"); + StartupCommand cmd = new StartupCommand(getType()); + cmd.setName(_name); + cmd.setGuid(_guid); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(""); + cmd.setStorageIpAddress(""); + cmd.setVersion(GloboAclApiResource.class.getPackage().getImplementationVersion()); + return new StartupCommand[] {cmd}; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _zoneId = (String)params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _guid = (String)params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find guid"); + } + + _name = (String)params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + if (params.get("url") == null) { + throw new ConfigurationException("Unable to find url"); + } + + if (params.get("username") == null) { + throw new ConfigurationException("Unable to find username"); + } + + if (params.get("password") == null) { + throw new ConfigurationException("Unable to find password"); + } + + _aclApiClient = createACLApiClient(params); + + return true; + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return new ReadyAnswer((ReadyCommand)cmd); + } else if (cmd instanceof MaintainCommand) { + return new MaintainAnswer((MaintainCommand)cmd); + }else if(cmd instanceof CreateACLRuleCommand){ + return execute((CreateACLRuleCommand) cmd); + }else if(cmd instanceof RemoveACLRuleCommand){ + return execute((RemoveACLRuleCommand) cmd); + }else if(cmd instanceof ListACLRulesCommand){ + return execute((ListACLRulesCommand) cmd); + } + return Answer.createUnsupportedCommandAnswer(cmd); + } + + private Answer execute(ListACLRulesCommand cmd) { + try{ + List rules = _aclApiClient.getAclAPI().listByEnvAndNumVlan(cmd.getEnvironmentId(), cmd.getVlanNumber()); + List aclRules = new ArrayList<>(); + for(Rule r : rules) { + aclRules.add(createACLRuleResponse(r)); + } + return new GloboACLRulesResponse(aclRules); + }catch(AclAPIException e){ + s_logger.error("Error while listing ACL Rules.", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(CreateACLRuleCommand cmd) { + try{ + Rule rule = _aclApiClient.getAclAPI().saveSync(cmd.getEnvironmentId(), cmd.getVlanNumber(), createRule(cmd), cmd.getAclOwner()); + return new Answer(cmd, true, "ACL Rule " + rule.getId() + " successfully created."); + }catch(AclAPIException e){ + s_logger.error("Error while creating ACL Rule: " + cmd.getAclRuleDescription(), e); + if(e.getHttpStatus() != null && e.getHttpStatus() == 403){ + return new Answer(cmd, false, "The user " + cmd.getAclOwner() + " don't have the required permission to perform this action."); + }else if(e.getCause() instanceof ConnectTimeoutException){ + return new Answer(cmd, false, "ACL creation timed out. Your request may still being processed by ACL API."); + } else { + return new Answer(cmd, false, e.getMessage()); + } + } + } + + private Answer execute(RemoveACLRuleCommand cmd) { + try { + _aclApiClient.getAclAPI().removeSync(cmd.getEnvironmentId(), cmd.getVlanNumber(), cmd.getRuleId(), cmd.getAclOwner()); + return new Answer(cmd, true, "ACL Rule " + cmd.getRuleId() + " successfully removed."); + }catch(AclAPIException e){ + s_logger.error("Error while removing ACL Rule: " + cmd.getRuleId(), e); + if(e.getHttpStatus() != null && e.getHttpStatus() == 403){ + return new Answer(cmd, false, "The user " + cmd.getAclOwner() + " doesn't have the required permission to perform this action."); + }else if(e.getCause() instanceof ConnectTimeoutException){ + return new Answer(cmd, false, "ACL removal timed out. Your request may still being processed by ACL API."); + } else { + return new Answer(cmd, false, e.getMessage()); + } + } + } + + protected Rule createRule(CreateACLRuleCommand cmd) { + Rule rule = new Rule(); + rule.setAction(Rule.Action.PERMIT); + rule.setProtocol(cmd.getProtocol()); + rule.setDestination(cmd.getDestinationCidr()); + rule.setSource(cmd.getSourceCidr()); + + if(cmd.getProtocol().equals("icmp")){ + rule.setIcmpOptions(new ICMPOption(cmd.getIcmpType(), cmd.getIcmpCode())); + }else{ + L4Option l4Option = new L4Option(); + l4Option.setDestPortStart(cmd.getStartPort()); + if(cmd.getEndPort() == null || cmd.getEndPort().equals(cmd.getStartPort())){ + l4Option.setDestPortOperation("eq"); + }else{ + l4Option.setDestPortOperation("range"); + l4Option.setDestPortEnd(cmd.getEndPort()); + } + rule.setL4Options(l4Option); + } + return rule; + } + + private GloboACLRulesResponse.ACLRule createACLRuleResponse(Rule r) { + GloboACLRulesResponse.ACLRule rule = new GloboACLRulesResponse.ACLRule(); + + Integer destPortStart = r.getL4Options() != null ? r.getL4Options().getDestPortStart() : null; + Integer destPortEnd = r.getL4Options() != null ? r.getL4Options().getDestPortEnd() : null; + Integer code = r.getIcmpOptions() != null ? r.getIcmpOptions().getCode() : null; + Integer type = r.getIcmpOptions() != null ? r.getIcmpOptions().getType() : null; + + rule.setId(r.getId()); + rule.setProtocol(r.getProtocol().name().toLowerCase()); + rule.setDestination(r.getDestination()); + rule.setIcmpType(type); + rule.setIcmpCode(code); + rule.setPortStart(destPortStart); + rule.setPortEnd(destPortEnd); + + return rule; + } + + private ClientAclAPI createACLApiClient(Map params) { + if(_aclApiClient == null){ + String url = (String) params.get("url"); + String username = (String) params.get("username"); + Integer timeout = new Integer((String) params.get("timeout")); + String password = (String) params.get("password"); + boolean verifySSL = params.get("trustssl").equals("true"); + return ClientAclAPI.buildHttpAPI(url, username, password, timeout, verifySSL); + } + return _aclApiClient; + } + + @Override + public Host.Type getType() { + return Host.Type.L2Networking; + } + + @Override + public PingCommand getCurrentStatus(long id) { + return new PingCommand(getType(), id); + } + + @Override + public void disconnected() { } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { } +} diff --git a/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/response/GloboACLRulesResponse.java b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/response/GloboACLRulesResponse.java new file mode 100644 index 000000000000..61e65f52511c --- /dev/null +++ b/plugins/network-elements/globoaclapi/src/com/globo/globoaclapi/cloudstack/response/GloboACLRulesResponse.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.globo.globoaclapi.cloudstack.response; + +import com.cloud.agent.api.Answer; + +import java.util.List; + +public class GloboACLRulesResponse extends Answer { + + protected List rules; + + public GloboACLRulesResponse(){ + super(); + } + + public GloboACLRulesResponse(List rules) { + this.result = true; + this.setRules(rules); + } + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public static class ACLRule { + + private String id; + + private String protocol; + + private String destination; + + private Integer icmpType; + + private Integer icmpCode; + + private Integer destPortStart; + + private Integer destPortEnd; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getPortStart() { + return destPortStart; + } + + public void setPortStart(Integer destPortStart) { + this.destPortStart = destPortStart; + } + + public Integer getPortEnd() { + return destPortEnd; + } + + public void setPortEnd(Integer destPortEnd) { + this.destPortEnd = destPortEnd; + } + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/manager/GloboACLManagerTest.java b/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/manager/GloboACLManagerTest.java new file mode 100644 index 000000000000..5a7b2adf142d --- /dev/null +++ b/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/manager/GloboACLManagerTest.java @@ -0,0 +1,408 @@ +package com.globo.globoaclapi.cloudstack.manager; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.resource.ResourceManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globoaclapi.cloudstack.commands.CreateACLRuleCommand; +import com.globo.globoaclapi.cloudstack.commands.ListACLRulesCommand; +import com.globo.globoaclapi.cloudstack.commands.RemoveACLRuleCommand; +import com.globo.globoaclapi.cloudstack.resource.GloboAclApiResource; +import com.globo.globoaclapi.cloudstack.response.GloboACLRulesResponse; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.GloboNetworkNetworkVO; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkNetworkDao; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import org.apache.cloudstack.acl.ControlledEntity; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GloboACLManagerTest { + + private GloboACLManager manager; + + @Before + public void setUp() throws Exception { + manager = spy(new GloboACLManager()); + doReturn("user").when(manager).getCallingUser(); + } + + @Test + public void testListACLsGivenNoACLsFound(){ + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + + AgentManager agentManager = mockAgentManagerEasySend(new GloboACLRulesResponse(new ArrayList())); + + List rules = manager.listACLRules(new NetworkVO()); + + assertTrue(rules.isEmpty()); + verify(agentManager, times(1)).easySend(anyLong(), any(ListACLRulesCommand.class)); + } + + @Test + public void testListACLsGivenError(){ + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, false, "Error")); + + try{ + manager.listACLRules(new NetworkVO()); + fail(); + }catch(CloudRuntimeException e){ + assertEquals("Error", e.getMessage()); + verify(agentManager, times(1)).easySend(anyLong(), any(ListACLRulesCommand.class)); + } + } + + @Test + public void testListACLsGivenVlanNotActivated(){ + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, false, GloboACLManager.VLAN_NOT_ACTIVATED_MESSAGE)); + + List rules = manager.listACLRules(new NetworkVO()); + + assertEquals(0, rules.size()); + verify(agentManager, times(1)).easySend(anyLong(), any(ListACLRulesCommand.class)); + } + + @Test + public void testListACLsGivenOneACLFound(){ + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + + GloboACLRulesResponse.ACLRule rule = createACLRuleResponse("10.0.0.5/24", 80, null); + AgentManager agentManager = mockAgentManagerEasySend(new GloboACLRulesResponse(Arrays.asList(rule))); + + List rules = manager.listACLRules(new NetworkVO()); + + assertEquals(1, rules.size()); + verify(agentManager, times(1)).easySend(anyLong(), any(ListACLRulesCommand.class)); + } + + @Test + public void testCreateACLGivenRuleWithoutDestinationCIDR() throws Exception { + try{ + manager.createACLRule(createNetwork(), createTCPFirewallRuleVO(" ", 80, 81)); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("Invalid source CIDR, value should not be empty.", e.getMessage()); + } + } + + @Test + public void testCreateACLGivenNotPortsSupplied() throws Exception { + try{ + manager.createACLRule(createNetwork(), createTCPFirewallRuleVO("10.0.0.5/24", null, null)); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("Start port should not be empty.", e.getMessage()); + } + } + + @Test + public void testCreateACLGivenEndPortGreaterThanStartPortSupplied() throws Exception { + try{ + manager.createACLRule(createNetwork(), createTCPFirewallRuleVO("10.0.0.5/24", 80, 70)); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("End port should not be greater than start port.", e.getMessage()); + } + } + + @Test + public void testCreateACLGivenNullIcmpCode() throws Exception { + try{ + manager.createACLRule(createNetwork(), createICMPFirewallRuleVO("10.0.0.5/24", null, 1)); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("ICMP type and code should not be empty", e.getMessage()); + } + } + + @Test + public void testCreateACLGivenNullIcmpType() throws Exception { + try{ + manager.createACLRule(createNetwork(), createICMPFirewallRuleVO("10.0.0.5/24", 1, null)); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("ICMP type and code should not be empty", e.getMessage()); + } + } + + @Test + public void testCreateACLGivenInvalidCommandResponse() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, false, "Invalid ACL rule")); + + try { + manager.createACLRule(createNetwork(), createTCPFirewallRuleVO("10.0.0.5/24", 80, null)); + fail(); + }catch(CloudRuntimeException e){ + assertEquals("Invalid ACL rule", e.getMessage()); + verify(agentManager, times(1)).easySend(anyLong(), any(CreateACLRuleCommand.class)); + } + } + + @Test + public void tesRemoveACLGivenInvalidCommandResponse() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, false, "Invalid ACL rule")); + + try { + manager.removeACLRule(createNetwork(), 1L); + fail(); + }catch(CloudRuntimeException e){ + assertEquals("Invalid ACL rule", e.getMessage()); + verify(agentManager, times(1)).easySend(anyLong(), any(RemoveACLRuleCommand.class)); + } + } + + @Test + public void testCreateACLGivenSuccessCommandResponse() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, true, "ACL rule created")); + + manager.createACLRule(createNetwork(), createTCPFirewallRuleVO("10.0.0.5/24", 80, null)); + verify(agentManager, times(1)).easySend(anyLong(), any(CreateACLRuleCommand.class)); + } + + @Test + public void testRemoveACLGivenSuccessCommandResponse() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + mockGetACLapiHost(); + AgentManager agentManager = mockAgentManagerEasySend(new Answer(null, true, "ACL rule created")); + + manager.removeACLRule(createNetwork(), 1L); + verify(agentManager, times(1)).easySend(anyLong(), any(RemoveACLRuleCommand.class)); + } + + @Test + public void testCreateACLRuleCommandGivenTCPRule() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + + CreateACLRuleCommand cmd = manager.createACLRuleCommand(createNetwork(), createTCPFirewallRuleVO("10.0.0.5/24", 80, 85)); + + assertEquals("tcp", cmd.getProtocol()); + assertEquals("10.0.0.5/24", cmd.getDestinationCidr()); + assertEquals(new Integer(80), cmd.getStartPort()); + assertEquals(new Integer(85), cmd.getEndPort()); + assertEquals(new Long(1), cmd.getVlanNumber()); + assertEquals(new Long(1), cmd.getEnvironmentId()); + } + + @Test + public void testCreateACLRuleCommandGivenICMPRule() throws Exception { + mockGetGloboNetworkVlanNumber(1L); + mockGetGloboNetworkEnvironmentID(1L); + + CreateACLRuleCommand cmd = manager.createACLRuleCommand(createNetwork(), createICMPFirewallRuleVO("10.0.0.5/24", 1, 1)); + + assertEquals("icmp", cmd.getProtocol()); + assertEquals("10.0.0.5/24", cmd.getDestinationCidr()); + assertEquals(new Integer(1), cmd.getIcmpCode()); + assertEquals(new Integer(1), cmd.getIcmpType()); + assertEquals(new Long(1), cmd.getVlanNumber()); + assertEquals(new Long(1), cmd.getEnvironmentId()); + } + + @Test + public void testCreateACLApiHostGivenInvalidURL(){ + try { + manager.addGloboAclApiHost(1L, null, null, null); + fail(); + }catch (InvalidParameterValueException e){ + assertEquals("Invalid ACL API URL", e.getMessage()); + } + } + + @Test + public void testCreateACLApiHostGivenInvalidUsername(){ + try { + manager.addGloboAclApiHost(1L, "https://aclapiurl.com", null, null); + fail(); + }catch (InvalidParameterValueException e){ + assertEquals("Invalid ACL API username", e.getMessage()); + } + } + + @Test + public void testCreateACLApiHostGivenInvalidPassword(){ + try { + manager.addGloboAclApiHost(1L, "https://aclapiurl.com", "username", null); + fail(); + }catch (InvalidParameterValueException e){ + assertEquals("Invalid ACL API password", e.getMessage()); + } + } + + @Test + public void testCreateACLApiHostGivenNullPhysicalNetworkId(){ + try { + manager.addGloboAclApiHost(null, "https://aclapiurl.com", "username", "password"); + fail(); + }catch (InvalidParameterValueException e){ + assertEquals("Invalid physicalNetworkId", e.getMessage()); + } + } + + @Test + public void testCreateACLApiHostGivenInvalidPhysicalNetwork(){ + mockFindPhyisicalNetwork(null); + try { + manager.addGloboAclApiHost(1L, "https://aclapiurl.com", "username", "password"); + fail(); + }catch (InvalidParameterValueException e){ + assertEquals("Unable to find a physical network having the specified physical network id", e.getMessage()); + } + } + + @Test + public void testCreateACLApiHostGivenFailOnAddingHost(){ + mockFindPhyisicalNetwork(new PhysicalNetworkVO(1L, 1L, null, null, null, null, null)); + ResourceManager resourceManagerMock = mockAddHost(null); + try { + manager.addGloboAclApiHost(1L, "https://aclapiurl.com", "username", "password"); + fail(); + }catch (CloudRuntimeException e){ + assertEquals("Failed to add Globo ACL API host", e.getMessage()); + verify(resourceManagerMock, times(1)).addHost(eq(1L), any(GloboAclApiResource.class), eq(Host.Type.L2Networking), any(HashMap.class)); + } + } + + private GloboACLRulesResponse.ACLRule createACLRuleResponse(String destination, Integer portStart, Integer portEnd) { + GloboACLRulesResponse.ACLRule rule = new GloboACLRulesResponse.ACLRule(); + rule.setId("1"); + rule.setProtocol("TCP"); + rule.setDestination(destination); + rule.setPortStart(portStart); + rule.setPortEnd(portEnd); + return rule; + } + + private FirewallRuleVO createTCPFirewallRuleVO(String destination, Integer portStart, Integer portEnd) { + FirewallRuleVO rule = new FirewallRuleVO( + "1", 1L, portStart, portEnd, "tcp", 1, 1, 1, + FirewallRule.Purpose.Firewall, null, null, null, null, + 1l, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User + ); + rule.setSourceCidrList(Arrays.asList(destination)); + return rule; + } + + private FirewallRuleVO createICMPFirewallRuleVO(String destination, Integer icmpCode, Integer icmpType) { + FirewallRuleVO rule = new FirewallRuleVO( + "1", 1L, null, null, "icmp", 1, 1, 1, + FirewallRule.Purpose.Firewall, null, Arrays.asList(destination), icmpCode, icmpType, null, + FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User + ); + rule.setSourceCidrList(Arrays.asList(destination)); + return rule; + } + + private NetworkVO createNetwork() { + NetworkVO network = new NetworkVO( + 1L, Networks.TrafficType.Guest, Networks.Mode.None, + Networks.BroadcastDomainType.Vlan, 1L, 1L, 1L, 1L, "bla", "fake", "eet.net", + Network.GuestType.Shared, 1L, 1L, + ControlledEntity.ACLType.Account, false, 1L, false + ); + network.setNetworkCidr("10.10.10.0/24"); + return network; + } + + private HostVO createMockHost() { + return new HostVO(10L, "Host-1", Host.Type.Routing, null, + "10.0.0.0", null, null, null, null, null, null, null, null, + Status.Up, null, null, null, 10L, 10L, 30L, 10233, null, null, + null, 0, null); + } + + private void mockGetGloboNetworkVlanNumber(long vlanNum) { + GloboNetworkManager globoNetworkService = mock(GloboNetworkManager.class); + Vlan vlan = new Vlan(); + vlan.setVlanNum(vlanNum); + when(globoNetworkService.getVlanInfoFromGloboNetwork(any(Network.class))).thenReturn(vlan); + manager._globoNetworkService = globoNetworkService; + } + + private void mockGetGloboNetworkEnvironmentID(long environmentId) { + GloboNetworkNetworkDao globoNetworkDao = mock(GloboNetworkNetworkDao.class); + GloboNetworkNetworkVO globonetwork = new GloboNetworkNetworkVO(1L, 1L, environmentId); + when(globoNetworkDao.findByNetworkId(any(Long.class))).thenReturn(globonetwork); + manager._globoNetworkDao = globoNetworkDao; + } + + private AgentManager mockAgentManagerEasySend(Answer answer) { + AgentManager agentManager = mock(AgentManager.class); + when(agentManager.easySend(anyLong(), any(Command.class))).thenReturn(answer); + manager._agentMgr = agentManager; + return agentManager; + } + + private void mockGetACLapiHost() { + HostDao hostDao = mock(HostDao.class); + when(hostDao.findByTypeNameAndZoneId(anyLong(), eq(Network.Provider.GloboAclApi.getName()), eq(Host.Type.L2Networking))).thenReturn(createMockHost()); + manager._hostDao = hostDao; + } + + private void mockFindPhyisicalNetwork(PhysicalNetworkVO physicalNetwork) { + PhysicalNetworkDao physicalNetworkDaoMock = mock(PhysicalNetworkDao.class); + when(physicalNetworkDaoMock.findById(anyLong())).thenReturn(physicalNetwork); + manager._physicalNetworkDao = physicalNetworkDaoMock; + } + + private ResourceManager mockAddHost(Host host) { + ResourceManager resourceManagerMock = mock(ResourceManager.class); + when(resourceManagerMock.addHost(eq(1L), any(GloboAclApiResource.class), eq(Host.Type.L2Networking), any(HashMap.class))).thenReturn(host); + manager._resourceMgr = resourceManagerMock; + return resourceManagerMock; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResourceTest.java b/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResourceTest.java new file mode 100644 index 000000000000..05572d6ba349 --- /dev/null +++ b/plugins/network-elements/globoaclapi/test/com/globo/globoaclapi/cloudstack/resource/GloboAclApiResourceTest.java @@ -0,0 +1,244 @@ +package com.globo.globoaclapi.cloudstack.resource; + +import com.cloud.agent.api.Answer; +import com.globo.aclapi.client.AclAPIException; +import com.globo.aclapi.client.ClientAclAPI; +import com.globo.aclapi.client.api.RuleAPI; +import com.globo.aclapi.client.model.L4Option; +import com.globo.aclapi.client.model.Rule; +import com.globo.globoaclapi.cloudstack.commands.CreateACLRuleCommand; +import com.globo.globoaclapi.cloudstack.commands.ListACLRulesCommand; +import com.globo.globoaclapi.cloudstack.commands.RemoveACLRuleCommand; +import com.globo.globoaclapi.cloudstack.response.GloboACLRulesResponse; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GloboAclApiResourceTest { + + private GloboAclApiResource globoAclApiResource; + + @Before + public void setUp() throws Exception { + globoAclApiResource = spy(new GloboAclApiResource()); + } + + @Test + public void testCreateTcpACLRuleAndNoEndPort(){ + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol("tcp"); + cmd.setDestinationCidr("10.1.1.0/24"); + cmd.setSourceCidr("192.168.1.0/24"); + cmd.setStartPort(80); + + Rule rule = globoAclApiResource.createRule(cmd); + + assertEquals("TCP", rule.getProtocol().name()); + assertEquals("10.1.1.0/24", rule.getDestination()); + assertEquals("192.168.1.0/24", rule.getSource()); + assertEquals(new Integer(80), rule.getL4Options().getDestPortStart()); + assertEquals("eq", rule.getL4Options().getDestPortOperation()); + } + + @Test + public void testCreateTcpACLRuleWithEqualStartAndEndPorts(){ + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol("tcp"); + cmd.setDestinationCidr("10.1.1.0/24"); + cmd.setSourceCidr("192.168.1.0/24"); + cmd.setStartPort(80); + cmd.setEndPort(80); + + Rule rule = globoAclApiResource.createRule(cmd); + + assertEquals("TCP", rule.getProtocol().name()); + assertEquals("10.1.1.0/24", rule.getDestination()); + assertEquals("192.168.1.0/24", rule.getSource()); + assertEquals(new Integer(80), rule.getL4Options().getDestPortStart()); + assertEquals("eq", rule.getL4Options().getDestPortOperation()); + } + + @Test + public void testCreateTcpACLRule(){ + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol("tcp"); + cmd.setDestinationCidr("10.1.1.0/24"); + cmd.setSourceCidr("192.168.1.0/24"); + cmd.setStartPort(80); + cmd.setEndPort(88); + + Rule rule = globoAclApiResource.createRule(cmd); + + assertEquals("TCP", rule.getProtocol().name()); + assertEquals("10.1.1.0/24", rule.getDestination()); + assertEquals("192.168.1.0/24", rule.getSource()); + assertEquals(new Integer(80), rule.getL4Options().getDestPortStart()); + assertEquals(new Integer(88), rule.getL4Options().getDestPortEnd()); + assertEquals("range", rule.getL4Options().getDestPortOperation()); + } + + @Test + public void testCreateIcmpACLRule(){ + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol("icmp"); + cmd.setDestinationCidr("10.1.1.0/24"); + cmd.setSourceCidr("192.168.1.0/24"); + cmd.setIcmpCode(1); + cmd.setIcmpType(3); + + Rule rule = globoAclApiResource.createRule(cmd); + + assertEquals("ICMP", rule.getProtocol().name()); + assertEquals("10.1.1.0/24", rule.getDestination()); + assertEquals("192.168.1.0/24", rule.getSource()); + assertEquals(new Integer(1), rule.getIcmpOptions().getCode()); + assertEquals(new Integer(3), rule.getIcmpOptions().getType()); + } + + @Test + public void testCreateACLGivenApiError(){ + RuleAPI ruleAPI = mockACLClientApiSaveSync(new AclAPIException("error")); + + Answer answer = globoAclApiResource.executeRequest(createCreateCmd()); + assertFalse(answer.getResult()); + assertEquals("error", answer.getDetails()); + verify(ruleAPI, times(1)).saveSync(eq(1L), eq(1L), any(Rule.class), anyString()); + } + + @Test + public void testCreateACLGivenRuleSuccessfullyCreated(){ + RuleAPI ruleAPI = mockACLClientApiSaveSync(new Rule()); + + Answer answer = globoAclApiResource.executeRequest(createCreateCmd()); + assertTrue(answer.getResult()); + verify(ruleAPI, times(1)).saveSync(eq(1L), eq(1L), any(Rule.class), anyString()); + } + + @Test + public void testRemoveACLGivenApiError(){ + RemoveACLRuleCommand cmd = createRemoveCmd(); + RuleAPI ruleAPI = mockACLClientApiRemoveSync(new AclAPIException("error"), cmd); + + Answer answer = globoAclApiResource.executeRequest(cmd); + assertFalse(answer.getResult()); + assertEquals("error", answer.getDetails()); + verify(ruleAPI, times(1)).removeSync(eq(1L), eq(1L), any(Long.class), anyString()); + } + + @Test + public void testRemoveACLGivenRuleSuccessfullyRemoved(){ + RemoveACLRuleCommand cmd = createRemoveCmd(); + RuleAPI ruleAPI = mockACLClientApiRemoveSync(null, cmd); + + Answer answer = globoAclApiResource.executeRequest(cmd); + assertTrue(answer.getResult()); + verify(ruleAPI, times(1)).removeSync(eq(1L), eq(1L), eq(1L), anyString()); + } + + @Test + public void testListACLRulesGivenNoRulesFound(){ + ListACLRulesCommand cmd = new ListACLRulesCommand(1L, 1L, 1L); + RuleAPI ruleAPI = mockAclApiClientListByEnvAndVlan(new ArrayList()); + + GloboACLRulesResponse answer = (GloboACLRulesResponse) globoAclApiResource.executeRequest(cmd); + + verify(ruleAPI).listByEnvAndNumVlan(1L, 1L); + assertEquals(0, answer.getRules().size()); + } + + @Test + public void testListACLRules(){ + Rule rule = new Rule(); + rule.setId("1"); + rule.setAction(Rule.Action.PERMIT); + rule.setProtocol("tcp"); + rule.setDestination("10.1.1.0/24"); + rule.setSource("192.168.1.0/24"); + L4Option l4Options = new L4Option(); + l4Options.setDestPortStart(80); + l4Options.setDestPortEnd(88); + l4Options.setDestPortOperation("range"); + rule.setL4Options(l4Options); + + RuleAPI ruleAPI = mockAclApiClientListByEnvAndVlan(Arrays.asList(rule)); + + ListACLRulesCommand cmd = new ListACLRulesCommand(1L, 1L, 1L); + GloboACLRulesResponse answer = (GloboACLRulesResponse) globoAclApiResource.executeRequest(cmd); + + verify(ruleAPI).listByEnvAndNumVlan(1L, 1L); + assertEquals(1, answer.getRules().size()); + GloboACLRulesResponse.ACLRule fwRule = answer.getRules().get(0); + + assertEquals(rule.getProtocol().name().toLowerCase(), fwRule.getProtocol()); + assertEquals(rule.getId(), fwRule.getId()); + assertEquals(rule.getDestination(), fwRule.getDestination()); + assertEquals(rule.getL4Options().getDestPortStart(), fwRule.getPortStart()); + assertEquals(rule.getL4Options().getDestPortEnd(), fwRule.getPortEnd()); + } + + private RuleAPI mockACLClientApiRemoveSync(Object response, RemoveACLRuleCommand cmd) { + ClientAclAPI aclAPI = mock(ClientAclAPI.class); + RuleAPI ruleAPI = mock(RuleAPI.class); + when(aclAPI.getAclAPI()).thenReturn(ruleAPI); + if(response instanceof AclAPIException) { + doThrow((AclAPIException)response).when(ruleAPI).removeSync(anyLong(), anyLong(), any(Long.class), anyString()); + } + globoAclApiResource._aclApiClient = aclAPI; + return ruleAPI; + } + + private RuleAPI mockACLClientApiSaveSync(Object response) { + ClientAclAPI aclAPI = mock(ClientAclAPI.class); + RuleAPI ruleAPI = mock(RuleAPI.class); + when(aclAPI.getAclAPI()).thenReturn(ruleAPI); + if(response instanceof Rule) { + when(ruleAPI.saveSync(anyLong(), anyLong(), any(Rule.class), anyString())).thenReturn((Rule) response); + }else{ + when(ruleAPI.saveSync(anyLong(), anyLong(), any(Rule.class), anyString())).thenThrow((AclAPIException) response); + } + globoAclApiResource._aclApiClient = aclAPI; + return ruleAPI; + } + + private RuleAPI mockAclApiClientListByEnvAndVlan(List rules) { + ClientAclAPI aclAPI = mock(ClientAclAPI.class); + RuleAPI ruleAPI = mock(RuleAPI.class); + when(aclAPI.getAclAPI()).thenReturn(ruleAPI); + when(ruleAPI.listByEnvAndNumVlan(anyLong(), anyLong())).thenReturn(rules); + globoAclApiResource._aclApiClient = aclAPI; + return ruleAPI; + } + + private CreateACLRuleCommand createCreateCmd() { + CreateACLRuleCommand cmd = new CreateACLRuleCommand(); + cmd.setProtocol("tcp"); + cmd.setDestinationCidr("10.1.1.0/24"); + cmd.setSourceCidr("192.168.1.0/24"); + cmd.setStartPort(80); + cmd.setEndPort(88); + cmd.setVlanNumber(1L); + cmd.setEnvironmentId(1L); + return cmd; + } + + private RemoveACLRuleCommand createRemoveCmd() { + return new RemoveACLRuleCommand(1L, 1L, 1L, "user"); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml index a1b914a9387d..c06f5575b8c1 100644 --- a/plugins/network-elements/globodns/pom.xml +++ b/plugins/network-elements/globodns/pom.xml @@ -31,7 +31,7 @@ com.globo.globodns globodns-client - 0.0.23 + 0.0.27 diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsNetworkVO.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsNetworkVO.java new file mode 100644 index 000000000000..3391aa84c9d5 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsNetworkVO.java @@ -0,0 +1,73 @@ +package com.globo.globodns.cloudstack; + +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT 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 javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "dnsapi_network_ref") +public class GloboDnsNetworkVO implements InternalIdentity { + + private static final long serialVersionUID = 6194042778939974188L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "network_id") + private long networkId; + + @Column(name = "dnsapi_domain_id") + private long globoDnsDomainId; + + @Column(name = "dnsapi_reverse_domain_id") + private long globoDnsReverseDomainId; + + public GloboDnsNetworkVO() { + } + + public GloboDnsNetworkVO(long networkId, long globoDnsDomainId, long globoDnsReverseDomainId) { + this.networkId = networkId; + this.globoDnsDomainId = globoDnsDomainId; + this.globoDnsReverseDomainId = globoDnsReverseDomainId; + } + + public long getId() { + return id; + } + + public long getNetworkId() { + return networkId; + } + + public long getGloboDnsDomainId() { + return globoDnsDomainId; + } + + public long getGloboDnsReverseDomainId() { + return globoDnsReverseDomainId; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsVirtualMachineVO.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsVirtualMachineVO.java new file mode 100644 index 000000000000..5c99b45c18ea --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/GloboDnsVirtualMachineVO.java @@ -0,0 +1,86 @@ +package com.globo.globodns.cloudstack; + +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT 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 javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "dnsapi_vm_ref") +public class GloboDnsVirtualMachineVO implements InternalIdentity { + + private static final long serialVersionUID = -1387637638203630926L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "vm_id") + private Long vmId; + + @Column(name = "dnsapi_domain_id") + private Long globoDnsDomainId; + + @Column(name = "dnsapi_record_id") + private Long globoDnsRecordId; + + public GloboDnsVirtualMachineVO() { + } + + public GloboDnsVirtualMachineVO(Long vmId, Long globoDnsDomainId, Long globoDnsRecordId) { + this.vmId = vmId; + this.globoDnsDomainId = globoDnsDomainId; + this.globoDnsRecordId = globoDnsRecordId; + } + + @Override + public long getId() { + return id; + } + + public Long getVirtualMachineId() { + return vmId; + } + + public void setVirtualMachineId(Long vmId) { + this.vmId = vmId; + } + + public Long getGloboDnsDomainId() { + return globoDnsDomainId; + } + + public void setGloboDnsDomainId(Long globoDnsDomainId) { + this.globoDnsDomainId = globoDnsDomainId; + } + + public Long getGloboDnsRecordId() { + return globoDnsRecordId; + } + + public void setGloboDnsRecordId(Long globoDnsRecordId) { + this.globoDnsRecordId = globoDnsRecordId; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateLbRecordAndReverseCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateLbRecordAndReverseCommand.java new file mode 100644 index 000000000000..246bfeb849e8 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateLbRecordAndReverseCommand.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.globo.globodns.cloudstack.commands; + +import com.cloud.agent.api.Command; +import com.globo.globodns.cloudstack.element.GloboDnsTO; + +public class CreateLbRecordAndReverseCommand extends Command { + + private String lbRecordName; + + private String lbRecordIp; + + private String lbDomain; + + private Long reverseTemplateId; + + private boolean override; + + public CreateLbRecordAndReverseCommand(String lbRecordName, String lbRecordIp, String lbDomain, Long reverseTemplateId, boolean override) { + this.lbRecordName = lbRecordName; + this.lbRecordIp = lbRecordIp; + this.lbDomain = lbDomain; + this.reverseTemplateId = reverseTemplateId; + this.override = override; + } + public CreateLbRecordAndReverseCommand(GloboDnsTO globoDns, Long reverseTemplateId, boolean override) { + this.lbRecordName = globoDns.getRecord(); + this.lbRecordIp = globoDns.getIpAddress(); + this.lbDomain = globoDns.getDomain(); + this.reverseTemplateId = reverseTemplateId; + this.override = override; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getLbRecordName() { + return this.lbRecordName; + } + + public String getLbRecordIp() { + return this.lbRecordIp; + } + + public String getLbDomain() { + return this.lbDomain; + } + + public Long getReverseTemplateId() { + return reverseTemplateId; + } + + public boolean isOverride() { + return override; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java index a5bafe95dea4..40d329845dd5 100644 --- a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/CreateOrUpdateRecordAndReverseCommand.java @@ -30,12 +30,18 @@ public class CreateOrUpdateRecordAndReverseCommand extends Command { private boolean override; - public CreateOrUpdateRecordAndReverseCommand(String recordName, String recordIp, String networkDomain, Long reverseTemplateId, boolean override) { + private boolean isIpv6; + + + public CreateOrUpdateRecordAndReverseCommand(String recordName, String recordIp, String networkDomain, Long reverseTemplateId, + boolean override, + boolean isIpv6) { this.recordName = recordName; this.recordIp = recordIp; this.networkDomain = networkDomain; this.reverseTemplateId = reverseTemplateId; this.override = override; + this.isIpv6 = isIpv6; } @Override @@ -63,4 +69,12 @@ public boolean isOverride() { return override; } + public boolean isIpv6() { + return isIpv6; + } + + public void setIpv6(boolean isIpv6) { + this.isIpv6 = isIpv6; + } + } diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java index 3479b26c39d4..09d478486a2b 100644 --- a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/RemoveRecordCommand.java @@ -26,13 +26,13 @@ public class RemoveRecordCommand extends Command { private String networkDomain; - private boolean override; + private boolean isIpv6; - public RemoveRecordCommand(String recordName, String recordIp, String networkDomain, boolean override) { + public RemoveRecordCommand(String recordName, String recordIp, String networkDomain, boolean isIpv6) { this.recordName = recordName; this.recordIp = recordIp; this.networkDomain = networkDomain; - this.override = override; + this.isIpv6 = isIpv6; } @Override @@ -52,8 +52,10 @@ public String getNetworkDomain() { return networkDomain; } - public boolean isOverride() { - return override; + public boolean isIpv6() { + return isIpv6; + } + public void setIpv6(boolean isIpv6) { + this.isIpv6 = isIpv6; } - } diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/ValidateLbRecordCommand.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/ValidateLbRecordCommand.java new file mode 100644 index 000000000000..f53af017f1aa --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/commands/ValidateLbRecordCommand.java @@ -0,0 +1,66 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globodns.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ValidateLbRecordCommand extends Command { + + private String lbRecordName; + + private String lbRecordContent; + + private String lbDomain; + + private boolean override; + + private boolean skipDnsError; + + public ValidateLbRecordCommand(String lbRecordName, String lbRecordContent, String lbDomain, boolean override, boolean skipDnsError) { + this.lbRecordName = lbRecordName; + this.lbRecordContent = lbRecordContent; + this.lbDomain = lbDomain; + this.override = override; + this.skipDnsError = skipDnsError; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getLbRecordName() { + return this.lbRecordName; + } + + public String getLbRecordContent() { + return this.lbRecordContent; + } + + public String getLbDomain() { + return this.lbDomain; + } + + public boolean isOverride() { + return override; + } + + public boolean isSkipDnsError() { + return skipDnsError; + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDao.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDao.java new file mode 100644 index 000000000000..41ebd3c0effa --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDao.java @@ -0,0 +1,25 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globodns.cloudstack.dao; + +import com.cloud.utils.db.GenericDao; +import com.globo.globodns.cloudstack.GloboDnsNetworkVO; + +public interface GloboDnsNetworkDao extends GenericDao { + + GloboDnsNetworkVO findByNetworkId(long networkId); +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDaoImpl.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDaoImpl.java new file mode 100644 index 000000000000..da8158511fa6 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsNetworkDaoImpl.java @@ -0,0 +1,49 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globodns.cloudstack.dao; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globodns.cloudstack.GloboDnsNetworkVO; + +@Component +@DB +public class GloboDnsNetworkDaoImpl extends GenericDaoBase implements GloboDnsNetworkDao { + + final SearchBuilder networkIdSearch; + + protected GloboDnsNetworkDaoImpl() { + super(); + + networkIdSearch = createSearchBuilder(); + networkIdSearch.and("network_id", networkIdSearch.entity().getNetworkId(), Op.EQ); + networkIdSearch.done(); + } + + @Override + public GloboDnsNetworkVO findByNetworkId(long networkId) { + SearchCriteria sc = networkIdSearch.create(); + sc.setParameters("network_id", networkId); + return findOneBy(sc); + } + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDao.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDao.java new file mode 100644 index 000000000000..497f1ecdb02b --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDao.java @@ -0,0 +1,26 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globodns.cloudstack.dao; + +import com.cloud.utils.db.GenericDao; +import com.globo.globodns.cloudstack.GloboDnsVirtualMachineVO; + +public interface GloboDnsVirtualMachineDao extends GenericDao { + + public GloboDnsVirtualMachineVO findByVirtualMachineIdAndDomainId(Long vmId, Long globoDnsDomainId); + +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDaoImpl.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDaoImpl.java new file mode 100644 index 000000000000..767e70dc9b20 --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/dao/GloboDnsVirtualMachineDaoImpl.java @@ -0,0 +1,51 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globodns.cloudstack.dao; + + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globodns.cloudstack.GloboDnsVirtualMachineVO; + +@Component +@DB +public class GloboDnsVirtualMachineDaoImpl extends GenericDaoBase implements GloboDnsVirtualMachineDao { + + protected final SearchBuilder VmAndDomainSearch; + + public GloboDnsVirtualMachineDaoImpl() { + VmAndDomainSearch = createSearchBuilder(); + VmAndDomainSearch.and("vmId", VmAndDomainSearch.entity().getVirtualMachineId(), Op.EQ); + VmAndDomainSearch.and("domainId", VmAndDomainSearch.entity().getGloboDnsDomainId(), Op.EQ); + VmAndDomainSearch.done(); + } + + @Override + public GloboDnsVirtualMachineVO findByVirtualMachineIdAndDomainId(Long vmId, Long globoDnsDomainId) { + SearchCriteria sc = VmAndDomainSearch.create(); + sc.addAnd("vmId", Op.EQ, vmId); +// sc.addAnd("domainId", Op.EQ, globoDnsDomainId); +// sc.setParameters("vmId", vmId); + sc.setParameters("domainId", globoDnsDomainId); + return findOneBy(sc); + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java index 28b2988cf6ab..3f7db0c49949 100644 --- a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElement.java @@ -17,7 +17,9 @@ package com.globo.globodns.cloudstack.element; +import com.cloud.network.element.DnsServiceProvider; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,8 +28,13 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; + import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -53,7 +60,6 @@ import com.cloud.network.PhysicalNetwork; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.dao.PhysicalNetworkDao; -import com.cloud.network.element.NetworkElement; import com.cloud.offering.NetworkOffering; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceStateAdapter; @@ -70,15 +76,17 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.globo.globodns.cloudstack.api.AddGloboDnsHostCmd; +import com.globo.globodns.cloudstack.commands.CreateLbRecordAndReverseCommand; import com.globo.globodns.cloudstack.commands.CreateOrUpdateDomainCommand; import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand; import com.globo.globodns.cloudstack.commands.RemoveDomainCommand; import com.globo.globodns.cloudstack.commands.RemoveRecordCommand; import com.globo.globodns.cloudstack.commands.SignInCommand; +import com.globo.globodns.cloudstack.commands.ValidateLbRecordCommand; import com.globo.globodns.cloudstack.resource.GloboDnsResource; @Component -public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter, NetworkElement, GloboDnsElementService, Configurable { +public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter, DnsServiceProvider, GloboDnsElementService, Configurable { private static final Logger s_logger = Logger.getLogger(GloboDnsElement.class); @@ -88,7 +96,13 @@ public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter "Template id to be used when creating domains in GloboDNS", true, ConfigKey.Scope.Global); private static final ConfigKey GloboDNSOverride = new ConfigKey("Advanced", Boolean.class, "globodns.override.entries", "true", "Allow GloboDns to override entries that already exist", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboDNSLbOverride = new ConfigKey("Advanced", Boolean.class, "globodns.override.lb.entries", "false", + "Allow GloboDns to override entries for load balancer names that already exist", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboDNSLbBlacklistedDomains = new ConfigKey<>("Advanced", String.class, "globodns.lb.domain.blacklist", "", + "List of comma separated domains that should be blacklisted for load balancers", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboDNSVmSkipDnsError = new ConfigKey("Advanced", Boolean.class, "globodns.vm.skipdnserror", "false", + "when false if dns integration failed, the vm will not be created and throw an exception, else vm will be created and dns it could be register later", true, ConfigKey.Scope.Global); // DAOs @Inject DataCenterDao _dcDao; @@ -97,6 +111,9 @@ public class GloboDnsElement extends AdapterBase implements ResourceStateAdapter @Inject PhysicalNetworkDao _physicalNetworkDao; + @Inject + GloboResourceConfigurationDao _globoResourceConfigDao; + // Managers @Inject AgentManager _agentMgr; @@ -122,10 +139,6 @@ public boolean implement(final Network network, NetworkOffering offering, Deploy return true; } - protected String hostNameOfVirtualMachine(VirtualMachineProfile vm) { - return vm.getHostName().toLowerCase(); - } - @Override @DB public boolean prepare(final Network network, final NicProfile nic, final VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) @@ -145,15 +158,36 @@ public boolean prepare(final Network network, final NicProfile nic, final Virtua /* Create new A record in GloboDNS */ // We allow only lower case names in DNS, so force lower case names for VMs String vmName = vm.getHostName(); - String vmHostname = hostNameOfVirtualMachine(vm); + String vmHostname = vm.getHostName().toLowerCase(); if (!vmName.equals(vmHostname) && vm.getType() == VirtualMachine.Type.User) { throw new InvalidParameterValueException("VM name should contain only lower case letters and digits: " + vmName + " - " + vm); } - CreateOrUpdateRecordAndReverseCommand cmd = new CreateOrUpdateRecordAndReverseCommand(vmHostname, nic.getIPv4Address(), network.getNetworkDomain(), - GloboDNSTemplateId.value(), GloboDNSOverride.value()); - callCommand(cmd, zoneId); - return true; + boolean isIpv6 = nic.getIPv6Address() != null; + String ipAddress = isIpv6 ? nic.getIPv6Address() : nic.getIPv4Address(); + + + GloboResourceConfigurationVO forceConfig = _globoResourceConfigDao.getFirst(GloboResourceType.VM_NIC, nic.getUuid(), GloboResourceKey.skipDnsError); + boolean forceDoaminRegister = forceConfig != null ? forceConfig.getBooleanValue() : GloboDNSVmSkipDnsError.value(); // false while the config is not persisted by vm flow + + return registerVmDomain(zoneId, nic.getUuid(), vmHostname, ipAddress, network.getNetworkDomain(), isIpv6, forceDoaminRegister); + } + + @Override + public boolean registerVmDomain(Long zoneId, String nicUuid, String hostName, String ipAddress, String networkDomain, boolean isIpv6, boolean skipDnsError) { + GloboResourceConfigurationVO dnsRegisteredConfig = _globoResourceConfigDao.getFirst(GloboResourceType.VM_NIC, nicUuid, GloboResourceKey.isDNSRegistered); + boolean isDnsRegistered = dnsRegisteredConfig != null ? dnsRegisteredConfig.getBooleanValue() : false; + + if (isDnsRegistered) { + return true; + } + + CreateOrUpdateRecordAndReverseCommand cmd = new CreateOrUpdateRecordAndReverseCommand(hostName, ipAddress, networkDomain, + GloboDNSTemplateId.value(), GloboDNSOverride.value(), isIpv6); + + + return registerRecord(cmd, zoneId, nicUuid, GloboResourceType.VM_NIC, skipDnsError); + } @Override @@ -172,8 +206,19 @@ public boolean release(final Network network, NicProfile nic, final VirtualMachi throw new CloudRuntimeException("Could not find zone associated to this network"); } - RemoveRecordCommand cmd = new RemoveRecordCommand(hostNameOfVirtualMachine(vm), nic.getIPv4Address(), network.getNetworkDomain(), GloboDNSOverride.value()); + + boolean isIpv6 = nic.getIPv6Address() != null; + String ipAddress = isIpv6 ? nic.getIPv6Address() : nic.getIPv4Address(); + + RemoveRecordCommand cmd = new RemoveRecordCommand(hostNameOfVirtualMachine(vm), ipAddress, network.getNetworkDomain(), isIpv6); + callCommand(cmd, zoneId); + + List configurationList = _globoResourceConfigDao.getConfiguration(GloboResourceType.VM_NIC, nic.getUuid(), GloboResourceKey.isDNSRegistered); + for (GloboResourceConfigurationVO globoResourceConfigurationVO : configurationList){ + _globoResourceConfigDao.remove(String.valueOf(globoResourceConfigurationVO.getId())); + } + return true; } @@ -182,6 +227,10 @@ public boolean shutdown(Network network, ReservationContext context, boolean cle return true; } + protected String hostNameOfVirtualMachine(VirtualMachineProfile vm) { + return vm.getHostName().toLowerCase(); + } + @Override @DB public boolean destroy(final Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { @@ -196,24 +245,6 @@ public boolean destroy(final Network network, ReservationContext context) throws return true; } - ///////// Provider control methods //////////// - private Answer callCommand(Command cmd, Long zoneId) { - - HostVO globoDnsHost = getGloboDnsHost(zoneId); - if (globoDnsHost == null) { - throw new CloudRuntimeException("Could not find the GloboDNS resource"); - } - - Answer answer = _agentMgr.easySend(globoDnsHost.getId(), cmd); - if (answer == null || !answer.getResult()) { - String msg = "Error executing command " + cmd; - msg = answer == null ? msg : answer.getDetails(); - throw new CloudRuntimeException(msg); - } - - return answer; - } - @Override public Map> getCapabilities() { return capabilities; @@ -266,7 +297,7 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {GloboDNSTemplateId, GloboDNSOverride}; + return new ConfigKey[] {GloboDNSTemplateId, GloboDNSOverride, GloboDNSLbOverride, GloboDNSLbBlacklistedDomains, GloboDNSVmSkipDnsError}; } ////////// Resource/Host methods //////////// @@ -307,7 +338,11 @@ public boolean configure(String name, Map params) throws Configu } private HostVO getGloboDnsHost(Long zoneId) { - return _hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboDns.getName(), Type.L2Networking); + HostVO host = _hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboDns.getName(), Type.L2Networking); + if (host == null) { + throw new CloudRuntimeException("Could not find the GloboDNS resource"); + } + return host; } @Override @@ -383,4 +418,169 @@ public Host doInTransaction(TransactionStatus status) throws CloudRuntimeExcepti return host; } + + // Load Balancing methods + @Override + public boolean validateDnsRecordForLoadBalancer(String lbDomain, String lbRecord, String lbRecordContent, Long zoneId, boolean skipDnsError) { + s_logger.debug("Validating LB DNS record " + lbRecord + " in domain " + lbDomain); + if (lbRecord.contains("_")) { + throw new InvalidParameterValueException("Underscore(_) is not allowed for load balancer name"); + } + + this.checkForBlacklistedDomain(lbDomain, lbRecord); + + DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new CloudRuntimeException("Could not find zone with ID " + zoneId); + } + + ValidateLbRecordCommand cmd = new ValidateLbRecordCommand(lbRecord, lbRecordContent, lbDomain, GloboDNSLbOverride.value(), skipDnsError); + Answer answer = easyCallCommand(cmd, zoneId); + if (answer == null || !answer.getResult()) { + // Could not sign in on GloboDNS + throw new InvalidParameterValueException("Could not validate LB record " + lbRecord + ". " + (answer == null ? "" : answer.getDetails())); + } + return answer.getResult(); + } + + protected void checkForBlacklistedDomain(String lbDomain, String lbRecord) { + List blacklist = getBlackListedDomains(); + + String lbName = lbRecord + "." + lbDomain; + for(String blacklistedDomain : blacklist){ + if(!blacklistedDomain.equals("") && lbName.contains(blacklistedDomain)){ + throw new InvalidParameterValueException("Invalid load balancer name, it cannot contain the the domain '"+ blacklistedDomain +"'"); + } + } + } + + protected List getBlackListedDomains() { + return Arrays.asList(GloboDNSLbBlacklistedDomains.value().split(",")); + } + + @Override + @Deprecated + public boolean createDnsRecordForLoadBalancer(String lbDomain, String lbRecord, String lbIpAddress, Long zoneId) { + s_logger.debug("Creating LB DNS record " + lbRecord + " in domain " + lbDomain); + DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new CloudRuntimeException("Could not find zone with ID " + zoneId); + } + + CreateLbRecordAndReverseCommand cmd = new CreateLbRecordAndReverseCommand(lbRecord, lbIpAddress, lbDomain, GloboDNSTemplateId.value(), GloboDNSLbOverride.value()); + callCommand(cmd, zoneId); + return true; + } + + @Override + public boolean createDnsRecordForLoadBalancer(GloboDnsTO globoDns, boolean skipDnsError) { + s_logger.debug("Creating LB DNS record " + globoDns.getRecord() + " in domain " + globoDns.getDomain()); + DataCenter zone = _dcDao.findById(globoDns.getZoneId()); + if (zone == null) { + throw new CloudRuntimeException("Could not find zone with ID " + globoDns.getZoneId()); + } + + CreateLbRecordAndReverseCommand cmd = new CreateLbRecordAndReverseCommand(globoDns, + GloboDNSTemplateId.value(), + GloboDNSLbOverride.value()); + + return registerRecord(cmd, globoDns.getZoneId(), globoDns.getResourceId(), GloboResourceType.LOAD_BALANCER, skipDnsError); + } + + + @Override + public boolean removeDnsRecordForLoadBalancer(String lbUuid, String lbDomain, String lbRecord, String lbIpAddress, Long zoneId) { + s_logger.debug("Removing LB DNS record " + lbRecord + " from domain " + lbDomain); + DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new CloudRuntimeException("Could not find zone with ID " + zoneId); + } + + RemoveRecordCommand cmd = new RemoveRecordCommand(lbRecord, lbIpAddress, lbDomain, false); + callCommand(cmd, zoneId); + + + List configurationList = _globoResourceConfigDao.getConfiguration(GloboResourceType.LOAD_BALANCER, lbUuid, GloboResourceKey.isDNSRegistered); + for (GloboResourceConfigurationVO globoResourceConfigurationVO : configurationList){ + _globoResourceConfigDao.remove(String.valueOf(globoResourceConfigurationVO.getId())); + } + + return true; + } + + private boolean registerRecord(Command cmd, Long zoneId, String resourceId, GloboResourceType resourceType, boolean skipDnsError){ + GloboResourceConfigurationVO dnsRegisteredConfig = _globoResourceConfigDao.getFirst(resourceType, resourceId, GloboResourceKey.isDNSRegistered); + boolean isDnsRegistered = dnsRegisteredConfig != null ? dnsRegisteredConfig.getBooleanValue() : false; + + if (isDnsRegistered) { + return true; + } + + HostVO globoDnsHost = getGloboDnsHost(zoneId); + Answer answer = _agentMgr.easySend(globoDnsHost.getId(), cmd); + + if (answer == null) { + throw new CloudRuntimeException("Error create " + resourceType.toString() + " record while send command to dns resource"); + + } + + if (!answer.getResult()){ + if (Answer.AnswerTypeError.DNS_IO_ERROR.equals(answer.getTypeError()) && skipDnsError) { + if (dnsRegisteredConfig == null) { + dnsRegisteredConfig = new GloboResourceConfigurationVO(resourceType, resourceId, GloboResourceKey.isDNSRegistered, Boolean.FALSE.toString()); + _globoResourceConfigDao.persist(dnsRegisteredConfig); + } else { + dnsRegisteredConfig.setBoolValue(false); + _globoResourceConfigDao.updateValue(dnsRegisteredConfig); + } + + } else { + throw new CloudRuntimeException("Error trying register " + resourceType.toString() + " record. Error: " + answer.getDetails()); + } + } else { + if (dnsRegisteredConfig == null) { + dnsRegisteredConfig = new GloboResourceConfigurationVO(resourceType, resourceId, GloboResourceKey.isDNSRegistered, Boolean.TRUE.toString()); + _globoResourceConfigDao.persist(dnsRegisteredConfig); + } else { + dnsRegisteredConfig.setBoolValue(true); + _globoResourceConfigDao.updateValue(dnsRegisteredConfig); + } + + } + + return answer.getResult(); + } + + ///////// Provider control methods //////////// + private Answer easyCallCommand(Command cmd, Long zoneId) { + HostVO globoDnsHost = getGloboDnsHost(zoneId); + + return _agentMgr.easySend(globoDnsHost.getId(), cmd); + } + private Answer callCommand(Command cmd, Long zoneId) { + + Answer answer = easyCallCommand(cmd, zoneId); + if (answer == null || !answer.getResult()) { + String msg = "Error executing command " + cmd; + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + + return answer; + } + + @Override + public boolean addDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException { + return true; + } } diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java index c141ad1c8e4a..c7f5cb79d689 100644 --- a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsElementService.java @@ -22,4 +22,14 @@ public interface GloboDnsElementService extends PluggableService { public Host addGloboDnsHost(Long pNtwkId, String username, String password, String url); + + public boolean validateDnsRecordForLoadBalancer(String lbDomain, String lbRecord, String lbRecordContent, Long zoneId, boolean skipDnsError); + + public boolean createDnsRecordForLoadBalancer(String lbDomain, String lbRecord, String lbIpAddress, Long zoneId); + + public boolean removeDnsRecordForLoadBalancer(String lbUuid, String lbDomain, String lbRecord, String lbIpAddress, Long zoneId); + + public boolean registerVmDomain(Long zoneId, String nicUuid, String hostName, String ipAddress, String networkDomain, boolean isIpv6, boolean skipDnsError); + + public boolean createDnsRecordForLoadBalancer(GloboDnsTO globoDns, boolean skipDnsError); } diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsTO.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsTO.java new file mode 100644 index 000000000000..fd6ebe8d7b3d --- /dev/null +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/element/GloboDnsTO.java @@ -0,0 +1,58 @@ +package com.globo.globodns.cloudstack.element; + + +public class GloboDnsTO { + private Long zoneId; + private String resourceId; + private String domain; + private String record; + private String ipAddress; + + public GloboDnsTO(Long zoneId, String resourceId, String record, String domain, String ipAddress) { + this.zoneId = zoneId; + this.resourceId = resourceId; + this.domain = domain; + this.record = record; + this.ipAddress = ipAddress; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getRecord() { + return record; + } + + public void setRecord(String record) { + this.record = record; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } +} diff --git a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java index 84c1b5b44e57..0a45659bf1c2 100644 --- a/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java +++ b/plugins/network-elements/globodns/src/com/globo/globodns/cloudstack/resource/GloboDnsResource.java @@ -16,6 +16,9 @@ */ package com.globo.globodns.cloudstack.resource; +import com.globo.globodns.client.exception.GloboDnsIOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -35,6 +38,7 @@ import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.resource.ServerResource; +import com.cloud.utils.StringUtils; import com.cloud.utils.component.ManagerBase; import com.globo.globodns.client.GloboDns; import com.globo.globodns.client.GloboDnsException; @@ -42,11 +46,14 @@ import com.globo.globodns.client.model.Domain; import com.globo.globodns.client.model.Export; import com.globo.globodns.client.model.Record; +import com.globo.globodns.cloudstack.commands.CreateLbRecordAndReverseCommand; import com.globo.globodns.cloudstack.commands.CreateOrUpdateDomainCommand; import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand; import com.globo.globodns.cloudstack.commands.RemoveDomainCommand; import com.globo.globodns.cloudstack.commands.RemoveRecordCommand; import com.globo.globodns.cloudstack.commands.SignInCommand; +import com.globo.globodns.cloudstack.commands.ValidateLbRecordCommand; +import com.googlecode.ipv6.IPv6Address; public class GloboDnsResource extends ManagerBase implements ServerResource { private String _zoneId; @@ -61,11 +68,17 @@ public class GloboDnsResource extends ManagerBase implements ServerResource { private String _password; + private Integer numberOfRetries = 1; + private Integer connectTimeout = 20000; + private Integer readTimeout = 20000;; + protected GloboDns _globoDns; private static final String IPV4_RECORD_TYPE = "A"; + private static final String IPV6_RECORD_TYPE = "AAAA"; private static final String REVERSE_RECORD_TYPE = "PTR"; - private static final String REVERSE_DOMAIN_SUFFIX = "in-addr.arpa"; + private static final String REVERSE_IPV4_DOMAIN_SUFFIX = "in-addr.arpa"; + private static final String REVERSE_IPV6_DOMAIN_SUFFIX = "ip6.arpa"; private static final String DEFAULT_AUTHORITY_TYPE = "M"; private static final Logger s_logger = Logger.getLogger(GloboDnsResource.class); @@ -105,6 +118,22 @@ public boolean configure(String name, Map params) throws Configu _globoDns = GloboDns.buildHttpApi(_url, _username, _password); + if (params.containsKey("readTimeout")) { + readTimeout = Integer.valueOf((String)params.get("readTimeout")); + } + + if (params.containsKey("connectTimeout")) { + connectTimeout = Integer.valueOf((String)params.get("connectTimeout")); + } + + if (params.containsKey("numberOfRetries")) { + numberOfRetries = Integer.valueOf((String)params.get("numberOfRetries")); + } + + _globoDns.setReadTimeout(readTimeout); + _globoDns.setConnectTimeout(connectTimeout); + _globoDns.setNumberOfRetries(numberOfRetries); + return true; } @@ -173,6 +202,10 @@ public Answer executeRequest(Command cmd) { return execute((CreateOrUpdateDomainCommand)cmd); } else if (cmd instanceof CreateOrUpdateRecordAndReverseCommand) { return execute((CreateOrUpdateRecordAndReverseCommand)cmd); + } else if (cmd instanceof ValidateLbRecordCommand) { + return execute((ValidateLbRecordCommand) cmd); + } else if (cmd instanceof CreateLbRecordAndReverseCommand) { + return execute((CreateLbRecordAndReverseCommand) cmd); } return Answer.createUnsupportedCommandAnswer(cmd); } @@ -217,16 +250,16 @@ public Answer execute(RemoveDomainCommand cmd) { public Answer execute(RemoveRecordCommand cmd) { boolean needsExport = false; try { - if (removeRecord(cmd.getRecordName(), cmd.getRecordIp(), cmd.getNetworkDomain(), false, cmd.isOverride())) { + if (removeRecord(cmd.getRecordName(), cmd.getRecordIp(), cmd.getNetworkDomain(), false)) { needsExport = true; } // remove reverse - String reverseGloboDnsName = generateReverseDomainNameFromNetworkIp(cmd.getRecordIp()); - String reverseRecordName = generateReverseRecordNameFromNetworkIp(cmd.getRecordIp()); - String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain(); + String reverseGloboDnsName = generateReverseDomainNameFromNetworkIp(cmd.getRecordIp(), cmd.isIpv6()); + String reverseRecordName = generateReverseRecordNameFromNetworkIp(cmd.getRecordIp(), cmd.isIpv6()); + String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain() + '.'; - if (removeRecord(reverseRecordName, reverseRecordContent, reverseGloboDnsName, true, cmd.isOverride())) { + if (removeRecord(reverseRecordName, reverseRecordContent, reverseGloboDnsName, true)) { needsExport = true; } @@ -250,7 +283,8 @@ public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) { + cmd.getReverseTemplateId()); } - boolean created = createOrUpdateRecord(domain.getId(), cmd.getRecordName(), cmd.getRecordIp(), IPV4_RECORD_TYPE, cmd.isOverride()); + String recordType = cmd.isIpv6() ? IPV6_RECORD_TYPE : IPV4_RECORD_TYPE; + boolean created = createOrUpdateRecord(domain.getId(), cmd.getRecordName(), cmd.getRecordIp(), recordType, cmd.isOverride()); if (!created) { String msg = "Unable to create record " + cmd.getRecordName() + " at " + cmd.getNetworkDomain(); if (!cmd.isOverride()) { @@ -261,8 +295,8 @@ public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) { needsExport = true; } - String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain(); - if (createOrUpdateReverse(cmd.getRecordIp(), reverseRecordContent, cmd.getReverseTemplateId(), cmd.isOverride())) { + String reverseRecordContent = cmd.getRecordName() + '.' + cmd.getNetworkDomain() + '.'; + if (createOrUpdateReverse(cmd.getRecordIp(), reverseRecordContent, cmd.getReverseTemplateId(), cmd.isOverride(), cmd.isIpv6())) { needsExport = true; } else { if (!cmd.isOverride()) { @@ -273,6 +307,8 @@ public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) { } return new Answer(cmd); + } catch (GloboDnsIOException ex) { + return new Answer(cmd, false, "DNS IO ERROR: "+ ex.getMessage(), Answer.AnswerTypeError.DNS_IO_ERROR); } catch (GloboDnsException e) { return new Answer(cmd, false, e.getMessage()); } finally { @@ -282,8 +318,100 @@ public Answer execute(CreateOrUpdateRecordAndReverseCommand cmd) { } } - protected boolean createOrUpdateReverse(String networkIp, String reverseRecordContent, Long templateId, boolean override) { - String reverseDomainName = generateReverseDomainNameFromNetworkIp(networkIp); + public Answer execute(ValidateLbRecordCommand cmd) { + String lbName = cmd.getLbRecordName() + "." + cmd.getLbDomain(); + s_logger.debug("[LoadBalancer " + lbName +"] init loadbalancer validating"); + try { + Domain domain = searchDomain(cmd.getLbDomain(), false); + if (domain == null) { + String msg = "Domain " + cmd.getLbDomain() + " doesn't exist."; + s_logger.error("[LoadBalancer " + lbName +"]" + msg); + return new Answer(cmd, false, msg); + } + + Record record = this.searchRecordByName(cmd.getLbRecordName(), domain.getId()); + if (record == null) { + // Record does not exist, return that it is valid + return new Answer(cmd); + } + + if (cmd.isOverride()) { + // If record exists and override is true, then also returns true + return new Answer(cmd); + } + + if (record.getContent().equals(cmd.getLbRecordContent())) { + // If record exists and override is false, then content must be equal, i.e. no changes will be made, only validating it + return new Answer(cmd); + } else { + s_logger.warn("[LoadBalancer " + lbName +"] Record " + cmd.getLbRecordName() + " is invalid or override option is false"); + String msg = "The given load balancer name record is not valid or it is already in use. Override is not possible."; + return new Answer(cmd, false, msg); + } + + } catch (GloboDnsIOException ex ){ + if (cmd.isSkipDnsError()) { + s_logger.warn("[LoadBalancer " + lbName +"] IOException: ignoring loadbalancer " + lbName + " validation because skipdnserror is " + cmd.isSkipDnsError() + ", maybe globoDNSAPI is off or with some problem. Error: " + ex.getMessage(), ex); + return new Answer(cmd, true, ex.getMessage()); + } else { + s_logger.error("[LoadBalancer " + lbName +"] IOException: force loadbalancer " + lbName + "validation because skipdnserror is "+ cmd.isSkipDnsError() + " failed: " + ex.getMessage(), ex); + return new Answer(cmd, false, "Integration problem with DNSAPI, please contact your system administrator.", Answer.AnswerTypeError.DNS_IO_ERROR); + } + + } catch (GloboDnsException e) { + s_logger.error("[LoadBalancer " + lbName +"] GloboDnsException error:" + e.getMessage(), e); + return new Answer(cmd, false, e.getLocalizedMessage()); + } + } + + public Answer execute(CreateLbRecordAndReverseCommand cmd) { + boolean needsExport = false; + String lbname = cmd.getLbRecordName() + cmd.getLbDomain(); + try { + Domain domain = searchDomain(cmd.getLbDomain(), false); + if (domain == null) { + String msg = "Domain " + cmd.getLbDomain() + " doesn't exist."; + s_logger.debug("[LoadBalancer " + lbname + "] " + msg); + return new Answer(cmd, false, msg); + } + + boolean created = createOrUpdateRecord(domain.getId(), cmd.getLbRecordName(), cmd.getLbRecordIp(), IPV4_RECORD_TYPE, cmd.isOverride()); + if (!created) { + String msg = "Unable to create LB record " + cmd.getLbRecordName() + " at " + cmd.getLbDomain(); + if (!cmd.isOverride()) { + msg += ". Override LB record option is false, maybe record already exists."; + } + return new Answer(cmd, false, msg); + } else { + needsExport = true; + } + + String reverseRecordContent = cmd.getLbRecordName() + '.' + cmd.getLbDomain() + '.'; + if (createOrUpdateReverse(cmd.getLbRecordIp(), reverseRecordContent, cmd.getReverseTemplateId(), cmd.isOverride(), false)) { //IPv6 LB not implemented yet + needsExport = true; + } else if (!cmd.isOverride()) { + String msg = "Unable to create LB reverse record " + cmd.getLbRecordName() + " for ip " + cmd.getLbRecordIp(); + msg += ". Override record option is false, maybe record already exists."; + return new Answer(cmd, false, msg); + } + + + return new Answer(cmd); + }catch (GloboDnsIOException ex) { + s_logger.error("[LoadBalancer " + lbname + "] DNS IO error " + ex.getMessage(), ex); + return new Answer(cmd, false, ex.getMessage(), Answer.AnswerTypeError.DNS_IO_ERROR); + } catch (GloboDnsException e) { + s_logger.error("[LoadBalancer " + lbname + "] unable to create lb: " + e); + return new Answer(cmd, false, e.getMessage()); + } finally { + if (needsExport) { + scheduleExportChangesToBind(); + } + } + } + + protected boolean createOrUpdateReverse(String networkIp, String reverseRecordContent, Long templateId, boolean override, boolean isIpv6) { + String reverseDomainName = generateReverseDomainNameFromNetworkIp(networkIp, isIpv6); Domain reverseDomain = searchDomain(reverseDomainName, true); if (reverseDomain == null) { reverseDomain = _globoDns.getDomainAPI().createReverseDomain(reverseDomainName, templateId, DEFAULT_AUTHORITY_TYPE); @@ -291,7 +419,7 @@ protected boolean createOrUpdateReverse(String networkIp, String reverseRecordCo } // create reverse - String reverseRecordName = generateReverseRecordNameFromNetworkIp(networkIp); + String reverseRecordName = generateReverseRecordNameFromNetworkIp(networkIp, isIpv6); return createOrUpdateRecord(reverseDomain.getId(), reverseRecordName, reverseRecordContent, REVERSE_RECORD_TYPE, override); } @@ -328,20 +456,20 @@ public Answer execute(CreateOrUpdateDomainCommand cmd) { * @param bindZoneName * @return true if record exists and was removed. */ - protected boolean removeRecord(String recordName, String recordValue, String bindZoneName, boolean reverse, boolean override) { + protected boolean removeRecord(String recordName, String recordValue, String bindZoneName, boolean reverse) { Domain domain = searchDomain(bindZoneName, reverse); if (domain == null) { s_logger.warn("Domain " + bindZoneName + " doesn't exists in GloboDNS. Record " + recordName + " has already been removed."); return false; } - Record record = searchRecord(recordName, domain.getId()); + Record record = searchRecordByNameAndContent(recordName, recordValue, domain.getId()); if (record == null) { s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " has already been removed."); return false; } else { - if (!override && !record.getContent().equals(recordValue)) { - s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " have different value from " + recordValue - + " and override is not enable. I will not delete it."); + if (!record.getContent().equals(recordValue)) { + s_logger.warn("Record " + recordName + " in domain " + bindZoneName + " has different value from " + recordValue + + ". Will not delete it."); return false; } _globoDns.getRecordAPI().removeRecord(record.getId()); @@ -359,11 +487,11 @@ protected boolean removeRecord(String recordName, String recordValue, String bin * @return if record was created or updated. */ private boolean createOrUpdateRecord(Long domainId, String name, String ip, String type, boolean override) { - Record record = this.searchRecord(name, domainId); + Record record = this.searchRecordByName(name, domainId); if (record == null) { // Create new record record = _globoDns.getRecordAPI().createRecord(domainId, name, ip, type); - s_logger.info("Created record " + record.getName() + " in domain " + domainId); + s_logger.info("Created record " + name + " in domain " + domainId); } else { if (!ip.equals(record.getContent())) { if (Boolean.TRUE.equals(override)) { @@ -420,11 +548,8 @@ private Domain searchDomain(String name, boolean reverse) { * @param domainId Id of BindZoneName. Maybe you need use searchDomain before to use BindZoneName. * @return Record or null if not exists. */ - private Record searchRecord(String recordName, Long domainId) { - if (recordName == null || domainId == null) { - return null; - } - List candidates = _globoDns.getRecordAPI().listByQuery(domainId, recordName); + private Record searchRecordByName(String recordName, Long domainId) { + List candidates = this.listRecords(recordName, domainId); // GloboDns search name in name and content. We need to iterate to check if recordName exists only in name for (Record candidate : candidates) { if (recordName.equalsIgnoreCase(candidate.getName())) { @@ -436,21 +561,65 @@ private Record searchRecord(String recordName, Long domainId) { return null; } + private Record searchRecordByNameAndContent(String recordName, String recordContent, Long domainId) { + List candidates = this.listRecords(recordName, domainId); + // GloboDns search name in name and content. We need to iterate to check if recordName exists only in name + for (Record candidate : candidates) { + if (recordName.equalsIgnoreCase(candidate.getName()) && recordContent.equalsIgnoreCase(candidate.getContent())) { + s_logger.debug("Record " + recordName + ":" + recordContent + " in domain id " + domainId + " found in GloboDNS"); + return candidate; + } + } + s_logger.debug("Record " + recordName + ":" + recordContent + " in domain id " + domainId + " not found in GloboDNS"); + return null; + } + + private List listRecords(String recordName, Long domainId) { + if (recordName == null || domainId == null) { + return null; + } + List candidates = _globoDns.getRecordAPI().listByQuery(domainId, recordName); + return candidates; + } + /** * Generate reverseBindZoneName of network. We ALWAYS use /24. * @param networkIp * @return Bind Zone Name reverse of network specified by networkIp */ - private String generateReverseDomainNameFromNetworkIp(String networkIp) { - String[] octets = networkIp.split("\\."); - String reverseDomainName = octets[2] + '.' + octets[1] + '.' + octets[0] + '.' + REVERSE_DOMAIN_SUFFIX; - return reverseDomainName; + private String generateReverseDomainNameFromNetworkIp(String networkIp, boolean isIpv6) { + if(isIpv6){ + String ipv6 = IPv6Address.fromString(networkIp).toLongString(); // returns the long representation of ipv6 address + ArrayList blocks = new ArrayList(Arrays.asList(Arrays.copyOfRange(ipv6.split(":"), 0, 4))); + String joinedIpv6Blocks = StringUtils.join(blocks, ""); + + StringBuilder reverseDomainName = new StringBuilder(); + for(char character : joinedIpv6Blocks.toCharArray()){ + reverseDomainName.append(character + "."); + } + return reverseDomainName.reverse().substring(1).toString() + '.' + REVERSE_IPV6_DOMAIN_SUFFIX; + }else{ + String[] octets = networkIp.split("\\."); //Considering only /24 networks + String reverseDomainName = octets[2] + '.' + octets[1] + '.' + octets[0] + '.' + REVERSE_IPV4_DOMAIN_SUFFIX; + return reverseDomainName; + } } - private String generateReverseRecordNameFromNetworkIp(String networkIp) { - String[] octets = networkIp.split("\\."); - String reverseRecordName = octets[3]; - return reverseRecordName; - } + private String generateReverseRecordNameFromNetworkIp(String networkIp, boolean isIpv6) { + if(isIpv6){ + String ipv6 = IPv6Address.fromString(networkIp).toLongString(); // returns the long representation of ipv6 address + ArrayList blocks = new ArrayList(Arrays.asList(Arrays.copyOfRange(ipv6.split(":"), 4, 8))); + String joinedIpv6Blocks = StringUtils.join(blocks, ""); + StringBuilder reverseDomainName = new StringBuilder(); + for(char character : joinedIpv6Blocks.toCharArray()){ + reverseDomainName.append(character + "."); + } + return reverseDomainName.reverse().substring(1).toString(); + }else{ + String[] octets = networkIp.split("\\."); + String reverseRecordName = octets[3]; + return reverseRecordName; + } + } } diff --git a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java index 12427f6f38ee..3b8e77da1dc0 100644 --- a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java +++ b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/element/GloboDnsElementTest.java @@ -16,6 +16,8 @@ */ package com.globo.globodns.cloudstack.element; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; @@ -23,31 +25,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.IOException; - -import javax.inject.Inject; - import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.test.utils.SpringUtils; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDaoImpl; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -64,14 +53,9 @@ import com.cloud.host.dao.HostDao; import com.cloud.network.Network; import com.cloud.network.Network.Provider; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.PhysicalNetworkDao; -import com.cloud.resource.ResourceManager; import com.cloud.user.Account; -import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.UserVO; -import com.cloud.utils.component.ComponentContext; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; @@ -80,9 +64,9 @@ import com.globo.globodns.cloudstack.commands.CreateOrUpdateRecordAndReverseCommand; import com.globo.globodns.cloudstack.commands.RemoveRecordCommand; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(loader = AnnotationConfigContextLoader.class) -@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) +import java.util.ArrayList; +import java.util.Arrays; + public class GloboDnsElementTest { private static long zoneId = 5L; @@ -91,24 +75,26 @@ public class GloboDnsElementTest { private AccountVO acct = null; private UserVO user = null; - @Inject - DataCenterDao _datacenterDao; - - @Inject GloboDnsElement _globodnsElement; - @Inject + @Mock + DataCenterDao _datacenterDao; + + @Mock HostDao _hostDao; - @Inject + @Mock AgentManager _agentMgr; - @Inject - AccountManager _acctMgr; - @Before public void setUp() throws Exception { - ComponentContext.initComponentsLifeCycle(); + MockitoAnnotations.initMocks(this); + + _globodnsElement = Mockito.spy(new GloboDnsElement()); + + _globodnsElement._dcDao = _datacenterDao; + _globodnsElement._hostDao = _hostDao; + _globodnsElement._agentMgr = _agentMgr; acct = new AccountVO(200L); acct.setType(Account.ACCOUNT_TYPE_NORMAL); @@ -120,8 +106,6 @@ public void setUp() throws Exception { user.setAccountId(acct.getAccountId()); CallContext.register(user, acct); - when(_acctMgr.getSystemAccount()).thenReturn(this.acct); - when(_acctMgr.getSystemUser()).thenReturn(this.user); } @After @@ -142,7 +126,10 @@ public void testUpperCaseCharactersAreNotAllowed() throws ConcurrentOperationExc when(_datacenterDao.findById(zoneId)).thenReturn(mock(DataCenterVO.class)); DeployDestination dest = new DeployDestination(); ReservationContext context = new ReservationContextImpl(null, null, user); - _globodnsElement.prepare(network, nic, vm, dest, context); + boolean result = _globodnsElement.prepare(network, nic, vm, dest, context); + + assertTrue(result); + verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(CreateOrUpdateRecordAndReverseCommand.class)); } @Test @@ -151,6 +138,7 @@ public void testPrepareMethodCallGloboDnsToRegisterHostName() throws Exception { when(network.getDataCenterId()).thenReturn(zoneId); when(network.getId()).thenReturn(1l); NicProfile nic = new NicProfile(); + nic.setUuid("123123"); nic.setIPv4Address("10.11.12.13"); VirtualMachineProfile vm = mock(VirtualMachineProfile.class); when(vm.getHostName()).thenReturn("vm-name"); @@ -174,7 +162,14 @@ public Answer answer(InvocationOnMock invocation) throws Throwable { } }); - _globodnsElement.prepare(network, nic, vm, dest, context); + GloboResourceConfigurationDaoImpl mock = mock(GloboResourceConfigurationDaoImpl.class); + GloboResourceConfigurationVO globoResourceConfigurationVO = new GloboResourceConfigurationVO(); + globoResourceConfigurationVO.setBoolValue(false); + when(mock.getConfiguration(GloboResourceType.VM_NIC, nic.getUuid(), GloboResourceKey.isDNSRegistered)).thenReturn(new ArrayList()); + _globodnsElement._globoResourceConfigDao = mock; + + boolean result = _globodnsElement.prepare(network, nic, vm, dest, context); + assertTrue(result); verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(CreateOrUpdateRecordAndReverseCommand.class)); } @@ -205,62 +200,39 @@ public Answer answer(InvocationOnMock invocation) throws Throwable { return new Answer(cmd); } }); - - _globodnsElement.release(network, nic, vm, context); + GloboResourceConfigurationDaoImpl mock = mock(GloboResourceConfigurationDaoImpl.class); + GloboResourceConfigurationVO globoResourceConfigurationVO = new GloboResourceConfigurationVO(); + globoResourceConfigurationVO.setBoolValue(false); + when(mock.getConfiguration(GloboResourceType.VM_NIC, nic.getUuid(), GloboResourceKey.isDNSRegistered)).thenReturn(new ArrayList()); + _globodnsElement._globoResourceConfigDao = mock; + + boolean result = _globodnsElement.release(network, nic, vm, context); + assertTrue(result); verify(_agentMgr, times(1)).easySend(eq(globoDnsHostId), isA(RemoveRecordCommand.class)); } - @Configuration - @ComponentScan(basePackageClasses = {GloboDnsElement.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) - public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { - - @Bean - public HostDao hostDao() { - return mock(HostDao.class); - } - - @Bean - public DataCenterDao dataCenterDao() { - return mock(DataCenterDao.class); - } - - @Bean - public PhysicalNetworkDao physicalNetworkDao() { - return mock(PhysicalNetworkDao.class); - } - - @Bean - public NetworkDao networkDao() { - return mock(NetworkDao.class); - } - - @Bean - public ConfigurationDao configurationDao() { - return mock(ConfigurationDao.class); - } - - @Bean - public AgentManager agentManager() { - return mock(AgentManager.class); - } - - @Bean - public ResourceManager resourceManager() { - return mock(ResourceManager.class); - } + @Test(expected=InvalidParameterValueException.class) + public void testUnderscoreInLoadBalancerNameNotAllowed() { + String lbDomain = "lb.globo.com"; + String lbRecord = "test_underscore"; + String lbRecordContent = "10.0.0.1"; + boolean result = _globodnsElement.validateDnsRecordForLoadBalancer(lbDomain, lbRecord, lbRecordContent, zoneId, true); + } - @Bean - public AccountManager accountManager() { - return mock(AccountManager.class); - } + @Test + public void testCheckLbNameGivenNotBlacklistedName(){ + Mockito.when(_globodnsElement.getBlackListedDomains()).thenReturn(new ArrayList()); + _globodnsElement.checkForBlacklistedDomain("domain.com", "name"); + } - public static class Library implements TypeFilter { - @Override - public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { - ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); - return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); - } + @Test + public void testCheckLbNameGivenBlacklistedName(){ + Mockito.when(_globodnsElement.getBlackListedDomains()).thenReturn(Arrays.asList("dev.domain.com")); + try { + _globodnsElement.checkForBlacklistedDomain("domain.com", "name.dev"); + }catch(InvalidParameterValueException e){ + assertEquals("Invalid load balancer name, it cannot contain the the domain 'dev.domain.com'", e.getMessage()); } } -} +} \ No newline at end of file diff --git a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java index d89d7e7f4637..eee2f6badde9 100644 --- a/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java +++ b/plugins/network-elements/globodns/test/com/globo/globodns/cloudstack/resource/GloboDnsResourceTest.java @@ -18,15 +18,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; @@ -119,12 +119,15 @@ private Domain generateFakeDomain(String domainName, boolean reverse) { private Record generateFakeRecord(Domain domain, String recordName, String recordContent, boolean reverse) { Record record = new Record(); + //String recordType; if (reverse) { + //recordType = "PTR"; record.getTypePTRRecordAttributes().setName(recordName); record.getTypePTRRecordAttributes().setContent(recordContent); record.getTypePTRRecordAttributes().setDomainId(domain.getId()); record.getTypePTRRecordAttributes().setId(sequenceId++); } else { + //recordType = "A"; record.getTypeARecordAttributes().setName(recordName); record.getTypeARecordAttributes().setContent(recordContent); record.getTypeARecordAttributes().setDomainId(domain.getId()); @@ -180,7 +183,7 @@ public void testCreateRecordAndReverseWithSuccessWhenDomainExistsAndRecordDoesnt String domainName = "domain.name.com"; String reverseDomainName = "20.30.40.in-addr.arpa"; String reverseRecordName = "10"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = generateFakeDomain(domainName, false); Record record = generateFakeRecord(domain, recordName, recordIp, false); @@ -190,7 +193,7 @@ public void testCreateRecordAndReverseWithSuccessWhenDomainExistsAndRecordDoesnt when(_recordApi.createRecord(eq(domain.getId()), eq(recordName), eq(recordIp), eq("A"))).thenReturn(record); when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true, false)); assertNotNull(answer); assertEquals(true, answer.getResult()); verify(_exportApi, times(1)).scheduleExport(); @@ -207,7 +210,7 @@ public void testCreateRecordAndReverseWillFailWhenRecordAlreadyExistsAndOverride Domain domain = generateFakeDomain(domainName, false); Record record = generateFakeRecord(domain, recordName, oldIp, false); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newIp, domainName, TEMPLATE_ID, false)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newIp, domainName, TEMPLATE_ID, false, false)); assertNotNull(answer); assertEquals(false, answer.getResult()); } @@ -226,7 +229,7 @@ public void testCreateRecordAndReverseWillFailWhenReverseRecordAlreadyExistsAndO Domain reverseDomain = generateFakeDomain(reverseDomainName, true); Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, "X", true); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false, false)); assertNotNull(answer); assertEquals(false, answer.getResult()); } @@ -238,7 +241,7 @@ public void testCreateRecordAndReverseWhenDomainDoesNotExist() throws Exception String domainName = "domain.name.com"; String reverseDomainName = "20.30.40.in-addr.arpa"; String reverseRecordName = "10"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = new Domain(); domain.getDomainAttributes().setId(sequenceId++); @@ -253,7 +256,7 @@ public void testCreateRecordAndReverseWhenDomainDoesNotExist() throws Exception when(_domainApi.createReverseDomain(eq(reverseDomainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(reverseDomain); when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, true, false)); assertNotNull(answer); assertEquals(true, answer.getResult()); verify(_exportApi, times(1)).scheduleExport(); @@ -266,7 +269,7 @@ public void testCreateRecordAndReverseWhenDomainDoesNotExistAndOverrideIsFalse() String domainName = "domain.name.com"; String reverseDomainName = "20.30.40.in-addr.arpa"; String reverseRecordName = "10"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = new Domain(); domain.getDomainAttributes().setId(sequenceId++); @@ -281,7 +284,7 @@ public void testCreateRecordAndReverseWhenDomainDoesNotExistAndOverrideIsFalse() when(_domainApi.createReverseDomain(eq(reverseDomainName), eq(TEMPLATE_ID), eq("M"))).thenReturn(reverseDomain); when(_recordApi.createRecord(eq(reverseDomain.getId()), eq(reverseRecordName), eq(reverseRecordContent), eq("PTR"))).thenReturn(record); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, recordIp, domainName, TEMPLATE_ID, false, false)); assertNotNull(answer); assertEquals(true, answer.getResult()); verify(_exportApi, times(1)).scheduleExport(); @@ -299,14 +302,14 @@ public void testUpdateRecordAndReverseWhenDomainExistsAndOverrideIsTrue() throws String domainName = "domain.name.com"; String reverseDomainName = "30.40.50.in-addr.arpa"; String reverseRecordName = "20"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = generateFakeDomain(domainName, false); Record record = generateFakeRecord(domain, recordName, oldRecordIp, false); Domain reverseDomain = generateFakeDomain(reverseDomainName, true); Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, "X", true); - Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newRecordIp, domainName, TEMPLATE_ID, true)); + Answer answer = _globoDnsResource.execute(new CreateOrUpdateRecordAndReverseCommand(recordName, newRecordIp, domainName, TEMPLATE_ID, true, false)); // ensure calls in sequence to ensure this call are the only ones. InOrder inOrder = inOrder(_recordApi); @@ -329,14 +332,14 @@ public void testRemoveRecordWhenRecordExists() throws Exception { String domainName = "domain.name.com"; String reverseDomainName = "20.30.40.in-addr.arpa"; String reverseRecordName = "10"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = generateFakeDomain(domainName, false); Record record = generateFakeRecord(domain, recordName, recordIp, false); Domain reverseDomain = generateFakeDomain(reverseDomainName, true); Record reverseRecord = generateFakeRecord(reverseDomain, reverseRecordName, reverseRecordContent, true); - Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, true)); + Answer answer = _globoDnsResource.execute(new RemoveRecordCommand(recordName, recordIp, domainName, false)); assertNotNull(answer); assertEquals(true, answer.getResult()); @@ -373,7 +376,7 @@ public void testRemoveReverseRecordButNotRemoveRecordWhenRecordExistsWithDiffere String domainName = "domain.name.com"; String reverseDomainName = "20.30.40.in-addr.arpa"; String reverseRecordName = "10"; - String reverseRecordContent = recordName + "." + domainName; + String reverseRecordContent = recordName + "." + domainName + '.'; Domain domain = generateFakeDomain(domainName, false); Record record = generateFakeRecord(domain, recordName, "X", false); diff --git a/plugins/network-elements/globonetwork/LICENSE b/plugins/network-elements/globonetwork/LICENSE new file mode 100644 index 000000000000..67db8588217f --- /dev/null +++ b/plugins/network-elements/globonetwork/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/plugins/network-elements/globonetwork/NOTICE b/plugins/network-elements/globonetwork/NOTICE new file mode 100644 index 000000000000..4094565353b5 --- /dev/null +++ b/plugins/network-elements/globonetwork/NOTICE @@ -0,0 +1,13 @@ +Copyright (c) 2013 Globo.com + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/network-elements/globonetwork/pom.xml b/plugins/network-elements/globonetwork/pom.xml new file mode 100644 index 000000000000..de15fe8d9ce6 --- /dev/null +++ b/plugins/network-elements/globonetwork/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + cloud-plugin-network-globonetwork + Apache CloudStack Plugin - GloboNetwork + + org.apache.cloudstack + cloudstack-plugins + 4.11.1.0 + ../../pom.xml + + + + com.globo.globonetwork + globonetwork-client + 0.0.60 + + + org.slf4j + slf4j-log4j12 + + + org.apache.cloudstack + cloud-plugin-network-globodns + ${project.version} + + + net.kencochrane.raven + raven + 4.2 + + + net.kencochrane.raven + raven-log4j + 4.2 + + + diff --git a/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/module.properties b/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/module.properties new file mode 100644 index 000000000000..e0bc9f37ec04 --- /dev/null +++ b/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/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=globonetwork +parent=globodns diff --git a/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/spring-globonetwork-context.xml b/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/spring-globonetwork-context.xml new file mode 100644 index 000000000000..20dfee4afebd --- /dev/null +++ b/plugins/network-elements/globonetwork/resources/META-INF/cloudstack/globonetwork/spring-globonetwork-context.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkEnvironmentVO.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkEnvironmentVO.java new file mode 100644 index 000000000000..a6f83e03342d --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkEnvironmentVO.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 com.globo.globonetwork.cloudstack; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "globonetwork_environment_ref") +public class GloboNetworkEnvironmentVO implements InternalIdentity { + + /** + * + */ + private static final long serialVersionUID = -2510501870987500493L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "name") + private String name; + + @Column(name = "globonetwork_environment_id") + private long globoNetworkEnvironmentId; + + public GloboNetworkEnvironmentVO() { + } + + public GloboNetworkEnvironmentVO(long physicalNetworkId, String name, long globoNetworkEnvironmentId) { + this.physicalNetworkId = physicalNetworkId; + this.name = name; + this.globoNetworkEnvironmentId = globoNetworkEnvironmentId; + } + + public long getId() { + return id; + } + + public long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getName() { + return name; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkIpDetailVO.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkIpDetailVO.java new file mode 100644 index 000000000000..8c921fbefc1d --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkIpDetailVO.java @@ -0,0 +1,87 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "globonetwork_ip_detail") +public class GloboNetworkIpDetailVO implements InternalIdentity { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "globonetwork_ip_id") + private long globoNetworkIpId; + + @Column(name = "user_ip_address_id") + private long ipAddressId; + + @Column(name = "globonetwork_lbenvironment_ref_id", nullable = true) + private Long globoNetworkEnvironmentRefId; + + @Column(name = "globonetwork_vip_id", nullable = true) + private Long globoNetworkVipId; + + public GloboNetworkIpDetailVO() { + } + + public GloboNetworkIpDetailVO(long ipAddressId, Long globoNetworkIpId) { + this.ipAddressId = ipAddressId; + this.globoNetworkIpId = globoNetworkIpId; + } + + public long getId() { + return id; + } + + public long getIpAddressId() { + return ipAddressId; + } + + public Long getGloboNetworkVipId() { + return globoNetworkVipId; + } + + public void setGloboNetworkVipId(Long globoNetworkVipId) { + this.globoNetworkVipId = globoNetworkVipId; + } + + public Long getGloboNetworkEnvironmentRefId() { + return globoNetworkEnvironmentRefId; + } + + public void setGloboNetworkEnvironmentRefId(Long globoNetworkEnvironmentRefId) { + this.globoNetworkEnvironmentRefId = globoNetworkEnvironmentRefId; + } + + public long getGloboNetworkIpId() { + return globoNetworkIpId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkLoadBalancerEnvironment.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkLoadBalancerEnvironment.java new file mode 100644 index 000000000000..1a88c06553ec --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkLoadBalancerEnvironment.java @@ -0,0 +1,73 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package com.globo.globonetwork.cloudstack; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "globonetwork_loadbalancer_environments") +public class GloboNetworkLoadBalancerEnvironment implements InternalIdentity { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "name") + private String name; + + @Column(name = "globonetwork_environment_ref_id") + private long globoNetworkEnvironmentRefId; + + @Column(name = "globonetwork_lb_environment_id") + private long globoNetworkLoadBalancerEnvironmentId; + + public GloboNetworkLoadBalancerEnvironment() { + } + + public GloboNetworkLoadBalancerEnvironment(String name, long globoNetworkEnvironmentId, long globoNetworkLoadBalancerEnvironmentId) { + this.name = name; + this.globoNetworkEnvironmentRefId = globoNetworkEnvironmentId; + this.globoNetworkLoadBalancerEnvironmentId = globoNetworkLoadBalancerEnvironmentId; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public long getGloboNetworkEnvironmentRefId() { + return globoNetworkEnvironmentRefId; + } + + public long getGloboNetworkLoadBalancerEnvironmentId() { + return globoNetworkLoadBalancerEnvironmentId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkNetworkVO.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkNetworkVO.java new file mode 100644 index 000000000000..406f70731220 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkNetworkVO.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 com.globo.globonetwork.cloudstack; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "globonetwork_network_ref") +public class GloboNetworkNetworkVO implements InternalIdentity { + + /** + * + */ + private static final long serialVersionUID = 1119504366663225252L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "globonetwork_vlan_id") + private long globoNetworkVlanId; + + @Column(name = "network_id") + private long networkId; + + @Column(name = "globonetwork_environment_id") + private long globoNetworkEnvironmentId; + + public GloboNetworkNetworkVO() { + } + + public GloboNetworkNetworkVO(long globoNetworkVlanId, long networkId, long globoNetworkEnvironmentId) { + this.globoNetworkVlanId = globoNetworkVlanId; + this.networkId = networkId; + this.globoNetworkEnvironmentId = globoNetworkEnvironmentId; + } + + public long getId() { + return id; + } + + public long getGloboNetworkVlanId() { + return globoNetworkVlanId; + } + + public long getNetworkId() { + return networkId; + } + + public long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkVipAccVO.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkVipAccVO.java new file mode 100644 index 000000000000..2a47b4e7e047 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/GloboNetworkVipAccVO.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 com.globo.globonetwork.cloudstack; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "globonetwork_vip_acc_ref") +public class GloboNetworkVipAccVO implements InternalIdentity { + + /** + * + */ + private static final long serialVersionUID = -6276857308303296742L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "globonetwork_vip_id") + private long globoNetworkVipId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "network_id") + private long networkId; + + public GloboNetworkVipAccVO() { + } + + public GloboNetworkVipAccVO(long globoNetworkVipId, long accountId, long networkId) { + this.globoNetworkVipId = globoNetworkVipId; + this.accountId = accountId; + this.networkId = networkId; + } + + public long getId() { + return id; + } + + public long getGloboNetworkVipId() { + return globoNetworkVipId; + } + + public long getAccountId() { + return accountId; + } + + public long getNetworkId() { + return networkId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AcquireNewIpForLbInGloboNetworkCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AcquireNewIpForLbInGloboNetworkCmd.java new file mode 100644 index 000000000000..7726ad23a4a2 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AcquireNewIpForLbInGloboNetworkCmd.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 com.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.IPAddressResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +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.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.addr.PublicIp; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "acquireNewLBIp", description = "Acquires and associates a Load Balancer IP to an network.", responseObject = IPAddressResponse.class) +public class AcquireNewIpForLbInGloboNetworkCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(AcquireNewIpForLbInGloboNetworkCmd.class.getName()); + private static final String s_name = "associateipaddressresponse"; + + @Inject + private GloboNetworkService globoNetworkSvc; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The network this ip address should be associated to.", required = true) + private Long networkId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project the IP address will be associated with") + private Long projectId; + + @Parameter(name = "lbenvironmentid", type = CommandType.LONG, entityType = GloboNetworkLBEnvironmentResponse.class, description = "Globo Network LB Environment Id", required = true) + private Long globoNetworkLBEnvironmentId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + public Long getProjectId() { + return projectId; + } + + public Long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + public Network getNetwork() { + Network network = _networkService.getNetwork(networkId); + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by network id specified"); + } + return network; + } + + @Override + public long getEntityOwnerId() { + Network network = getNetwork(); + return network.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PORTABLE_IP_ASSIGN; + } + + @Override + public String getEventDescription() { + return "acquiring lb ip to network id: " + getNetworkId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void create() throws ResourceAllocationException { + try { + PublicIp ip = globoNetworkSvc.acquireLbIp(getNetworkId(), getProjectId(), getGloboNetworkLBEnvironmentId()); + + if (ip != null) { + setEntityId(ip.getId()); + setEntityUuid(ip.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to allocate ip address"); + } + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientCapacityException ex) { + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); + } catch (ResourceUnavailableException ex) { + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { + CallContext.current().setEventDetails("Ip Id: " + getEntityId()); + + IpAddress result = _networkService.associateIPToNetwork(getEntityId(), getNetworkId()); + + if (result != null) { + IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(ResponseView.Full, result); + ipResponse.setResponseName(getCommandName()); + setResponseObject(ipResponse); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to acquire lb ip address"); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IpAddress; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkEnvironmentCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkEnvironmentCmd.java new file mode 100644 index 000000000000..3600b94e6911 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkEnvironmentCmd.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 com.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.api.ApiDBUtils; +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "addGloboNetworkEnvironment", responseObject = GloboNetworkEnvironmentResponse.class, description = "Adds a GloboNetwork environment to a zone") +public class AddGloboNetworkEnvironmentCmd extends BaseAsyncCmd { + + private static final String s_name = "addglobonetworkenvironmentresponse"; + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// 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.NAME, type = CommandType.STRING, required = true, description = "Name of the relationship between zone and GloboNetwork environment (like BACKEND, FRONTEND)") + private String name; + + @Parameter(name = "napienvironmentid", type = CommandType.LONG, required = true, description = "the Id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getName() { + return name; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + GloboNetworkEnvironmentVO napiEnvironmentVO = _globoNetworkService.addGloboNetworkEnvironment(physicalNetworkId, name, globoNetworkEnvironmentId); + GloboNetworkEnvironmentResponse response = new GloboNetworkEnvironmentResponse(); + response.setId(napiEnvironmentVO.getId()); + response.setName(napiEnvironmentVO.getName()); + response.setPhysicalNetworkId(ApiDBUtils.findPhysicalNetworkById(napiEnvironmentVO.getPhysicalNetworkId()).getUuid()); + response.setNapiEnvironmentId(napiEnvironmentVO.getGloboNetworkEnvironmentId()); + response.setObjectName("globonetworkenvironment"); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public String getEventType() { + //EventTypes.EVENT_NETWORK_CREATE + return EventTypes.EVENT_NETWORK_CREATE; + } + + @Override + public String getEventDescription() { + return "Adding a GloboNetwork Environment to Zone"; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkHostCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkHostCmd.java new file mode 100644 index 000000000000..ddd8fab034b3 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkHostCmd.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +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.host.Host; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "addGloboNetworkHost", responseObject = SuccessResponse.class, description = "Adds the GloboNetwork external host") +public class AddGloboNetworkHostCmd extends BaseAsyncCmd { + + private static final String s_name = "addglobonetworkhostresponse"; + @Inject + GloboNetworkService _globoNetworkManager; + + // /////////////////////////////////////////////////// + // ////////////// 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.USERNAME, type = CommandType.STRING, required = true, description = "Username for GloboNetwork") + private String username; + + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password for GloboNetwork") + private String password; + + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "GloboNetwork url") + private String url; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getUrl() { + return url; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + Host host = _globoNetworkManager.addGloboNetworkHost(physicalNetworkId, username, password, url); + + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess((host == null ? false : true)); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_CREATE; + } + + @Override + public String getEventDescription() { + return "Adding a GloboNetwork Environment to Zone"; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkLBEnvironmentCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkLBEnvironmentCmd.java new file mode 100644 index 000000000000..879756c96e2e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddGloboNetworkLBEnvironmentCmd.java @@ -0,0 +1,128 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; + +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "addGloboNetworkLBEnvironment", responseObject = GloboNetworkLBEnvironmentResponse.class, description = "Add a LB environment to a GloboNetwork environment") +public class AddGloboNetworkLBEnvironmentCmd extends BaseAsyncCmd { + + private static final String s_name = "addglobonetworklbenvironmentresponse"; + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the relationship between LB network and GloboNetwork environment") + private String name; + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name = "napienvironmentid", type = CommandType.LONG, required = true, description = "the Id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + @Parameter(name = "globolbenvironmentid", type = CommandType.LONG, required = true, description = "the Id of LB environment in GloboNetwork") + private Long globoNetworkLBEnvironmentId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + public Long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + GloboNetworkLoadBalancerEnvironment globoLBEnvironmentVO = _globoNetworkService.addGloboNetworkLBEnvironment(name, physicalNetworkId, globoNetworkEnvironmentId, + globoNetworkLBEnvironmentId); + GloboNetworkLBEnvironmentResponse response = new GloboNetworkLBEnvironmentResponse(); + response.setId(globoLBEnvironmentVO.getId()); + response.setName(globoLBEnvironmentVO.getName()); + response.setGloboNetworkEnvironmentId(globoLBEnvironmentVO.getGloboNetworkEnvironmentRefId()); + response.setGloboNetworkLBEnvironmentId(globoLBEnvironmentVO.getGloboNetworkLoadBalancerEnvironmentId()); + response.setObjectName("globonetworklbenvironment"); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public String getEventType() { + //EventTypes.EVENT_NETWORK_CREATE + return EventTypes.EVENT_NETWORK_CREATE; + } + + @Override + public String getEventDescription() { + return "Adding a GloboNetwork Environment to Zone"; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddNetworkViaGloboNetworkCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddNetworkViaGloboNetworkCmd.java new file mode 100644 index 000000000000..53cd534d4c08 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/AddNetworkViaGloboNetworkCmd.java @@ -0,0 +1,169 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.NetworkACLResponse; +import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "addNetworkViaGloboNetwork", responseObject = NetworkResponse.class, description = "Adds a vlan/network in Cloudstack and GloboNetwork") +public class AddNetworkViaGloboNetworkCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(AddNetworkViaGloboNetworkCmd.class.getName()); + private static final String s_name = "addnetworkviaglobonetworkresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the network") + private String name; + + @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of the network") + private String displayText; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "the Zone ID for the network") + private Long zoneId; + + @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, required = true, description = "the network offering id") + private Long networkOfferingId; + + @Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "network domain") + private String networkDomain; + + @Parameter(name = ApiConstants.ACL_TYPE, type = CommandType.STRING, description = "Access control type; supported values" + + " are account and domain. In 3.0 all shared networks should have aclType=Domain, and all Isolated networks" + + " - Account. Account means that only the account owner can use the network, domain - all accouns in the domain can use the network") + private String aclType; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the network") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the ssh key") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a network") + private Long domainId; + + @Parameter(name = ApiConstants.SUBDOMAIN_ACCESS, type = CommandType.BOOLEAN, description = "Defines whether to allow" + + " subdomains to use networks dedicated to their parent domain(s). Should be used with aclType=Domain, defaulted to allow.subdomain.network.access global config if not specified") + private Boolean subdomainAccess; + + @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the network to the end user or not.") + private Boolean displayNetwork; + + @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "Network ACL Id associated for the network") + private Long aclId; + + @Parameter(name = "napienvironmentid", type = CommandType.LONG, required = true, description = "GloboNetwork environment ID.") + private Long globoNetworkEnvironmentId; + + @Parameter(name = "isipv6", type = CommandType.BOOLEAN, description = "If true defines de network IP type as IPV6") + private boolean isIpv6 = false; + + @Parameter(name = "subnet", type = CommandType.LONG, description = "Subnet mask in bits (24 for /24, 29 for /29).") + private Long subnet; + + public Long getZoneId() { + return zoneId; + } + + public Long getNetworkOfferingId() { + return networkOfferingId; + } + + public ACLType getACLType() { + if (aclType == null) { + return null; + } + for (ACLType aclTypeEnum : ACLType.values()) { + if (aclType.equalsIgnoreCase(aclTypeEnum.name())) { + return aclTypeEnum; + } + } + s_logger.warn("Invalid value for ACLType: " + aclType); + return null; + } + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("addNetworkViaGloboNetworkCmd command with name=" + name + " displayText=" + displayText + " zoneId=" + zoneId + " networkOfferingId=" + + networkOfferingId + " networkDomain=" + networkDomain + " aclType=" + aclType + " accountName=" + accountName + " projectId=" + projectId + " domainId" + + domainId + " subdomainAccess=" + subdomainAccess + " displayNetwork=" + displayNetwork + " aclId=" + aclId + " napienvironmentid=" + + globoNetworkEnvironmentId + " isIpv6="+ isIpv6); + Network network = _globoNetworkService.createNetwork(name, displayText, zoneId, networkOfferingId, globoNetworkEnvironmentId, networkDomain, getACLType(), accountName, + projectId, domainId, subdomainAccess, displayNetwork, aclId, isIpv6, subnet); + if (network != null) { + CallContext.current().putContextParameter(Network.class.getName(), network.getUuid()); + ActionEventUtils.onActionEvent(getUserId(), getEntityOwnerId(), domainId, EventTypes.EVENT_NETWORK_CREATE, getEventDescription(network.getId())); + NetworkResponse response = _responseGenerator.createNetworkResponse(ResponseView.Full, network); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create network from GloboNetwork."); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + private Long getUserId(){ + return CallContext.current().getCallingUserId(); + } + + public String getEventDescription(Long id) { + return "Creating network. Network Id: " + id; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmd.java new file mode 100644 index 000000000000..ff050848d1e3 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmd.java @@ -0,0 +1,188 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.manager.Protocol; +import javax.inject.Inject; + +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +@APICommand(name = "createGloboNetworkPool", description = "Creates a new pool to a load balancer", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class CreateGloboNetworkPoolCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(CreateGloboNetworkPoolCmd.class.getName()); + + private static final String s_name = "createglobonetworkpoolresponse"; + + @Parameter(name= ApiConstants.ZONE_ID, type = CommandType.UUID, required = true, entityType = ZoneResponse.class, description = "the ID of the zone") + private Long zoneId; + + @Parameter(name= ApiConstants.LBID, type = CommandType.UUID, required = true, entityType = FirewallRuleResponse.class, description = "the ID of the load balancer rule") + private Long lbId; + + @Parameter(name = ApiConstants.PUBLIC_PORT, type = CommandType.INTEGER, required = true, description = "the public port from where the network traffic will be load balanced from") + private Integer publicPort; + + @Parameter(name = ApiConstants.PRIVATE_PORT, type = CommandType.INTEGER, required = true, description = "the private port of the private ip address/virtual machine where the network traffic will be load balanced to") + private Integer privatePort; + + @Parameter(name = ApiConstants.L4_PROTOCOL, type = CommandType.STRING, description = "layer 4 protocol for connection between vip and pool") + private String l4Protocol = Protocol.L4.TCP.getNetworkApiOptionValue(); + + @Parameter(name = ApiConstants.L7_PROTOCOL, type = CommandType.STRING, description = "layer 7 protocol for connection between vip and pool") + private String l7Protocol = Protocol.L7.OTHERS.getNetworkApiOptionValue(); + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(lbId, getActualCommandName()); + + validateParams(); + GloboNetworkPoolResponse.Pool pool = _globoNetworkService.createPool(this); + PoolResponse poolResp = new PoolResponse(); + if(pool != null) { + poolResp.setId(pool.getId()); + poolResp.setName(pool.getIdentifier()); + poolResp.setLbMethod(pool.getLbMethod()); + poolResp.setPort(pool.getPort()); + poolResp.setHealthcheckType(pool.getHealthcheckType()); + poolResp.setHealthcheck(pool.getHealthcheck()); + poolResp.setExpectedHealthcheck(pool.getExpectedHealthcheck()); + poolResp.setMaxconn(pool.getMaxconn()); + poolResp.setObjectName("globonetworkpool"); + } + poolResp.setResponseName(getCommandName()); + this.setResponseObject(poolResp); + } + + protected void validateParams() { + if (l4Protocol == null) { + throw new CloudRuntimeException("l4protocol can not be null."); + } + + if (l7Protocol == null) { + throw new CloudRuntimeException("l7protocol can not be null."); + } + + Protocol.L4 l4 = Protocol.L4.valueOfFromNetworkAPI(l4Protocol); + Protocol.L7 l7 = Protocol.L7.valueOfFromNetworkAPI(l7Protocol); + + if (!Protocol.validProtocols(l4, l7)) { + throw new CloudRuntimeException("l4protocol with value '" + l4.getNetworkApiOptionValue() + "' does not match with l7protocol '" + l7.getNetworkApiOptionValue() + "'. Possible l7 value(s): " + l4.getL7s() + "."); + } + } + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getLbId() { + return lbId; + } + + public void setLbId(Long lbId) { + this.lbId = lbId; + } + + public Integer getPublicPort() { + return publicPort; + } + + public void setPublicPort(Integer publicPort) { + this.publicPort = publicPort; + } + + public Integer getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(Integer privatePort) { + this.privatePort = privatePort; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_CREATE_POOL; + } + + @Override + public String getEventDescription() { + return "Creates a new pool"; + } + + public String getL4Protocol() { + return l4Protocol; + } + + public void setL4Protocol(String l4Protocol) { + this.l4Protocol = l4Protocol; + } + + public String getL7Protocol() { + return l7Protocol; + } + + public void setL7Protocol(String l7Protocol) { + this.l7Protocol = l7Protocol; + } + + + public Protocol.L4 getL4() { + return Protocol.L4.valueOfFromNetworkAPI(l4Protocol); + } + public Protocol.L7 getL7() { + return Protocol.L7.valueOfFromNetworkAPI(l7Protocol); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteGloboNetworkPoolCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteGloboNetworkPoolCmd.java new file mode 100644 index 000000000000..a60a075b24fc --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteGloboNetworkPoolCmd.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 com.globo.globonetwork.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + + +@APICommand(name = "deleteGloboNetworkPool", description = "Deletes a pool from a load balancer", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class DeleteGloboNetworkPoolCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(DeleteGloboNetworkPoolCmd.class.getName()); + + private static final String s_name = "deleteglobonetworkpoolresponse"; + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = true, description = "the ID of the pool to be removed") + private Long poolId; + + @Parameter(name= ApiConstants.LBID, type = CommandType.UUID, required = true, entityType = FirewallRuleResponse.class, description = "the ID of the load balancer rule") + private Long lbId; + + @Parameter(name= ApiConstants.ZONE_ID, type = CommandType.UUID, required = true, entityType = ZoneResponse.class, description = "the ID of the zone") + private Long zoneId; + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(lbId, getActualCommandName()); + + _globoNetworkService.deletePool(this); + setResponseObject(new SuccessResponse(getCommandName())); + } + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getLbId() { + return lbId; + } + + public void setLbId(Long lbId) { + this.lbId = lbId; + } + + public Long getPoolId() { + return poolId; + } + + public void setPoolId(Long poolId) { + this.poolId = poolId; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_REMOVE_POOL; + } + + @Override + public String getEventDescription() { + return "Deletes a pool"; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmd.java new file mode 100644 index 000000000000..df9c223fc653 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmd.java @@ -0,0 +1,126 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "deleteNetworkInGloboNetwork", description = "Deletes a GloboNetwork network", responseObject = SuccessResponse.class, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class DeleteNetworkInGloboNetworkCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteNetworkOfferingCmd.class.getName()); + private static final String s_name = "deletenetworkresponse"; + + @Inject + GloboNetworkService _glbNetService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @ACL(accessType = AccessType.OperateEntry) + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the ID of the network") + private Long id; + + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Force delete a network." + + " Network will be marked as 'Destroy' even when commands to shutdown and cleanup to the backend fails.") + private Boolean forced; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public boolean isForced() { + return (forced != null) ? forced : false; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Network Id: " + getId()); + try { + boolean result = _glbNetService.destroyGloboNetwork(getId(), isForced()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete network"); + } + }catch (CloudRuntimeException e ) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage(), e); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting network: " + id; + } + + @Override + public long getEntityOwnerId() { + Network network = _networkService.getNetwork(id); + if (network == null) { + throw new InvalidParameterValueException("Networkd id=" + id + " doesn't exist"); + } else { + return _networkService.getNetwork(id).getAccountId(); + } + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DisassociateIpAddrFromGloboNetworkCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DisassociateIpAddrFromGloboNetworkCmd.java new file mode 100644 index 000000000000..2e799a560a4e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/DisassociateIpAddrFromGloboNetworkCmd.java @@ -0,0 +1,138 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.IPAddressResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +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.IpAddress; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "disassociateIpAddressFromGloboNetwork", description = "Disassociates an ip address from GloboNetwork.", responseObject = IPAddressResponse.class) +public class DisassociateIpAddrFromGloboNetworkCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DisassociateIpAddrFromGloboNetworkCmd.class.getName()); + private static final String s_name = "disassociateipaddressfromglobonetworkresponse"; + + @Inject + private GloboNetworkService globoNetworkSvc; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = IPAddressResponse.class, required = true, description = "the id of the ip address" + + " to disassociate") + private long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getIpAddressId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { + CallContext.current().setEventDetails("Ip Id: " + getIpAddressId()); + + boolean result = globoNetworkSvc.disassociateIpAddrFromGloboNetwork(getIpAddressId()); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disassociate IP address from Globo Network"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PORTABLE_IP_RELEASE; + } + + @Override + public String getEventDescription() { + return ("Disassociating ip address from GloboNetwork with id=" + id); + } + + @Override + public long getEntityOwnerId() { + IpAddress ip = getIpAddress(id); + if (ip == null) { + throw new InvalidParameterValueException("Unable to find ip address by id=" + id); + } + return ip.getAccountId(); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + IpAddress ip = getIpAddress(id); + return ip.getAssociatedWithNetworkId(); + } + + private IpAddress getIpAddress(long id) { + IpAddress ip = _entityMgr.findById(IpAddress.class, id); + + if (ip == null) { + throw new InvalidParameterValueException("Unable to find ip address by id=" + id); + } else { + return ip; + } + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.IpAddress; + } + + @Override + public Long getInstanceId() { + return getIpAddressId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmd.java new file mode 100644 index 000000000000..687b35473a55 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmd.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 com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +@APICommand(name = "getGloboNetworkPool", description = "Get pool from globonetwork.", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetGloboNetworkPoolCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(GetGloboNetworkPoolCmd.class.getName()); + + private static final String s_name = "getglobonetworkpoolresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name= "poolid", type = CommandType.LONG, entityType = PoolResponse.class, description = "the ID of the pool load balancer rule") + private Long poolId; + + @Parameter(name= ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone") + private Long zoneId; + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + + public Long getPoolId() { + return poolId; + } + + public void setPoolId(Long poolId) { + this.poolId = poolId; + } + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + GloboNetworkPoolResponse.Pool pool = _globoNetworkService.getPoolById(getPoolId(), getZoneId()); + + PoolResponse poolResp = new PoolResponse(); + poolResp.setId(pool.getId()); + poolResp.setName(pool.getIdentifier()); + poolResp.setLbMethod(pool.getLbMethod()); + poolResp.setPort(pool.getPort()); + poolResp.setHealthcheckType(pool.getHealthcheckType()); + poolResp.setHealthcheck(pool.getHealthcheck()); + poolResp.setExpectedHealthcheck(pool.getExpectedHealthcheck()); + poolResp.setMaxconn(pool.getMaxconn()); + + poolResp.setObjectName("globonetworkpool"); + poolResp.setResponseName(getCommandName()); + this.setResponseObject(poolResp); + } + + private Long getZoneId() { + + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboResourceConfigurationCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboResourceConfigurationCmd.java new file mode 100644 index 000000000000..ddaf8932fa81 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GetGloboResourceConfigurationCmd.java @@ -0,0 +1,102 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.response.GetGloboResourceConfigurationResponse; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +/** + * Created by sinval.neto on 7/27/16. + */ +@APICommand(name = "getGloboResourceConfiguration", description = "Get a GloboResourceConfiguration", responseObject = GetGloboResourceConfigurationResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetGloboResourceConfigurationCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(GetGloboResourceConfigurationCmd.class.getName()); + + private static final String s_name = "getgloboresourceconfigurationresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, entityType = GetGloboResourceConfigurationResponse.class, description = "the ID of the RESOURCE") + private String resourceUuid; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, entityType = GetGloboResourceConfigurationResponse.class, description = "the type of the resource") + private String resourceType; + + @Parameter(name = ApiConstants.RESOURCE_KEY, type = CommandType.STRING, required = true, entityType = GetGloboResourceConfigurationResponse.class, description = "the type of the resource") + private String resourceKey; + + public String getUuid() { + return resourceUuid; + } + + public GloboResourceType getResourceType() { + try { + return GloboResourceType.valueOf(resourceType); + } catch (Exception e) { + throw new CloudRuntimeException("Globo resource type \'" + resourceType + "\' does not exist. Possible values: " + StringUtils.join(",", GloboResourceType.values())); + } + + } + + public GloboResourceKey getResourceKey() { + try { + return GloboResourceKey.valueOf(resourceKey); + } catch (Exception e) { + throw new CloudRuntimeException("Globo resource type \'" + resourceKey + "\' does not exist. Possible values: " + StringUtils.join(",", GloboResourceKey.values())); + } + } + + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + GetGloboResourceConfigurationResponse response = new GetGloboResourceConfigurationResponse(); + response.setObjectName("globoresourceconfiguration"); + response.setResponseName(getCommandName()); + + + GloboResourceConfigurationVO globoResourceConfigurationVO = _globoNetworkService.getGloboResourceConfiguration(this.getUuid(), getResourceType(), getResourceKey()); + if (globoResourceConfigurationVO == null) { + s_logger.warn("Could not find GloboResource Configuration. resouceid:" + resourceUuid + ", resourceType: " + resourceType + ", resoucekey: " + resourceKey); + } else { + response.setUuid(this.getUuid()); + response.setResourceType(globoResourceConfigurationVO.getResourceType().toString()); + response.setConfigurationValue(globoResourceConfigurationVO.getValue()); + response.setConfigurationKey(globoResourceConfigurationVO.getKey().toString()); + } + + + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkCapabilitiesResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkCapabilitiesResponse.java new file mode 100644 index 000000000000..744c42f9a761 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkCapabilitiesResponse.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.globo.globonetwork.cloudstack.api; + +import org.apache.cloudstack.api.BaseResponse; + +import java.util.List; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class GloboNetworkCapabilitiesResponse extends BaseResponse { + + @SerializedName("domainSuffix") + @Param(description = "Domain suffix of all networks") + private String domainSuffix; + + @SerializedName("supportCustomNetworkDomain") + @Param(description = "user can customize network domain") + private Boolean supportCustomNetworkDomain; + + @SerializedName("enabled") + @Param(description = "is GloboNetwork provider enabled in any zone") + private Boolean enabled; + + @SerializedName("allowedLbSuffixes") + @Param(description = "allowed domain suffixes for load balancers in GloboNetwork") + private List allowedLbSuffixes; + + public String getDomainSuffix() { + return domainSuffix; + } + + public void setDomainSuffix(String domainSuffix) { + this.domainSuffix = domainSuffix; + } + + public Boolean getSupportCustomNetworkDomain() { + return supportCustomNetworkDomain; + } + + public void setSupportCustomNetworkDomain(Boolean supportCustomNetworkDomain) { + this.supportCustomNetworkDomain = supportCustomNetworkDomain; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getAllowedLbSuffixes() { + return allowedLbSuffixes; + } + + public void setAllowedLbSuffixes(List allowedLbSuffixes) { + this.allowedLbSuffixes = allowedLbSuffixes; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkEnvironmentResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkEnvironmentResponse.java new file mode 100644 index 000000000000..0fa7c23e5453 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkEnvironmentResponse.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 com.globo.globonetwork.cloudstack.api; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.serializer.Param; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = GloboNetworkEnvironmentVO.class) +public class GloboNetworkEnvironmentResponse extends BaseResponse { + + @SerializedName("id") + @Param(description = "id of the GloboNetwork Environment ref") + private Long id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) + @Param(description = "the physicalNetworkId of GloboNetwork environment belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the provider") + private String name; + + @SerializedName("napienvironmentid") + @Param(description = "id of environment in GloboNetwork") + private Long napiEnvironmentId; + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setNapiEnvironmentId(Long napiEnvironmentId) { + this.napiEnvironmentId = napiEnvironmentId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBCacheGroupResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBCacheGroupResponse.java new file mode 100644 index 000000000000..1cf995bd9b70 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBCacheGroupResponse.java @@ -0,0 +1,34 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class GloboNetworkLBCacheGroupResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the LB cache group") + private String name; + + public void setName(String name) { + this.name = name; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBEnvironmentResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBEnvironmentResponse.java new file mode 100644 index 000000000000..e1d72c84e28f --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkLBEnvironmentResponse.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 com.globo.globonetwork.cloudstack.api; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.serializer.Param; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = GloboNetworkLoadBalancerEnvironment.class) +public class GloboNetworkLBEnvironmentResponse extends BaseResponse { + + @SerializedName("id") + @Param(description = "ID of the relationship between environment ID and LB network") + private Long id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the LB network") + private String name; + + @SerializedName("globonetworkenvironmentid") + @Param(description = "id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + @SerializedName("globonetworklbenvironmentid") + @Param(description = "id of LB environment in GloboNetwork") + private Long globoNetworkLBEnvironmentId; + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setGloboNetworkEnvironmentId(Long globoNetworkEnvironmentId) { + this.globoNetworkEnvironmentId = globoNetworkEnvironmentId; + } + + public void setGloboNetworkLBEnvironmentId(Long globoNetworkLBEnvironmentId) { + this.globoNetworkLBEnvironmentId = globoNetworkLBEnvironmentId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkPoolOptionExternalResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkPoolOptionExternalResponse.java new file mode 100644 index 000000000000..0a1645bcbada --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/GloboNetworkPoolOptionExternalResponse.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 com.globo.globonetwork.cloudstack.api; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class GloboNetworkPoolOptionExternalResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the Pool option") + private Long id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the Pool option") + private String name; + + public GloboNetworkPoolOptionExternalResponse(Long id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ImportGloboNetworkLoadBalancerCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ImportGloboNetworkLoadBalancerCmd.java new file mode 100644 index 000000000000..330c148f7d08 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ImportGloboNetworkLoadBalancerCmd.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; + +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.rules.LoadBalancer; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "importGloboNetworkLoadBalancer", responseObject = LoadBalancerResponse.class, description = "Imports an existant Load Balancer in GloboNetwork to Cloudstack") +public class ImportGloboNetworkLoadBalancerCmd extends BaseAsyncCmd { + + private static final String s_name = "importglobonetworkloadbalancerresponse"; + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = "lbid", type = CommandType.LONG, required = true, description = "the VIP ID") + private Long lbId; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "LB network (primary) ID") + private Long networkId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project ID to be associated with LB") + private Long projectId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getLbId() { + return lbId; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getProjectId() { + return projectId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + LoadBalancer lb = _globoNetworkService.importGloboNetworkLoadBalancer(lbId, networkId, projectId); + LoadBalancerResponse response = new LoadBalancerResponse(); + if (lb != null) { + response = _responseGenerator.createLoadBalancerResponse(lb); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LOAD_BALANCER_CREATE; + } + + @Override + public String getEventDescription() { + return "Importing a GloboNetwork Load Balancer to Cloudstack"; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListAllEnvironmentsFromGloboNetworkCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListAllEnvironmentsFromGloboNetworkCmd.java new file mode 100644 index 000000000000..a5531f9a49c6 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListAllEnvironmentsFromGloboNetworkCmd.java @@ -0,0 +1,99 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.globo.globonetwork.cloudstack.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAllEnvironmentResponse.Environment; +import com.globo.globonetwork.cloudstack.response.GloboNetworkEnvironmentExternalResponse; + +@APICommand(name = "listAllEnvironmentsFromGloboNetwork", responseObject = GloboNetworkEnvironmentExternalResponse.class, description = "Lists all environments from GloboNetwork") +public class ListAllEnvironmentsFromGloboNetworkCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListAllEnvironmentsFromGloboNetworkCmd.class.getName()); + private static final String s_name = "listallenvironmentsfromglobonetworkresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, description = "the Physical Network ID") + private Long physicalNetworkId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + s_logger.debug("listAllEnvironmentsFromGloboNetworkCmd command"); + List environmentList = _globoNetworkService.listAllEnvironmentsFromGloboNetwork(physicalNetworkId); + if (environmentList != null) { + List responseList = new ArrayList(); + for (Environment environment : environmentList) { + GloboNetworkEnvironmentExternalResponse envResponse = new GloboNetworkEnvironmentExternalResponse(); + envResponse.setEnvironmentId(environment.getId()); + envResponse.setEnvironmentFullName(environment.getDcDivisionName() + " - " + environment.getLogicalEnvironmentName() + " - " + environment.getL3GroupName()); + envResponse.setObjectName("globonetworkenvironment"); + responseList.add(envResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to retrieve all environments from GloboNetwork."); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboLbNetworksCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboLbNetworksCmd.java new file mode 100644 index 000000000000..2fbbe23463aa --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboLbNetworksCmd.java @@ -0,0 +1,38 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.network.Network; +import com.cloud.utils.Pair; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +@APICommand(name = "listGloboLbNetworks", description = "Lists all networks that support lb services.", responseObject = NetworkResponse.class, responseView = ResponseObject.ResponseView.Full, entityType = {Network.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListGloboLbNetworksCmd extends ListNetworksCmd { + + @Inject + GloboNetworkManager _globoNetworkManager; + + @Override + public void execute() { + Pair, Integer> networks = _globoNetworkManager.searchForLbNetworks(this); + + ListResponse response = new ListResponse(); + List networkResponses = new ArrayList(); + for (Network network : networks.first()) { + + NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network); + networkResponses.add(networkResponse); + + } + response.setResponses(networkResponses, networks.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkCapabilitiesCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkCapabilitiesCmd.java new file mode 100644 index 000000000000..3fdb6ca1c958 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkCapabilitiesCmd.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 com.globo.globonetwork.cloudstack.api; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.dc.DataCenter; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "listGloboNetworkCapabilities", responseObject = GloboNetworkCapabilitiesResponse.class, description = "Lists GloboNetwork capabilities") +public class ListGloboNetworkCapabilitiesCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkCapabilitiesCmd.class); + private static final String s_name = "listglobonetworkcapabilitiesresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Override + public void execute() { + s_logger.debug("listGloboNetworkCapabilities command"); + List enabledInZones = _globoNetworkService.getAllZonesThatProviderAreEnabled(); + + GloboNetworkCapabilitiesResponse response = new GloboNetworkCapabilitiesResponse(); + response.setDomainSuffix(_globoNetworkService.getDomainSuffix()); + response.setSupportCustomNetworkDomain(_globoNetworkService.isSupportedCustomNetworkDomain()); + response.setEnabled(enabledInZones != null && !enabledInZones.isEmpty()); + response.setAllowedLbSuffixes(_globoNetworkService.listAllowedLbSuffixes()); + response.setObjectName("globoNetworkCapability"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkEnvironmentsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkEnvironmentsCmd.java new file mode 100644 index 000000000000..619ac0797fda --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkEnvironmentsCmd.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 com.globo.globonetwork.cloudstack.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.api.ApiDBUtils; +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "listGloboNetworkEnvironments", responseObject = GloboNetworkEnvironmentResponse.class, description = "Lists GloboNetwork environments") +public class ListGloboNetworkEnvironmentsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkEnvironmentsCmd.class); + private static final String s_name = "listglobonetworkenvironmentsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = false, description = "the Physical Network ID the network belongs to") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, description = "the ZoneID") + private Long zoneId; + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getZoneId() { + return zoneId; + } + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("listGloboNetworkEnvironmentsCmd command with physicalNetowrkId=" + physicalNetworkId + " zoneId=" + zoneId); + List globoNetworkEnvironments = _globoNetworkService.listGloboNetworkEnvironmentsFromDB(this.physicalNetworkId, this.zoneId); + + List responseList = new ArrayList(); + + for (GloboNetworkEnvironmentVO globoNetworkEnvironmentVO : globoNetworkEnvironments) { + GloboNetworkEnvironmentResponse envResponse = new GloboNetworkEnvironmentResponse(); + envResponse.setId(globoNetworkEnvironmentVO.getId()); + envResponse.setName(globoNetworkEnvironmentVO.getName()); + envResponse.setPhysicalNetworkId(ApiDBUtils.findPhysicalNetworkById(globoNetworkEnvironmentVO.getPhysicalNetworkId()).getUuid()); + envResponse.setNapiEnvironmentId(globoNetworkEnvironmentVO.getGloboNetworkEnvironmentId()); + envResponse.setObjectName("globonetworkenvironment"); + responseList.add(envResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmd.java new file mode 100644 index 000000000000..9096edfe5996 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmd.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 com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.response.ExpectedHealthcheckResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +@APICommand(name = "listGloboNetworkExpectedHealthchecks", description = "Lists expected healthchecks from networkapi .", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListGloboNetworkExpectedHealthchecksCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkExpectedHealthchecksCmd.class.getName()); + + private static final String s_name = "listgloboNetworkexpectedhealthchecksresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + ListResponse response = new ListResponse(); + + List expectedHealthchecks = _globoNetworkService.listAllExpectedHealthchecks();; + + List lbResponses = new ArrayList<>(); + + for ( GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck expectedHealthcheck : expectedHealthchecks) { + + ExpectedHealthcheckResponse poolResp = new ExpectedHealthcheckResponse(); + poolResp.setId(expectedHealthcheck.getId()); + poolResp.setExpected(expectedHealthcheck.getExpected()); + + poolResp.setObjectName("globonetworkexpectedhealthcheck"); + lbResponses.add(poolResp); + } + + Collections.sort(lbResponses); + + response.setResponses(lbResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBCacheGroupsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBCacheGroupsCmd.java new file mode 100644 index 000000000000..533a383f6fb4 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBCacheGroupsCmd.java @@ -0,0 +1,97 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.globo.globonetwork.cloudstack.api; + +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listGloboNetworkLBCacheGroups", responseObject = GloboNetworkLBCacheGroupResponse.class, description = "Lists GloboNetwork LB cache groups") +public class ListGloboNetworkLBCacheGroupsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkLBCacheGroupsCmd.class); + private static final String s_name = "listglobonetworklbcachegroupsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = "lbenvironment", type = CommandType.LONG, required = true, description = "the Id of LB environment in GloboNetwork") + private Long globoNetworkLBEnvironmentId; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + private Long networkId; + + public Long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + public Long getNetworkId() { return networkId; } + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("listGloboNetworkLBCacheGroupsCmd command with LBEnvironmentId=" + globoNetworkLBEnvironmentId); + List globoNetworkLBCacheGroups = _globoNetworkService.listGloboNetworkLBCacheGroups(getGloboNetworkLBEnvironmentId(), getNetworkId()); + + List responseList = new ArrayList(); + + for (String globoNetworkLBCacheGroup : globoNetworkLBCacheGroups) { + GloboNetworkLBCacheGroupResponse lbCacheGroupResponse = new GloboNetworkLBCacheGroupResponse(); + lbCacheGroupResponse.setName(globoNetworkLBCacheGroup); + lbCacheGroupResponse.setObjectName("globonetworkcachegroups"); + responseList.add(lbCacheGroupResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBEnvironmentsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBEnvironmentsCmd.java new file mode 100644 index 000000000000..0b894d2aa407 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkLBEnvironmentsCmd.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 com.globo.globonetwork.cloudstack.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "listGloboNetworkLBEnvironments", responseObject = GloboNetworkLBEnvironmentResponse.class, description = "Lists GloboNetwork LB environments") +public class ListGloboNetworkLBEnvironmentsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkLBEnvironmentsCmd.class); + private static final String s_name = "listglobonetworklbenvironmentsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @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.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = false, description = "the Network ID") + private Long networkId; + + @Parameter(name = "environmentid", type = CommandType.LONG, required = false, description = "the Id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("listGloboNetworkLBEnvironmentsCmd command with physicalnetwork=" + physicalNetworkId + " and environmentId=" + globoNetworkEnvironmentId + + " and networkId=" + this.networkId); + List globoNetworkLBEnvironments = _globoNetworkService.listGloboNetworkLBEnvironmentsFromDB(this.physicalNetworkId, + this.networkId, this.globoNetworkEnvironmentId); + + List responseList = new ArrayList(); + + for (GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironmentVO : globoNetworkLBEnvironments) { + GloboNetworkLBEnvironmentResponse lbNetworkResponse = new GloboNetworkLBEnvironmentResponse(); + lbNetworkResponse.setId(globoNetworkLBEnvironmentVO.getId()); + lbNetworkResponse.setName(globoNetworkLBEnvironmentVO.getName()); + lbNetworkResponse.setGloboNetworkEnvironmentId(globoNetworkLBEnvironmentVO.getGloboNetworkEnvironmentRefId()); + lbNetworkResponse.setGloboNetworkLBEnvironmentId(globoNetworkLBEnvironmentVO.getGloboNetworkLoadBalancerEnvironmentId()); + lbNetworkResponse.setObjectName("globonetworklbenvironments"); + responseList.add(lbNetworkResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolOptionsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolOptionsCmd.java new file mode 100644 index 000000000000..c9eeac48ad48 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolOptionsCmd.java @@ -0,0 +1,97 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse.PoolOption; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listGloboNetworkPoolOptions", responseObject = GloboNetworkPoolOptionExternalResponse.class, description = "List GloboNetwork pool options") +public class ListGloboNetworkPoolOptionsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkPoolOptionsCmd.class); + private static final String s_name = "listglobonetworkpooloptionsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = "lbenvironmentid", type = CommandType.LONG, entityType = GloboNetworkLBEnvironmentResponse.class, description = "Globo Network LB Environment Id", required = true) + private Long globoNetworkLBEnvironmentId; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "The network ID") + private Long networkId; + + @Parameter(name = ApiConstants.TYPE, required = true, type = CommandType.STRING, entityType = GloboNetworkVipResponse.class, description = "The type of the pool option") + private String type; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + List poolOptions = _globoNetworkService.listPoolOptions(this.globoNetworkLBEnvironmentId, this.networkId, this.type); + + List responseList = new ArrayList(); + + for (PoolOption option : poolOptions) { + GloboNetworkPoolOptionExternalResponse lbNetworkResponse = new GloboNetworkPoolOptionExternalResponse(option.getId(), option.getName()); + + lbNetworkResponse.setObjectName("globonetworkpooloptions"); + responseList.add(lbNetworkResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmd.java new file mode 100644 index 000000000000..8f1b2d4e0547 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmd.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 com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +@APICommand(name = "listGloboNetworkPools", description = "Lists pools .", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListGloboNetworkPoolsCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkPoolsCmd.class.getName()); + + private static final String s_name = "listglobonetworkpoolresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name= ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required=true, description = "the ID of the load balancer rule") + private Long lbId; + + @Parameter(name= ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required=true, description = "the ID of the zone") + private Long zoneId; + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + + public Long getLbId() { + return lbId; + } + + + public Long getZoneId() { + return zoneId; + } + + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + ListResponse response = new ListResponse(); + + List pools = _globoNetworkService.listAllPoolByVipId(getLbId(), getZoneId()); + + List lbResponses = new ArrayList<>(); + + for ( GloboNetworkPoolResponse.Pool pool : pools) { + + PoolResponse poolResp = new PoolResponse(); + poolResp.setId(pool.getId()); + poolResp.setName(pool.getIdentifier()); + poolResp.setLbMethod(pool.getLbMethod()); + poolResp.setPort(pool.getPort()); + poolResp.setHealthcheckType(pool.getHealthcheckType()); + poolResp.setHealthcheck(pool.getHealthcheck()); + poolResp.setExpectedHealthcheck(pool.getExpectedHealthcheck()); + poolResp.setMaxconn(pool.getMaxconn()); + poolResp.setVipPort(pool.getVipPort()); + poolResp.setL4Protocol(pool.getL4protocol()); + poolResp.setL7Protocol(pool.getL7protocol()); + + + poolResp.setObjectName("globonetworkpool"); + lbResponses.add(poolResp); + + } + + response.setResponses(lbResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setLbId(Long lbId) { + this.lbId = lbId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkRealsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkRealsCmd.java new file mode 100644 index 000000000000..ba23a24fbe0a --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkRealsCmd.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.globo.globonetwork.cloudstack.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipExternalResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import com.google.common.base.Joiner; + +@APICommand(name = "listGloboNetworkReals", responseObject = GloboNetworkVipExternalResponse.class, description = "List GloboNetwork Reals") +public class ListGloboNetworkRealsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkRealsCmd.class); + private static final String s_name = "listglobonetworkrealsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = ApiConstants.VIP_ID, required = true, type = CommandType.LONG, entityType = GloboNetworkVipResponse.class, description = "the vip id") + private Long vipId; + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("listGloboNetworkRealsCmd command with vipId=" + vipId); + List globoNetworkReals = _globoNetworkService.listGloboNetworkReals(this.vipId); + + List responseList = new ArrayList(); + + for (GloboNetworkVipResponse.Real globoNetworkReal : globoNetworkReals) { + GloboNetworkVipExternalResponse.Real realResponse = new GloboNetworkVipExternalResponse.Real(); + realResponse.setVmname(globoNetworkReal.getVmName()); + realResponse.setIp(globoNetworkReal.getIp()); + realResponse.setNetwork(globoNetworkReal.getNetwork()); + realResponse.setPorts(Joiner.on(", ").join(globoNetworkReal.getPorts())); + realResponse.setState(globoNetworkReal.getState()); + realResponse.setNic(globoNetworkReal.getNic()); + + realResponse.setObjectName("globonetworkreal"); + responseList.add(realResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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(), runtimeExcp); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkVipsCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkVipsCmd.java new file mode 100644 index 000000000000..14d3fdab10cf --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/ListGloboNetworkVipsCmd.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.globo.globonetwork.cloudstack.api; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipExternalResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; + +@APICommand(name = "listGloboNetworkVips", responseObject = GloboNetworkVipExternalResponse.class, description = "Lists GloboNetwork Vips") +public class ListGloboNetworkVipsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ListGloboNetworkVipsCmd.class); + private static final String s_name = "listglobonetworkvipsresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "the project id") + private Long projectId; + + /* Implementation */ + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + s_logger.debug("listGloboNetworkVipsCmd command with projectId=" + projectId); + List globoNetworkVips = _globoNetworkService.listGloboNetworkVips(this.projectId); + + List responseList = new ArrayList(); + + for (GloboNetworkVipResponse globoNetworkVip : globoNetworkVips) { + GloboNetworkVipExternalResponse vipResponse = new GloboNetworkVipExternalResponse(); + vipResponse.setId(globoNetworkVip.getId()); + vipResponse.setName(globoNetworkVip.getName()); + vipResponse.setIp(globoNetworkVip.getIp()); + vipResponse.setCache(globoNetworkVip.getCache()); + vipResponse.setMethod(globoNetworkVip.getMethod()); + vipResponse.setPersistence(globoNetworkVip.getPersistence()); + vipResponse.setHealthchecktype(globoNetworkVip.getHealthcheckType()); + vipResponse.setHealthcheck(globoNetworkVip.getHealthcheck()); + vipResponse.setMaxconn(globoNetworkVip.getMaxConn()); + vipResponse.setPorts(globoNetworkVip.getPorts()); + vipResponse.setNetworkids(globoNetworkVip.getNetworkIds()); + + vipResponse.setObjectName("globonetworkvip"); + responseList.add(vipResponse); + } + + ListResponse response = new ListResponse(); + response.setResponses(responseList); + 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(), runtimeExcp); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RegisterDnsForResourceCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RegisterDnsForResourceCmd.java new file mode 100644 index 000000000000..cf7d4f6ac4e8 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RegisterDnsForResourceCmd.java @@ -0,0 +1,94 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.RegisterDnsForLoadBalancerResponse; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + + +/** + * Created by sinval.neto on 7/20/16. + */ +@APICommand(name = "registerDnsForResource", description = "Register a DNS for a Load Balancer", responseObject = RegisterDnsForLoadBalancerResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class RegisterDnsForResourceCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RegisterDnsForResourceCmd.class.getName()); + + private static final String s_name = "registerdnsforresourceresponse"; + + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.UUID, type = CommandType.STRING, entityType = RegisterDnsForLoadBalancerResponse.class, description = "the ID of the Load Balancer") + private String id; + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, entityType = RegisterDnsForLoadBalancerResponse.class, description = "the type of the resource to register the DNS") + private String resourceType; + + public String getId() { + return id; + } + + public GloboResourceType getResourceType() { + try { + return GloboResourceType.valueOf(resourceType); + } catch (Exception e) { + throw new CloudRuntimeException("Globo resource type \'" + resourceType + "\' does not exist. Possible values: " + StringUtils.join(",", GloboResourceType.values())); + } + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + RegisterDnsForLoadBalancerResponse response = new RegisterDnsForLoadBalancerResponse(); + + _globoNetworkService.registerDnsForResource(id, getResourceType()); + response.setResourceType(resourceType); + response.setId(id); + response.setObjectName("registerdns"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return 0; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_REGISTER_DNS; + } + + @Override + public String getEventDescription() { + return "RegisterDnsForResourceDescription"; + } +} + + + + diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkEnvironmentCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkEnvironmentCmd.java new file mode 100644 index 000000000000..86f4e4db5f22 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkEnvironmentCmd.java @@ -0,0 +1,99 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package com.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "removeGloboNetworkEnvironment", responseObject = SuccessResponse.class, description = "Removes a GloboNetwork environment from a zone") +public class RemoveGloboNetworkEnvironmentCmd extends BaseCmd { + + private static final String s_name = "removeglobonetworkresponse"; + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// 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 = "environmentid", type = CommandType.LONG, required = true, description = "the Id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + boolean result = _globoNetworkService.removeGloboNetworkEnvironment(physicalNetworkId, globoNetworkEnvironmentId); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove the environment."); + } + } 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkLBEnvironmentCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkLBEnvironmentCmd.java new file mode 100644 index 000000000000..c3553be4a5b2 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/RemoveGloboNetworkLBEnvironmentCmd.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 com.globo.globonetwork.cloudstack.api; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +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.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@APICommand(name = "removeGloboNetworkLBEnvironment", responseObject = SuccessResponse.class, description = "Removes relationship between an environment and a LB network") +public class RemoveGloboNetworkLBEnvironmentCmd extends BaseCmd { + + private static final String s_name = "removeglobonetworklbenvironmentresponse"; + @Inject + GloboNetworkService _globoNetworkService; + + // /////////////////////////////////////////////////// + // ////////////// 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 = "napienvironmentid", type = CommandType.LONG, required = true, description = "the Id of environment in GloboNetwork") + private Long globoNetworkEnvironmentId; + + @Parameter(name = "globolbenvironmentid", type = CommandType.LONG, required = true, description = "the Id of LB environment in GloboNetwork") + private Long globoNetworkLBEnvironmentId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + public Long getGloboNetworkLBNetworkId() { + return globoNetworkLBEnvironmentId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + boolean result = _globoNetworkService.removeGloboNetworkLBEnvironment(physicalNetworkId, globoNetworkEnvironmentId, globoNetworkLBEnvironmentId); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove the VIP environment."); + } + } 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; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmd.java new file mode 100644 index 000000000000..387749ae0837 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmd.java @@ -0,0 +1,243 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.event.EventTypes; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.manager.Protocol; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +@APICommand(name = "updateGloboNetworkPool", description = "Update pools.", responseObject = PoolResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UpdateGloboNetworkPoolCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(UpdateGloboNetworkPoolCmd.class.getName()); + + private static final String s_name = "updateglobonetworkpoolresponse"; + + + @Parameter(name= ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone") + private Long zoneId; + + @Parameter(name= "poolids", type = CommandType.LIST, collectionType = CommandType.LONG, entityType = PoolResponse.class, description = "comma separated list of pool ids") + private List poolIds; + + @Parameter(name= ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, description = "the ID of the load balancer rule") + private Long lbId; + + @Parameter(name = "healthchecktype", type = CommandType.STRING, required = true, description = "Healthcheck Type") + private String healthcheckType; + + @Parameter(name = "healthcheck", type = CommandType.STRING, required = true, description = "HealthcheckURI") + private String healthcheck; + + @Parameter(name = "expectedhealthcheck", type = CommandType.STRING, required = true, description = "Expected healthcheck.") + private String expectedHealthcheck; + + @Parameter(name = "maxconn", type = CommandType.INTEGER, required = true, description = "Max number of connections") + private Integer maxConn; + + @Parameter(name = "l4protocol", type = CommandType.STRING, required = false, description = "l4 protocol (TCP or UDP)") + private String l4protocol; + + @Parameter(name = "l7protocol", type = CommandType.STRING, required = false, description = "l7 protocol (HTTP, HTTPS or Outros)") + private String l7protocol; + + @Parameter(name = "redeploy", type = CommandType.BOOLEAN, required = false, description = "force redeploy vip when need to change l4 and l7 protocol") + private Boolean redeploy = false; + + @Inject + GloboNetworkManager _globoNetworkService; + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsChildLoadBalancer(lbId, getActualCommandName()); + validateParams(); + ListResponse response = new ListResponse(); + + + Protocol.L4 l4 = null; + if (l4protocol != null){ + l4 = Protocol.L4.valueOfFromNetworkAPI(l4protocol); + } + Protocol.L7 l7 = null; + if (l7protocol != null) { + l7 = Protocol.L7.valueOfFromNetworkAPI(l7protocol); + } + + List pools = _globoNetworkService.updatePools(getPoolIds(), getLbId(), getZoneId(), + getHealthcheckType(), getHealthcheck(), getExpectedHealthcheck(), getMaxConn(), l4, l7, redeploy); + + List lbResponses = new ArrayList<>(); + + for ( GloboNetworkPoolResponse.Pool pool : pools) { + + PoolResponse poolResp = new PoolResponse(); + poolResp.setId(pool.getId()); + poolResp.setName(pool.getIdentifier()); + poolResp.setLbMethod(pool.getLbMethod()); + poolResp.setPort(pool.getPort()); + poolResp.setHealthcheckType(pool.getHealthcheckType()); + poolResp.setHealthcheck(pool.getHealthcheck()); + poolResp.setExpectedHealthcheck(pool.getExpectedHealthcheck()); + poolResp.setMaxconn(pool.getMaxconn()); + + + poolResp.setObjectName("globonetworkpool"); + lbResponses.add(poolResp); + + } + + response.setResponses(lbResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + protected void validateParams() { + if (l4protocol != null && !Protocol.validValueL4(l4protocol)) { + throw new CloudRuntimeException("l4protocol invalid value, possible values: " + Protocol.L4.getNetworkAPIValues() + "."); + } + + + if (l7protocol != null && !Protocol.validValueL7(l7protocol)) { + throw new CloudRuntimeException("l7protocol invalid value, possible values: " + Protocol.L7.getNetworkAPIValues() + "."); + } + + if (l4protocol != null && l7protocol != null && !redeploy) { + throw new CloudRuntimeException("Can not change l4protocol and l7protocol values when redeploy is false!"); + } + + if (l4protocol != null && l7protocol != null) { + Protocol.L4 l4 = Protocol.L4.valueOfFromNetworkAPI(l4protocol); + Protocol.L7 l7 = Protocol.L7.valueOfFromNetworkAPI(l7protocol); + + if (!Protocol.validProtocols(l4, l7)) { + throw new CloudRuntimeException("l4protocol with value '" + l4.getNetworkApiOptionValue() + "' does not match with l7protocol '" + l7.getNetworkApiOptionValue() + "'. Possible l7 value(s): " + l4.getL7s() + "."); + } + } + } + + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + + public Long getLbId() { + return lbId; + } + + public String getHealthcheckType() { + return healthcheckType; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public String getHealthcheck() { + if ( healthcheck != null && healthcheck.equals("null")) { + healthcheck = null; //fix for sanity tests, cloudmonkey doesnt allow send parameter "" + } + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public String getExpectedHealthcheck() { + if ( expectedHealthcheck != null && expectedHealthcheck.equals("null")) { + expectedHealthcheck = null; //fix for sanity tests, cloudmonkey doesnt allow send parameter "" + } + return expectedHealthcheck; + } + + public void setExpectedHealthcheck(String expectedHealthcheck) { + this.expectedHealthcheck = expectedHealthcheck; + } + + public Integer getMaxConn() { return maxConn != null ? maxConn : 0; } + + public void setMaxConn(Integer maxConn) { this.maxConn = maxConn; } + + private Long getZoneId() { + + return zoneId; + } + public void setLbId(Long lbId) { + this.lbId = lbId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public List getPoolIds() { + return poolIds; + } + + public void setPoolIds(List poolIds) { + this.poolIds = poolIds; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_HEALTHCHECKPOLICY_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating Pool Healthcheck in GloboNetwork"; + } + + + public String getL4protocol() { + return l4protocol; + } + + public void setL4protocol(String l4protocol) { + this.l4protocol = l4protocol; + } + + public String getL7protocol() { + return l7protocol; + } + + public void setL7protocol(String l7protocol) { + this.l7protocol = l7protocol; + } + + public Boolean getRedeploy() { + return redeploy; + } + + public void setRedeploy(Boolean redeploy) { + this.redeploy = redeploy; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UrlResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UrlResponse.java new file mode 100644 index 000000000000..26c28bfd43fb --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/UrlResponse.java @@ -0,0 +1,35 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.api; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class UrlResponse extends BaseResponse { + + @SerializedName(ApiConstants.URL) + @Param(description = "url") + private String url; + + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmd.java new file mode 100644 index 000000000000..f709a6c6181c --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmd.java @@ -0,0 +1,337 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.addr.PublicIp; + +import com.cloud.network.rules.HealthCheckPolicy; +import com.cloud.network.rules.StickinessPolicy; +import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.UserCloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.manager.HealthCheckHelper; +import com.globo.globonetwork.cloudstack.resource.GloboNetworkResource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerRuleCmd; +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.apache.log4j.Logger; + +@APICommand(name = "createGloboLoadBalancer", description = "Creates a globo load balancer", responseObject = LoadBalancerResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class CreateGloboLoadBalancerCmd extends CreateLoadBalancerRuleCmd /*implements LoadBalancer */{ + public static final Logger s_logger = Logger.getLogger(CreateGloboLoadBalancerCmd.class.getName()); + + private static final String s_name = "creategloboloadbalancerresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Inject + private GloboNetworkService globoNetworkSvc; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project the IP address will be associated with") + private Long projectId; + + @Parameter(name = "lbenvironmentid", type = CommandType.LONG, description = "Globo Network LB Environment Id", required = true) + private Long globoNetworkLBEnvironmentId; + + @Parameter(name = "healthcheckrequest", type = CommandType.STRING, required = false, description = "HTTP Request Path (ex: /healthcheck.html)") + private String healthcheckRequest; + + + @Parameter(name = "stickinessmethodname", type = CommandType.STRING, + description = "name of the LB Stickiness policy method.") + private String stickinessMethodName; + private static final String DEFAULT_ERROR_MESSAGE = "Error trying to create load balancer, please contact your system administrator."; + + @Inject + GloboNetworkService _globoNetworkService; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + private boolean ipCreated = false; + private CreateLBHealthCheckPolicyCmd healthCmd; + private CreateLBStickinessPolicyCmd stickinessCmd; + private Long ipAddressId = null; + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void create() throws CloudRuntimeException{ + validation(); + CallContext current = CallContext.current(); + final String name = getName(); + try { + PublicIp ip = globoNetworkSvc.acquireLbIp(getNetworkId(), getProjectId(), getGloboNetworkLBEnvironmentId()); + IpAddress ipAddress = _networkService.associateIPToNetwork(ip.getId(), getNetworkId()); + ipAddressId = ipAddress.getId(); + ipCreated = true; + + setPublicIpId(ip.getId()); + setSourceIpAddressId(ipAddress.getId()); + + s_logger.debug("[load_balancer " + name + "] portable ip created with id " + ip.getId() + ", ipaddress " + ipAddress.getId()); + + super.create(); + } catch (UserCloudRuntimeException e) { + throw e; + } catch (Exception e) { + s_logger.error("[load_balancer " + name + "] error creating load balancer ", e); + handleErrorAfterIpCreated(name, ipCreated, ipAddressId); + + + String msg = DEFAULT_ERROR_MESSAGE; + if ( current != null) { + msg += " Context: " + current.getNdcContext() + ". Error: " + e.getLocalizedMessage(); + } + throw new CloudRuntimeException(msg, e); + } + } + + @Override + public void execute() throws ResourceAllocationException, ResourceUnavailableException { + final String name = getName(); + try { + s_logger.debug("[load_balancer " + name + "] processing execute createGloboLoadBalance"); + + super.execute(); + createHealthcheck(); + createStickiness(); + loadGloboResourceConfig(); + + } catch (Exception e) { + s_logger.warn("[load_balancer " + name + "] removing loadbalancer after error", e); + _lbService.deleteLoadBalancerRule(getEntityId(), false); + handleErrorAfterIpCreated(name, ipCreated, ipAddressId); + throw e; + } + } + + private void validation() { + HealthCheckHelper healthcheck = HealthCheckHelper.build(getName(), getHealthCheckType(), healthcheckRequest, getExpectedHealthCheck()); + + if ( HealthCheckHelper.HealthCheckType.isLayer7(healthcheck.getHealthCheckType()) + && healthcheck.getHealthCheck().isEmpty()) { + throw new CloudRuntimeException("Health check validation: when health check type is HTTP/HTTPS, health check request can not be empty. type: " + healthcheck.getHealthCheckType() + ", request: " + healthcheck.getHealthCheck()); + } + if (stickinessMethodName != null && !stickinessMethodName.isEmpty()) + GloboNetworkResource.PersistenceMethod.validateValue(stickinessMethodName); + + } + + protected void createStickiness() throws ResourceUnavailableException { + final String name = getName(); + + if (!isToCreateStickiness()) { + s_logger.debug("[load_balancer " + name + "] it is not necessary to create stickiness. stickinessmethodname: " + stickinessMethodName); + return; + } + + try { + s_logger.debug("[load_balancer " + name + "] creating stickiness policy "); + stickinessCmd = new CreateLBStickinessPolicyCmd(); + stickinessCmd.setStickinessMethodName(stickinessMethodName); + stickinessCmd.setLbStickinessPolicyName(stickinessMethodName); + stickinessCmd.setLbRuleId(getEntityId()); + + StickinessPolicy result = _lbService.createLBStickinessPolicy(stickinessCmd); + stickinessCmd.setEntityId(result.getId()); + stickinessCmd.setEntityUuid(result.getUuid()); + + boolean success = _lbService.applyLBStickinessPolicy(stickinessCmd); // with error it already rollback the stickiness + if (!success) { + throw new CloudRuntimeException(DEFAULT_ERROR_MESSAGE); + } + } catch (NetworkRuleConflictException e) { + throw new CloudRuntimeException(DEFAULT_ERROR_MESSAGE, e); + } + + } + + + protected void createHealthcheck() { + final String name = getName(); + + if (!isToCreateHealthcheck()) { + s_logger.debug("[load_balancer " + name + "] it is not necessary to create healthcheck. healthcheckRequest: " + healthcheckRequest); + return; + } + + s_logger.debug("[load_balancer " + name + "] creating healthcheck "); + healthCmd = new CreateLBHealthCheckPolicyCmd(); + healthCmd.setFullUrlParams(new HashMap()); + healthCmd.setPingPath(healthcheckRequest); + healthCmd.setLbRuleId(this.getEntityId()); + + + HealthCheckPolicy healthcheck = _lbService.createLBHealthCheckPolicy(healthCmd); + + healthCmd.setEntityId(healthcheck.getId()); + healthCmd.setEntityUuid(healthcheck.getUuid()); + + s_logger.debug("[load_balancer " + name + "] apply healthcheck: " + healthcheck.getUuid()); + try { + _lbService.applyLBHealthCheckPolicy(healthCmd); // with error it already rollback the healthcheck + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException(DEFAULT_ERROR_MESSAGE, e); + } + + } + + + private void loadGloboResourceConfig() { + LoadBalancerResponse lbResponse = (LoadBalancerResponse)getResponseObject(); + List globoResourceConfigs = _globoNetworkService.getGloboResourceConfigs(this.getEntityUuid(), GloboResourceType.LOAD_BALANCER); + List configs = new ArrayList(); + for (GloboResourceConfiguration config : globoResourceConfigs) { + LoadBalancerResponse.GloboResourceConfigurationResponse conf = new LoadBalancerResponse.GloboResourceConfigurationResponse(); + conf.setConfigurationKey(config.getKey().name()); + conf.setConfigurationValue(config.getValue()); + configs.add(conf); + } + + lbResponse.setGloboResourceConfigs(configs); + } + + + @Override + public long getEntityOwnerId() { + return getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LOAD_BALANCER_CREATE; + } + + @Override + public String getEventDescription() { + return "creating load balancer: " + getName() + " account: " + getAccountName(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.FirewallRule; + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + public void setGloboNetworkLBEnvironmentId(Long globoNetworkLBEnvironmentId) { + this.globoNetworkLBEnvironmentId = globoNetworkLBEnvironmentId; + } + + public long getAccountId() { + long networkId = getNetworkId(); + + Network network = _networkService.getNetwork(networkId); + return network.getAccountId(); + } + + + private void handleErrorAfterIpCreated(String lbName, boolean ipCreated, Long ipAddressId) { + if (ipCreated) { + boolean result = globoNetworkSvc.disassociateIpAddrFromGloboNetwork(ipAddressId); + if (result) { + s_logger.warn("[load_balancer " + lbName + "] error after try to disassociateIp " + ipAddressId); + } + } + } + + public boolean isToCreateStickiness() { + return stickinessMethodName != null && !stickinessMethodName.isEmpty(); + } + + public boolean isToCreateHealthcheck() { + return healthcheckRequest != null && !healthcheckRequest.isEmpty(); + } + + + public void setPingPath(String pingPath) { + this.healthcheckRequest = pingPath; + } + + public void setStickinessMethodName(String stickinessMethodName) { + this.stickinessMethodName = stickinessMethodName; + } + + public String getStickinessMethodName() { + return stickinessMethodName; + } + + @Override + public String getHealthCheckType() { + try { + HealthCheckHelper.HealthCheckType.valueOf(healthCheckType); + }catch (IllegalArgumentException e) { + throw new UserCloudRuntimeException("Health check type \'"+ healthCheckType + "\' not allow, possible values: " + StringUtils.join(", ", HealthCheckHelper.HealthCheckType.values()) + "."); + } + + return healthCheckType; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/DeleteGloboLoadBalancerCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/DeleteGloboLoadBalancerCmd.java new file mode 100644 index 000000000000..eb5005390d8f --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/DeleteGloboLoadBalancerCmd.java @@ -0,0 +1,146 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import javax.inject.Inject; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; + +@APICommand(name = "deleteGloboLoadBalancer", description = "Deletes a load balancer rule.", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class DeleteGloboLoadBalancerCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteGloboLoadBalancerCmd.class.getName()); + private static final String s_name = "deletegloboloadbalancerresponse"; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Inject + private GloboNetworkService globoNetworkSvc; + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = FirewallRuleResponse.class, + required = true, + description = "the ID of the load balancer rule") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getId()); + if (lb != null) { + return lb.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LOAD_BALANCER_DELETE; + } + + @Override + public String getEventDescription() { + return "deleting load balancer: " + getId(); + } + + @Override + public void execute() { + _lbService.throwExceptionIfIsParentLoadBalancer(id, getActualCommandName()); + + LoadBalancer lb = _lbService.findById(id); + if (lb == null) { + throw new CloudRuntimeException("Could not find load balancer " + getId()); + } + CallContext.current().setEventDetails("Load balancer Id: " + getId()); + + boolean result = _firewallService.revokeRelatedFirewallRule(id, true); + result = result && _lbService.deleteLoadBalancerRule(id, true); + + if (result) { + removeIpAddress(lb); + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete load balancer"); + } + } + + private void removeIpAddress(final LoadBalancer lb) { + try { + s_logger.debug("[load_balancer " + lb.getName() + "] removing ipaddress " + lb.getSourceIpAddressId()); + globoNetworkSvc.disassociateIpAddrFromGloboNetwork(lb.getSourceIpAddressId()); + + } catch (Exception e) { + throw new CloudRuntimeException("Load balancer " + lb.getName() + " was deleted, but the IpAddress "+ lb.getSourceIpAddressId() +" could not be released, please contact your system administrator.", e); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _lbService.findById(id); + if (lb == null) { + throw new InvalidParameterValueException("Unable to find load balancer rule: " + id); + } + return lb.getNetworkId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.FirewallRule; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmd.java new file mode 100644 index 000000000000..6c48c8627e95 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmd.java @@ -0,0 +1,107 @@ +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.event.EventTypes; + +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboLoadBalancerService; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; + +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; + +import javax.inject.Inject; + +@APICommand(name = "linkGloboLoadBalancer", description = "Link a load balancer with another lb, it will add pool's load balancer parent in load balancer child ", responseObject = LoadBalancerResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class LinkGloboLoadBalancerCmd extends BaseAsyncCmd { + + private static final String s_name = "linkgloboloadbalancerresponse"; + + @Inject + protected GloboNetworkService globoNetworkSvc; + + @Inject + protected GloboLoadBalancerService globoLBService; + + @Parameter(name = ApiConstants.LB_CHILD_LBID, + type = CommandType.UUID, + entityType = FirewallRuleResponse.class, + required = true, + description = "Load Balancer Child Id") + private Long childlbid; + + + @Parameter(name = ApiConstants.LB_PARENT_LBID, + type = CommandType.UUID, + entityType = FirewallRuleResponse.class, + required = true, + description = "Load Balancer Parent Id") + private Long parentlbid; + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_LINK; + } + + @Override + public String getEventDescription() { + return "link load balancer child in parent"; + } + + @Override + public void execute() throws CloudRuntimeException { + LoadBalancer lb = globoLBService.linkLoadBalancer(childlbid, parentlbid); + + LoadBalancerResponse lbResponse = _responseGenerator.createLoadBalancerResponse(lb); + + GloboResourceConfiguration linkedConfig = globoNetworkSvc.getGloboResourceConfiguration(lb.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + + if (linkedConfig != null) { + LoadBalancer targetLb = _lbService.findByUuid(linkedConfig.getValue()); + + lbResponse.setLinkedParentLoadBalancer(targetLb, linkedConfig); + } + + setResponseObject(lbResponse); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, childlbid); + 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 getChildlbid() { + return childlbid; + } + + public void setChildlbid(Long childlbid) { + this.childlbid = childlbid; + } + + public Long getParentlbid() { + return parentlbid; + } + + public void setParentlbid(Long parentlbid) { + this.parentlbid = parentlbid; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmd.java new file mode 100644 index 000000000000..897e8410ee1b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmd.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.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.dao.LoadBalancerVO; +import com.globo.globonetwork.cloudstack.api.response.LinkableLoadBalancerResponse; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +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.FirewallRuleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listGloboLinkableLoadBalancers", responseObject = LinkableLoadBalancerResponse.class, description = "Lists all environments from GloboNetwork") +public class ListGloboLinkableLoadBalancersCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ListGloboLinkableLoadBalancersCmd.class.getName()); + + private static final String s_name = "listlinkableloadbalancerresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Inject + protected GloboNetworkService globoNetworkSvc; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Project the IP address will be associated with") + private Long projectId; + + @Parameter(name = ApiConstants.LBID, + type = CommandType.UUID, + entityType = FirewallRuleResponse.class, + required = true, + description = "the ID of the load balancer rule") + private Long lbid; + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + @Override + public void execute() throws ResourceAllocationException, ResourceUnavailableException { + List lbs = globoNetworkSvc.listLinkableLoadBalancers(lbid, projectId); + + + List lbResponse = new ArrayList<>(); + + for (LoadBalancerVO lb : lbs) { + LinkableLoadBalancerResponse llb = new LinkableLoadBalancerResponse(); + llb.setName(lb.getName()); + llb.setUuid(lb.getUuid()); + lbResponse.add(llb); + } + + + ListResponse response = new ListResponse(); + response.setResponses(lbResponse, lbResponse.size()); + response.setResponseName(getCommandName()); + + setResponseObject(response); + } + + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getLbid() { + return lbid; + } + + public void setLbid(Long lbid) { + this.lbid = lbid; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/UnlinkGloboLoadBalancerCmd.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/UnlinkGloboLoadBalancerCmd.java new file mode 100644 index 000000000000..fe1d80736e41 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/loadbalancer/UnlinkGloboLoadBalancerCmd.java @@ -0,0 +1,64 @@ +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.event.EventTypes; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboLoadBalancerService; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.LoadBalancerResponse; + + +import javax.inject.Inject; + +@APICommand(name = "unlinkGloboLoadBalancer", description = "Unlink a load balancer from another one, all vms and networks keep in load balancer", responseObject = LoadBalancerResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UnlinkGloboLoadBalancerCmd extends BaseAsyncCmd { + + private static final String s_name = "unlinkgloboloadbalancerresponse"; + + @Inject + protected GloboLoadBalancerService globoLBService; + + + @Parameter(name = ApiConstants.LBID, + type = CommandType.UUID, + entityType = FirewallRuleResponse.class, + required = true, + description = "the ID of the load balancer rule") + private Long lbid; + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_UNLINK; + } + + @Override + public String getEventDescription() { + return EventTypes.EVENT_LB_UNLINK; + } + + + @Override + public void execute() throws CloudRuntimeException { + LoadBalancer lb = globoLBService.unlinkLoadBalancer(lbid); + + LoadBalancerResponse lbResponse = _responseGenerator.createLoadBalancerResponse(lb); + + setResponseObject(lbResponse); + } + + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/response/LinkableLoadBalancerResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/response/LinkableLoadBalancerResponse.java new file mode 100644 index 000000000000..fed3c3609f90 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/api/response/LinkableLoadBalancerResponse.java @@ -0,0 +1,50 @@ +package com.globo.globonetwork.cloudstack.api.response; + +import org.apache.cloudstack.api.BaseResponse; + +public class LinkableLoadBalancerResponse extends BaseResponse { + + private String uuid; + + private String name; + + private String lbenv; + + private String network; + + public LinkableLoadBalancerResponse() { + setObjectName("linkableloadbalancerresponse"); + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLbenv() { + return lbenv; + } + + public void setLbenv(String lbenv) { + this.lbenv = lbenv; + } + + public String getNetwork() { + return network; + } + + public void setNetwork(String network) { + this.network = network; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/AcquireNewIpForLbCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/AcquireNewIpForLbCommand.java new file mode 100644 index 000000000000..72db9cc906e2 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/AcquireNewIpForLbCommand.java @@ -0,0 +1,58 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class AcquireNewIpForLbCommand extends Command { + + private long globoNetworkLBEnvironmentId; + + private boolean isv6; + + private String description; + + public AcquireNewIpForLbCommand(long globoNetworkLBEnvironmentId, boolean isv6) { + this.globoNetworkLBEnvironmentId = globoNetworkLBEnvironmentId; + this.setIsv6(isv6); + } + + @Override + public boolean executeInSequence() { + return false; + } + + public long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ActivateNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ActivateNetworkCommand.java new file mode 100644 index 000000000000..451c10671019 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ActivateNetworkCommand.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ActivateNetworkCommand extends Command { + + private long networkId; + + private boolean isv6; + + public ActivateNetworkCommand(long networkId, boolean isv6) { + this.networkId = networkId; + this.isv6 = isv6; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public long getNetworkId() { + return networkId; + } + + public boolean isv6() { + return isv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ApplyVipInGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ApplyVipInGloboNetworkCommand.java new file mode 100644 index 000000000000..b659fb54960e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ApplyVipInGloboNetworkCommand.java @@ -0,0 +1,227 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.agent.api.Command; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; + +public class ApplyVipInGloboNetworkCommand extends Command { + + private FirewallRule.State ruleState; + + private Long vipId; + + private String ipv4; + + private String methodBal; + + private LoadBalancingRule.LbStickinessPolicy persistencePolicy; + + private Long vipEnvironmentId; + + private String businessArea; + + private String host; + + private String cache; + + private boolean dsr; + + private String serviceDownAction; + + private String healthCheckDestination; + + private String expectedHealthcheck; + + private String healthcheckType; + + private String healthcheck; + + private String serviceName; + + private List ports; + + private List realList; + private String region; + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVipId() { + return vipId; + } + + public void setVipId(Long vipId) { + this.vipId = vipId; + } + + public String getIpv4() { + return ipv4; + } + + public void setIpv4(String ipv4) { + this.ipv4 = ipv4; + } + + public String getMethodBal() { + return methodBal; + } + + public void setMethodBal(String methodBal) { + this.methodBal = methodBal; + } + + public Long getVipEnvironmentId() { + return vipEnvironmentId; + } + + public void setVipEnvironmentId(Long vipEnvironmentId) { + this.vipEnvironmentId = vipEnvironmentId; + } + + public String getBusinessArea() { + return businessArea; + } + + public void setBusinessArea(String businessArea) { + this.businessArea = businessArea; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getCache() { return cache; } + + public void setCache(String cache) { this.cache = cache; } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public List getPorts() { + return ports; + } + + public List> getPortPairs(){ + List> portPairs = new ArrayList<>(); + for(String portPair : ports){ + String[] splitPorts = portPair.split(":"); + portPairs.add(new Pair<>(new Integer(splitPorts[0]), new Integer(splitPorts[1]))); + } + return portPairs; + } + + public void setPorts(List ports) { + this.ports = ports; + } + + public List getRealList() { + return realList; + } + + public void setRealList(List realList) { + this.realList = realList; + } + + public FirewallRule.State getRuleState() { + return ruleState; + } + + public void setRuleState(FirewallRule.State ruleState) { + this.ruleState = ruleState; + } + + public LoadBalancingRule.LbStickinessPolicy getPersistencePolicy() { + return persistencePolicy; + } + + public void setPersistencePolicy(LoadBalancingRule.LbStickinessPolicy persistencePolicy) { + this.persistencePolicy = persistencePolicy; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public void setServiceDownAction(String serviceDownAction) { + this.serviceDownAction = serviceDownAction; + } + + public String getServiceDownAction() { + return serviceDownAction; + } + + public void setHealthCheckDestination(String healthCheckDestination) { + this.healthCheckDestination = healthCheckDestination; + } + + public String getHealthCheckDestination() { + return healthCheckDestination; + } + + public String getExpectedHealthcheck() { + return expectedHealthcheck; + } + + public void setExpectedHealthcheck(String expectedHealthcheck) { + this.expectedHealthcheck = expectedHealthcheck; + } + + public String getHealthcheckType() { + return healthcheckType; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public String getRegion() { + return region; + } + public void setRegion(String region) { + this.region = region; + } + + public void setDsr(boolean dsr) { + this.dsr = dsr; + } + + public boolean isDsr() { + return dsr; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CheckDSREnabled.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CheckDSREnabled.java new file mode 100644 index 000000000000..cc8bc2463749 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CheckDSREnabled.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.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class CheckDSREnabled extends Command { + + private Long vipEnvironmentId; + + public CheckDSREnabled(Long vipEnvironmentId) { + this.vipEnvironmentId = vipEnvironmentId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVipEnvironmentId() { + return vipEnvironmentId; + } + + public void setVipEnvironmentId(Long vipEnvironmentId) { + this.vipEnvironmentId = vipEnvironmentId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreateNewVlanInGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreateNewVlanInGloboNetworkCommand.java new file mode 100644 index 000000000000..934dca3b1f8b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreateNewVlanInGloboNetworkCommand.java @@ -0,0 +1,81 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class CreateNewVlanInGloboNetworkCommand extends Command { + + private String vlanName; + + private String vlanDescription; + + private Long globoNetworkEnvironmentId; + + private Boolean isIpv6; + + private Long subnet; + + @Override + public boolean executeInSequence() { + return false; + } + + public String getVlanName() { + return vlanName; + } + + public void setVlanName(String vlanName) { + this.vlanName = vlanName; + } + + public String getVlanDescription() { + return vlanDescription; + } + + public void setVlanDescription(String vlanDescription) { + this.vlanDescription = vlanDescription; + } + + public Long getGloboNetworkEnvironmentId() { + return globoNetworkEnvironmentId; + } + + public void setGloboNetworkEnvironmentId(Long globoNetworkEnvironmentId) { + this.globoNetworkEnvironmentId = globoNetworkEnvironmentId; + } + + public Boolean getIsIpv6() { + return isIpv6; + } + + public Long getSubnet() { + return subnet; + } + + public void setSubnet(Long subnet) { + this.subnet = subnet; + } + + public Boolean isIpv6() { + return isIpv6; + } + + public void setIsIpv6(Boolean isIpv6) { + this.isIpv6 = isIpv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreatePoolCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreatePoolCommand.java new file mode 100644 index 000000000000..08eccffd498a --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/CreatePoolCommand.java @@ -0,0 +1,141 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; +import com.globo.globonetwork.cloudstack.manager.Protocol; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; + +import java.util.List; + +public class CreatePoolCommand extends Command { + + private Long vipId; + private String vipName; + private String vipIp; + private Integer publicPort; + private Integer privatePort; + private String balacingAlgorithm; + private String serviceDownAction; + private String region; + private Long vipEnvironment; + private Protocol.L4 l4protocol; + private Protocol.L7 l7protocol; + private List reals; + + @Override + public boolean executeInSequence() { + return false; + } + + public void setVipId(Long vipId) { + this.vipId = vipId; + } + + public Long getVipId() { + return vipId; + } + + public void setVipName(String vipName) { + this.vipName = vipName; + } + + public String getVipName() { + return vipName; + } + + public void setPublicPort(Integer publicPort) { + this.publicPort = publicPort; + } + + public Integer getPublicPort() { + return publicPort; + } + + public void setPrivatePort(Integer privatePort) { + this.privatePort = privatePort; + } + + public Integer getPrivatePort() { + return privatePort; + } + + public void setBalacingAlgorithm(String balacingAlgorithm) { + this.balacingAlgorithm = balacingAlgorithm; + } + + public String getBalacingAlgorithm() { + return balacingAlgorithm; + } + + public void setServiceDownAction(String serviceDownAction) { + this.serviceDownAction = serviceDownAction; + } + + public String getServiceDownAction() { + return serviceDownAction; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getRegion() { + return region; + } + + public void setVipEnvironment(Long vipEnvironment) { + this.vipEnvironment = vipEnvironment; + } + + public Long getVipEnvironment() { + return vipEnvironment; + } + + public void setReals(List reals) { + this.reals = reals; + } + + public List getReals() { + return reals; + } + + public void setVipIp(String vipIp) { + this.vipIp = vipIp; + } + + public String getVipIp() { + return vipIp; + } + + public Protocol.L4 getL4protocol() { + return l4protocol; + } + + public void setL4protocol(Protocol.L4 l4protocol) { + this.l4protocol = l4protocol; + } + + public Protocol.L7 getL7protocol() { + return l7protocol; + } + + public void setL7protocol(Protocol.L7 l7protocol) { + this.l7protocol = l7protocol; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeallocateVlanFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeallocateVlanFromGloboNetworkCommand.java new file mode 100644 index 000000000000..69a33323dcf4 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeallocateVlanFromGloboNetworkCommand.java @@ -0,0 +1,43 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +/** + * Deallocates an inactive VLAN in GloboNetwork + * @author Daniel Vega + * + */ +public class DeallocateVlanFromGloboNetworkCommand extends Command { + + private Long vlanId; + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVlanId() { + return vlanId; + } + + public void setVlanId(Long vlanId) { + this.vlanId = vlanId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeletePoolCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeletePoolCommand.java new file mode 100644 index 000000000000..ef7c15474f73 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/DeletePoolCommand.java @@ -0,0 +1,51 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class DeletePoolCommand extends Command{ + + private final Integer vipPort; + private Long poolId; + private Long vipId; + + public DeletePoolCommand(Long vipId, Long poolId, Integer vipPort) { + this.vipId = vipId; + this.poolId = poolId; + this.vipPort = vipPort; + } + + public Long getPoolId() { + return poolId; + } + + public Long getVipId() { + return vipId; + } + + public Integer getVipPort() { + return vipPort; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetNetworkFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetNetworkFromGloboNetworkCommand.java new file mode 100644 index 000000000000..bd947899f85b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetNetworkFromGloboNetworkCommand.java @@ -0,0 +1,44 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class GetNetworkFromGloboNetworkCommand extends Command { + + private Long networkId; + + private boolean isv6; + + public GetNetworkFromGloboNetworkCommand(Long networkId, boolean isv6) { + this.networkId = networkId; + this.isv6 = isv6; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getNetworkId() { + return networkId; + } + + public boolean isv6() { + return isv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetPoolLBByIdCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetPoolLBByIdCommand.java new file mode 100644 index 000000000000..f43ba1ee96b5 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetPoolLBByIdCommand.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.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class GetPoolLBByIdCommand extends Command{ + + private Long poolId; + + public GetPoolLBByIdCommand(Long poolId) { + this.poolId = poolId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getPoolId() { + return poolId; + } + + public void setPoolId(Long poolId) { + this.poolId = poolId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVipInfoFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVipInfoFromGloboNetworkCommand.java new file mode 100644 index 000000000000..e4c95e531c0f --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVipInfoFromGloboNetworkCommand.java @@ -0,0 +1,60 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class GetVipInfoFromGloboNetworkCommand extends Command { + + private Long vipId; + + private String ip; + + private Long vipEnvironmentId; + + private boolean isv6; + + public GetVipInfoFromGloboNetworkCommand(long vipId, boolean isv6) { + this.vipId = vipId; + this.isv6 = isv6; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVipId() { + return vipId; + } + + public String getIp() { + return ip; + } + + public Long getVipEnvironmentId() { + return this.vipEnvironmentId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVlanInfoFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVlanInfoFromGloboNetworkCommand.java new file mode 100644 index 000000000000..7864fbe9db53 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GetVlanInfoFromGloboNetworkCommand.java @@ -0,0 +1,38 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class GetVlanInfoFromGloboNetworkCommand extends Command { + + private Long vlanId; + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVlanId() { + return vlanId; + } + + public void setVlanId(Long vlanId) { + this.vlanId = vlanId; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkErrorAnswer.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkErrorAnswer.java new file mode 100644 index 000000000000..3c3cb0cd7426 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkErrorAnswer.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import org.apache.cloudstack.context.CallContext; + +public class GloboNetworkErrorAnswer extends Answer { + + protected int napiCode; + protected String napiDescription; + protected String context; + + public GloboNetworkErrorAnswer(Command command, int napiCode, String napiDescription) { + super(command, false, napiCode + " - " + napiDescription); + this.napiCode = napiCode; + this.napiDescription = napiDescription; + CallContext callContext = CallContext.current(); + if (callContext != null){ + context = callContext.getNdcContext(); + } + } + + public int getNapiCode() { + return this.napiCode; + } + + public String getNapiDescription() { + return this.napiDescription; + } + + public String getNdcContext() { + return context; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkResourceCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkResourceCommand.java new file mode 100644 index 000000000000..c112cb927abd --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/GloboNetworkResourceCommand.java @@ -0,0 +1,44 @@ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.exception.GloboNetworkErrorCodeException; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import java.nio.charset.Charset; +import org.apache.log4j.Logger; + +public abstract class GloboNetworkResourceCommand extends Command { + + private static final Logger s_logger = Logger.getLogger(GloboNetworkResourceCommand.class); + + @Override + public boolean executeInSequence() { + return false; + } + + + public abstract Answer execute(GloboNetworkAPI api); + + + public static Answer handleGloboNetworkException(Command cmd, GloboNetworkException e) { + if (e instanceof GloboNetworkErrorCodeException) { + GloboNetworkErrorCodeException ex = (GloboNetworkErrorCodeException)e; + s_logger.error("Error accessing GloboNetwork: " + ex.getCode() + " - " + ex.getDescription(), ex); + return new GloboNetworkErrorAnswer(cmd, ex.getCode(), getUtf8(ex.getDescription())); + } else { + s_logger.error("Generic error accessing GloboNetwork", e); + return new Answer(cmd, false, e.getMessage()); + } + } + public static String getUtf8(String value) { + try { + Charset charsetISO = Charset.forName("ISO_8859_1"); + byte text[] = value.getBytes(charsetISO); + return new String(text, Charset.defaultCharset()); + } catch (Exception e) { + s_logger.warn("could not convert message: " + value + " error:" + e.getMessage()); + } + return value; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/LinkParentLbPoolsInChildLbCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/LinkParentLbPoolsInChildLbCommand.java new file mode 100644 index 000000000000..f412a1ae34ae --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/LinkParentLbPoolsInChildLbCommand.java @@ -0,0 +1,110 @@ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.api.VipV3API; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.resource.VipAPIFacade; +import java.util.ArrayList; +import java.util.List; +import org.apache.log4j.Logger; + +public class LinkParentLbPoolsInChildLbCommand extends GloboNetworkResourceCommand { + + private static final Logger s_logger = Logger.getLogger(LinkParentLbPoolsInChildLbCommand.class); + + + private String childLbId; + private String childLbName; + private Long childVipId; + + private String parentLbId; + private Long parentVipId; + private String parentLbName; + + @Override + public Answer execute(GloboNetworkAPI api) { + try { + s_logger.debug("[LinkLBPools " + parentVipId + ":" + childVipId + "] adding pools from parent lb '" + parentLbName + "'(" + parentVipId + ") in bhild lb '" + childLbName + "'(" + childVipId + ")."); + + VipAPIFacade parentFacade = new VipAPIFacade(parentVipId, api); + VipAPIFacade childFacade = new VipAPIFacade(childVipId, api); + + VipV3 targetVip = parentFacade.getVip(); + + VipV3 vip = childFacade.getVip(); + List poolsToDelete = getPoolsToDelete(vip); + + updateSourceVipWithNewPorts(api, targetVip.getPorts(), vip); + + s_logger.debug("[LinkLBPools " + parentVipId + ":" + childVipId + "] removing pools: " + poolsToDelete); + deleteSourcePools(api, poolsToDelete); + + return new Answer(this, true, ""); + } catch (GloboNetworkException e) { + return GloboNetworkResourceCommand.handleGloboNetworkException(this, e); + } + + + } + + private void updateSourceVipWithNewPorts(GloboNetworkAPI api, List ports, VipV3 vip) throws GloboNetworkException { + clearPortsIds(ports); + + VipV3API vipV3API = api.getVipV3API(); + vip.setPorts(ports); + if (vip.getCreated()) { + vipV3API.undeploy(vip.getId()); + } + vipV3API.save(vip); + vipV3API.deploy(vip.getId()); + } + + private void clearPortsIds(List ports) { + for (VipV3.Port port : ports) { + port.setId(null); + for(VipV3.Pool pool : port.getPools()) { + pool.setId(null); + } + } + } + + private void deleteSourcePools(GloboNetworkAPI api, List poolsToDelete) throws GloboNetworkException { + PoolAPI poolAPI = api.getPoolAPI(); + List byIdsV3 = poolAPI.getByIdsV3(poolsToDelete); + for (PoolV3 pool : byIdsV3) { + if (pool.isPoolCreated()){ + poolAPI.undeployV3(pool.getId()); + } + } + poolAPI.deleteV3(poolsToDelete); + } + + private List getPoolsToDelete(VipV3 vip) { + List poolsIds = new ArrayList<>(); + for (VipV3.Port port : vip.getPorts()) { + + for (VipV3.Pool pool : port.getPools()) { + poolsIds.add(pool.getPoolId()); + } + port.setPools(new ArrayList()); + + } + return poolsIds; + } + + public void setParentLb(String uuid, String name, Long vipId) { + this.parentLbId = uuid; + this.parentLbName = name; + this.parentVipId = vipId; + } + + public void setChildLb(String uuid, String name, Long vipId) { + this.childLbId = uuid; + this.childLbName = name; + this.childVipId = vipId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListAllEnvironmentsFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListAllEnvironmentsFromGloboNetworkCommand.java new file mode 100644 index 000000000000..2b414310350e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListAllEnvironmentsFromGloboNetworkCommand.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListAllEnvironmentsFromGloboNetworkCommand extends Command { + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListExpectedHealthchecksCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListExpectedHealthchecksCommand.java new file mode 100644 index 000000000000..dedc034ad435 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListExpectedHealthchecksCommand.java @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListExpectedHealthchecksCommand extends Command{ + + public ListExpectedHealthchecksCommand() {} + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListGloboNetworkLBCacheGroupsCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListGloboNetworkLBCacheGroupsCommand.java new file mode 100644 index 000000000000..f13c6a234829 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListGloboNetworkLBCacheGroupsCommand.java @@ -0,0 +1,37 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListGloboNetworkLBCacheGroupsCommand extends Command { + + private Long lbEnvironmentId; + + public Long getLBEnvironmentId() { + return lbEnvironmentId; + } + + public void setLBEnvironmentId(Long lbEnvironmentId) { + this.lbEnvironmentId = lbEnvironmentId; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolLBCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolLBCommand.java new file mode 100644 index 000000000000..5c2079ef127f --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolLBCommand.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.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListPoolLBCommand extends Command{ + + private Long vipId; + + public ListPoolLBCommand(Long vipId) { + this.vipId = vipId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getVipId() { + return vipId; + } + + public void setVipId(Long vipId) { + this.vipId = vipId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolOptionsCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolOptionsCommand.java new file mode 100644 index 000000000000..308cf26d1be0 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ListPoolOptionsCommand.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ListPoolOptionsCommand extends Command{ + + private Long globoNetworkLBEnvironmentId; + + private String type; + + public ListPoolOptionsCommand(Long environmentId, String type) { + this.globoNetworkLBEnvironmentId = environmentId; + this.type = type; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public Long getGloboNetworkLBEnvironmentId() { + return globoNetworkLBEnvironmentId; + } + + public String getType() { + return type; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RegisterEquipmentAndIpInGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RegisterEquipmentAndIpInGloboNetworkCommand.java new file mode 100644 index 000000000000..b0aebd8711ee --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RegisterEquipmentAndIpInGloboNetworkCommand.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class RegisterEquipmentAndIpInGloboNetworkCommand extends Command { + + private String nicIp; + private String nicDescription; + private String vmName; + private Long vlanId; + private Long environmentId; + private Long equipmentGroupId; + private Long equipmentModelId; + + @Override + public boolean executeInSequence() { + return true; + } + + public String getNicIp() { + return nicIp; + } + + public void setNicIp(String nicIp) { + this.nicIp = nicIp; + } + + public String getNicDescription() { + return nicDescription; + } + + public void setNicDescription(String nicDescription) { + this.nicDescription = nicDescription; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public Long getVlanId() { + return vlanId; + } + + public void setVlanId(Long vlanId) { + this.vlanId = vlanId; + } + + public Long getEnvironmentId() { + return environmentId; + } + + public void setEnvironmentId(Long environmentId) { + this.environmentId = environmentId; + } + + public Long getEquipmentGroupId() { + return equipmentGroupId; + } + + public void setEquipmentGroupId(Long equipmentGroupId) { + this.equipmentGroupId = equipmentGroupId; + } + + public Long getEquipmentModelId() { + return equipmentModelId; + } + + public void setEquipmentModelId(Long equipmentModelId) { + this.equipmentModelId = equipmentModelId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ReleaseIpFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ReleaseIpFromGloboNetworkCommand.java new file mode 100644 index 000000000000..171659bc40d8 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ReleaseIpFromGloboNetworkCommand.java @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ReleaseIpFromGloboNetworkCommand extends Command { + + private String ip; + + private boolean isv6; + + private long vipEnvironmentId; + + public ReleaseIpFromGloboNetworkCommand(String ip, long vipEnvironmentId, boolean isv6) { + this.ip = ip; + this.vipEnvironmentId = vipEnvironmentId; + this.isv6 = isv6; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getIp() { + return ip; + } + + public long getVipEnvironmentId() { + return vipEnvironmentId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveNetworkInGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveNetworkInGloboNetworkCommand.java new file mode 100644 index 000000000000..45e4dcd64987 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveNetworkInGloboNetworkCommand.java @@ -0,0 +1,55 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +/** + * Removes network from a specific VLAN in GloboNetwork + * @author Daniel Vega + * + */ +public class RemoveNetworkInGloboNetworkCommand extends Command { + + private Long networkId; + private boolean isIpv6; + + @Override + public boolean executeInSequence() { + return true; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public Long getNetworkId() { + return networkId; + } + + public void setIsIpv6(boolean isIpv6) { + this.isIpv6 = isIpv6; + } + + public boolean isIpv6() { + return isIpv6; + } + + public void setIpv6(boolean isIpv6) { + this.isIpv6 = isIpv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveVipFromGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveVipFromGloboNetworkCommand.java new file mode 100644 index 000000000000..562167c90d6b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/RemoveVipFromGloboNetworkCommand.java @@ -0,0 +1,51 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +/** + * Remove VIP from GloboNetwork + * + */ +public class RemoveVipFromGloboNetworkCommand extends Command { + + private Long vipId; + + private boolean keepIp; + + @Override + public boolean executeInSequence() { + return true; + } + + public Long getVipId() { + return vipId; + } + + public void setVipId(Long vipId) { + this.vipId = vipId; + } + + public boolean isKeepIp() { + return keepIp; + } + + public void setKeepIp(boolean keepIp) { + this.keepIp = keepIp; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnlinkPoolsFromLbCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnlinkPoolsFromLbCommand.java new file mode 100644 index 000000000000..2351f1185f40 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnlinkPoolsFromLbCommand.java @@ -0,0 +1,69 @@ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.api.VipV3API; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.resource.GloboNetworkResource; +import com.globo.globonetwork.cloudstack.resource.VipAPIFacade; +import java.util.ArrayList; +import org.apache.log4j.Logger; + + +public class UnlinkPoolsFromLbCommand extends GloboNetworkResourceCommand{ + + private static final Logger s_logger = Logger.getLogger(UnlinkPoolsFromLbCommand.class); + + private Long lbId; + private Long vipId; + private String lbName; + private String region; + + public UnlinkPoolsFromLbCommand(Long lbId, Long vipId, String lbName, String region) { + this.lbId = lbId; + this.vipId = vipId; + this.lbName = lbName; + this.region = region; + } + + @Override + public Answer execute(GloboNetworkAPI api) { + try { + s_logger.debug("[Unlink " + lbName + "] lbid:+ " + lbId + "unlink lb "+ lbName); + VipAPIFacade sourceFacade = new VipAPIFacade(vipId, api); + PoolAPI poolAPI = api.getPoolAPI(); + VipV3 vip = sourceFacade.getVip(); + + for (VipV3.Port port : vip.getPorts()) { + for (VipV3.Pool poolPort : port.getPools()) { + PoolV3 pool = poolAPI.getById(poolPort.getPoolId()); + pool.setId(null); + + String identifier = GloboNetworkResource.buildPoolName(region, lbName, port.getPort(), pool.getDefaultPort()); + pool.setIdentifier(identifier); + + pool.setPoolCreated(false); + poolAPI.save(pool); + poolPort.setPoolId(pool.getId()); + pool.setVips(new ArrayList()); + } + } + + VipV3API vipV3API = api.getVipV3API(); + + if (vip.getCreated()) { + vipV3API.deployUpdate(vip); + } else { + vipV3API.save(vip); + } + + return new Answer(this, true, ""); + } catch (GloboNetworkException e) { + return GloboNetworkResourceCommand.handleGloboNetworkException(this, e); + } + + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnregisterEquipmentAndIpInGloboNetworkCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnregisterEquipmentAndIpInGloboNetworkCommand.java new file mode 100644 index 000000000000..c57286b0815c --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UnregisterEquipmentAndIpInGloboNetworkCommand.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class UnregisterEquipmentAndIpInGloboNetworkCommand extends Command { + + private String nicIp; + private String vmName; + private Long environmentId; + private boolean isv6; + + @Override + public boolean executeInSequence() { + return true; + } + + public String getNicIp() { + return nicIp; + } + + public void setNicIp(String nicIp) { + this.nicIp = nicIp; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public Long getEnvironmentId() { + return environmentId; + } + + public void setEnvironmentId(Long environmentId) { + this.environmentId = environmentId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommand.java new file mode 100644 index 000000000000..d3aee29aa0ae --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommand.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 com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Answer; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.api.VipV3API; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.OptionVipV3; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.manager.Protocol; +import com.globo.globonetwork.cloudstack.resource.GloboNetworkResource; +import com.globo.globonetwork.cloudstack.resource.VipAPIFacade; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.List; +import org.apache.log4j.Logger; + +public class UpdatePoolCommand extends GloboNetworkResourceCommand { + + private static final Logger s_logger = Logger.getLogger(UpdatePoolCommand.class); + + private String expectedHealthcheck; + private String healthcheck; + private String healthcheckType; + private List poolIds; + private Integer maxConn; + + private String lbHostname; + private Protocol.L4 l4Protocol; + private Protocol.L7 l7Protocol; + private boolean redeploy; + private Long vipId; + + public UpdatePoolCommand(List poolIds) { + this.poolIds = poolIds; + } + + public UpdatePoolCommand(List poolIds, String healthcheckType, String healthcheck, String expectedHealthcheck, Integer maxConn, String lbHostname) { + this.poolIds = poolIds; + this.healthcheckType = healthcheckType; + this.healthcheck = healthcheck; + this.expectedHealthcheck = expectedHealthcheck; + this.maxConn = maxConn; + this.lbHostname = lbHostname; + } + + public String getLbHostname() { + return lbHostname; + } + + public void setLbHostname(String lbHostname) { + this.lbHostname = lbHostname; + } + + @Override + public Answer execute(GloboNetworkAPI api) { + try { + PoolAPI poolAPI = api.getPoolAPI(); + List poolsV3 = poolAPI.getByIdsV3(this.getPoolIds()); + + changePortsProtocol(api, poolsV3); + + List pools = new ArrayList(); + + for (PoolV3 poolv3 : poolsV3) { + + PoolV3.Healthcheck healthCheck = poolv3.getHealthcheck(); + healthCheck.setHealthcheck(this.getHealthcheckType(), this.getHealthcheck(), this.getExpectedHealthcheck() ); + + poolv3.setMaxconn(this.getMaxConn()); + + for (PoolV3.PoolMember poolMember : poolv3.getPoolMembers()) { + poolMember.setLimit(this.getMaxConn()); + } + } + + if ( poolsV3.size() > 0 ) { + if (poolsV3.get(0).isPoolCreated()) { + poolAPI.updateDeployAll(poolsV3); + } else { + poolAPI.updateAll(poolsV3); + } + } + + for (PoolV3 poolv3 : poolsV3) { + pools.add(GloboNetworkResource.poolV3FromNetworkApi(poolv3)); + } + + GloboNetworkPoolResponse answer = new GloboNetworkPoolResponse(this, pools, true, ""); + + return answer; + } catch (GloboNetworkException e) { + return GloboNetworkResourceCommand.handleGloboNetworkException(this, e); + } catch (Exception e) { + s_logger.error("Generic error accessing GloboNetwork while update pool", e); + return new Answer(this, false, e.getMessage()); + } + } + + private void changePortsProtocol(GloboNetworkAPI api, List poolsV3) throws GloboNetworkException { + if (getL4Protocol() == null && getL7Protocol() == null) { + s_logger.debug("No changes to do in l4 and l7 protocols"); + return; + } + + VipV3API vipV3API = api.getVipV3API(); + + + VipAPIFacade vipAPIFacade = new VipAPIFacade(this.vipId, api); + VipV3 vip = vipAPIFacade.getVip(); + + OptionVipV3 l4Protocol = vipAPIFacade.getProtocolOption(vip.getEnvironmentVipId(), "l4_protocol", getL4Protocol().getNetworkApiOptionValue()); + OptionVipV3 l7Protocol = vipAPIFacade.getProtocolOption(vip.getEnvironmentVipId(), "l7_protocol", getL7Protocol().getNetworkApiOptionValue()); + + + boolean hasPortToChange = false; + for (VipV3.Port port : vip.getPorts()) { + + PoolV3 pool = getPoolToChangePort(port.getPools(), poolsV3); + + if (pool != null){ + VipV3.PortOptions options = port.getOptions(); + + + if (!options.getL4ProtocolId().equals(l4Protocol.getId())) { + options.setL4ProtocolId(l4Protocol.getId()); + hasPortToChange = true; + } + + if (!options.getL7ProtocolId().equals(l7Protocol.getId())) { + options.setL7ProtocolId(l7Protocol.getId()); + hasPortToChange = true; + } + + } + } + + + if (hasPortToChange && !redeploy) { + throw new CloudRuntimeException("Change ports require undeploy and deploy Vip. Can not change ports protocol if parameter 'redeploy' is false."); + } + + if (hasPortToChange) { + if (vip.getCreated()) { + vipV3API.undeploy(vip.getId()); + } + vipV3API.save(vip); + vipV3API.deploy(vip.getId()); + } + + } + + private PoolV3 getPoolToChangePort(List pools, List poolV3s) { + for (PoolV3 poolV3 : poolV3s){ + for(VipV3.Pool pool : pools) { + if (poolV3.getId().equals(pool.getPoolId())){ + return poolV3; + } + } + } + + return null; + } + + public String getExpectedHealthcheck() { + return expectedHealthcheck; + } + + public void setExpectedHealthcheck(String expectedHealthcheck) { + this.expectedHealthcheck = expectedHealthcheck; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public String getHealthcheckType() { + return healthcheckType; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public List getPoolIds() { + return poolIds; + } + + public void setPoolIds(List poolIds) { + this.poolIds = poolIds; + } + + public List getPoolId() { + return poolIds; + } + + public void setPoolId(List poolIds) { + this.poolIds = poolIds; + } + + public Integer getMaxConn() { return maxConn; } + + public void setMaxConn(Integer maxConn) { this.maxConn = maxConn; } + + public void setL4Protocol(Protocol.L4 l4Protocol) { + this.l4Protocol = l4Protocol; + } + + public void setL7Protocol(Protocol.L7 l7Protocol) { + this.l7Protocol = l7Protocol; + } + + public void setRedeploy(boolean redeploy) { + this.redeploy = redeploy; + } + + public Protocol.L4 getL4Protocol() { + return l4Protocol; + } + + public Protocol.L7 getL7Protocol() { + return l7Protocol; + } + + public boolean isRedeploy() { + return redeploy; + } + + public Long getVipId() { + return vipId; + } + + public void setVipId(Long vipId) { + this.vipId = vipId; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ValidateVipUpdateCommand.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ValidateVipUpdateCommand.java new file mode 100644 index 000000000000..bf0316ae1c7c --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/commands/ValidateVipUpdateCommand.java @@ -0,0 +1,35 @@ +package com.globo.globonetwork.cloudstack.commands; + +import com.cloud.agent.api.Command; + +public class ValidateVipUpdateCommand extends Command { + + private Long vipId; + + private String name; + + private String ip; + + public ValidateVipUpdateCommand(Long vipId, String name, String ip) { + this.vipId = vipId; + this.name = name; + this.ip = ip; + } + + public String getName() { + return name; + } + + public String getIp() { + return ip; + } + + public Long getVipId() { + return vipId; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDao.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDao.java new file mode 100644 index 000000000000..aaf419791acd --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDao.java @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; + +public interface GloboNetworkEnvironmentDao extends GenericDao { + + List listByPhysicalNetworkId(long physicalNetworkId); + + GloboNetworkEnvironmentVO findByPhysicalNetworkIdAndEnvironmentId(long physicalNetworkId, long environmentId); + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDaoImpl.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDaoImpl.java new file mode 100644 index 000000000000..e7bcf1ac8428 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkEnvironmentDaoImpl.java @@ -0,0 +1,66 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; + +@Component +@DB +public class GloboNetworkEnvironmentDaoImpl extends GenericDaoBase implements GloboNetworkEnvironmentDao { + + final SearchBuilder physicalNetworkIdSearch; + final SearchBuilder physicalNetworkIdAndEnvironmentIdSearch; + + protected GloboNetworkEnvironmentDaoImpl() { + super(); + + physicalNetworkIdSearch = createSearchBuilder(); + physicalNetworkIdSearch.and("physical_network_id", physicalNetworkIdSearch.entity().getPhysicalNetworkId(), Op.EQ); + physicalNetworkIdSearch.done(); + + physicalNetworkIdAndEnvironmentIdSearch = createSearchBuilder(); + physicalNetworkIdAndEnvironmentIdSearch.and("physical_network_id", physicalNetworkIdAndEnvironmentIdSearch.entity().getPhysicalNetworkId(), Op.EQ); + physicalNetworkIdAndEnvironmentIdSearch.and("napi_environment_id", physicalNetworkIdAndEnvironmentIdSearch.entity().getGloboNetworkEnvironmentId(), Op.EQ); + physicalNetworkIdAndEnvironmentIdSearch.done(); + + } + + @Override + public List listByPhysicalNetworkId(long physicalNetworkId) { + SearchCriteria sc = physicalNetworkIdSearch.create(); + sc.setParameters("physical_network_id", physicalNetworkId); + return listBy(sc); + } + + @Override + public GloboNetworkEnvironmentVO findByPhysicalNetworkIdAndEnvironmentId(long physicalNetworkId, long environmentId) { + SearchCriteria sc = physicalNetworkIdAndEnvironmentIdSearch.create(); + sc.setParameters("physical_network_id", physicalNetworkId); + sc.setParameters("napi_environment_id", environmentId); + return findOneBy(sc); + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDao.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDao.java new file mode 100644 index 000000000000..41d0a9df76b6 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDao.java @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; + +public interface GloboNetworkIpDetailDao extends GenericDao { + + GloboNetworkIpDetailVO findByIp(long ipId); + + List listByLBEnvironmentRef(long lbEnvironmentRefId); + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDaoImpl.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDaoImpl.java new file mode 100644 index 000000000000..76ee6e0ce0a3 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkIpDetailDaoImpl.java @@ -0,0 +1,61 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; + +@Component +@DB +public class GloboNetworkIpDetailDaoImpl extends GenericDaoBase implements GloboNetworkIpDetailDao { + + final SearchBuilder byIpAddress; + final SearchBuilder byLBEnvironmentRef; + + protected GloboNetworkIpDetailDaoImpl() { + byIpAddress = createSearchBuilder(); + byIpAddress.and("ip_address_id", byIpAddress.entity().getIpAddressId(), Op.EQ); + byIpAddress.done(); + + byLBEnvironmentRef = createSearchBuilder(); + byLBEnvironmentRef.and("globonetwork_lbenvironment_ref_id", byLBEnvironmentRef.entity().getGloboNetworkEnvironmentRefId(), Op.EQ); + byLBEnvironmentRef.done(); + } + + @Override + public GloboNetworkIpDetailVO findByIp(long ipAddressId) { + SearchCriteria sc = byIpAddress.create(); + sc.setParameters("ip_address_id", ipAddressId); + return findOneBy(sc); + } + + @Override + public List listByLBEnvironmentRef(long lbEnvironmentRefId) { + SearchCriteria sc = byLBEnvironmentRef.create(); + sc.setParameters("globonetwork_lbenvironment_ref_id", lbEnvironmentRefId); + return listBy(sc); + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAO.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAO.java new file mode 100644 index 000000000000..caad159f480d --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAO.java @@ -0,0 +1,30 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; + +public interface GloboNetworkLoadBalancerEnvironmentDAO extends GenericDao { + + List listByEnvironmentRefId(long globoNetworkEnvironmentRefId); + + GloboNetworkLoadBalancerEnvironment findByEnvironmentRefAndLBNetwork(long globoNetworkEnvironmentRefId, long globoNetworkLBNetworkId); + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAOImpl.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAOImpl.java new file mode 100644 index 000000000000..aa53b18e08b4 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkLoadBalancerEnvironmentDAOImpl.java @@ -0,0 +1,65 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; + +@Component +@DB +public class GloboNetworkLoadBalancerEnvironmentDAOImpl extends GenericDaoBase implements GloboNetworkLoadBalancerEnvironmentDAO { + + final SearchBuilder byNetworkEnvironmentRefId; + + final SearchBuilder byNetworkEnvironmentRefIdAndLBNetworkId; + + protected GloboNetworkLoadBalancerEnvironmentDAOImpl() { + byNetworkEnvironmentRefId = createSearchBuilder(); + byNetworkEnvironmentRefId.and("globonetwork_environment_ref_id", byNetworkEnvironmentRefId.entity().getGloboNetworkEnvironmentRefId(), Op.EQ); + byNetworkEnvironmentRefId.done(); + + byNetworkEnvironmentRefIdAndLBNetworkId = createSearchBuilder(); + byNetworkEnvironmentRefIdAndLBNetworkId.and("globonetwork_environment_ref_id", byNetworkEnvironmentRefIdAndLBNetworkId.entity().getGloboNetworkEnvironmentRefId(), Op.EQ); + byNetworkEnvironmentRefIdAndLBNetworkId.and("globonetwork_lb_network_id", byNetworkEnvironmentRefIdAndLBNetworkId.entity().getGloboNetworkLoadBalancerEnvironmentId(), + Op.EQ); + byNetworkEnvironmentRefIdAndLBNetworkId.done(); + } + + @Override + public List listByEnvironmentRefId(long globoNetworkEnvironmentRefId) { + SearchCriteria sc = byNetworkEnvironmentRefId.create(); + sc.setParameters("globonetwork_environment_ref_id", globoNetworkEnvironmentRefId); + return listBy(sc); + } + + @Override + public GloboNetworkLoadBalancerEnvironment findByEnvironmentRefAndLBNetwork(long globoNetworkEnvironmentRefId, long globoNetworkLBNetworkId) { + SearchCriteria sc = byNetworkEnvironmentRefIdAndLBNetworkId.create(); + sc.setParameters("globonetwork_environment_ref_id", globoNetworkEnvironmentRefId); + sc.setParameters("globonetwork_lb_network_id", globoNetworkLBNetworkId); + return findOneBy(sc); + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDao.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDao.java new file mode 100644 index 000000000000..ac84b1029a29 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDao.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.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.globo.globonetwork.cloudstack.GloboNetworkNetworkVO; + +public interface GloboNetworkNetworkDao extends GenericDao { + + GloboNetworkNetworkVO findByNetworkId(long networkId); + + List listByEnvironmentId(long napiEnvironmentId); +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDaoImpl.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDaoImpl.java new file mode 100644 index 000000000000..d696963f7303 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkNetworkDaoImpl.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 com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globonetwork.cloudstack.GloboNetworkNetworkVO; + +@Component +@DB +public class GloboNetworkNetworkDaoImpl extends GenericDaoBase implements GloboNetworkNetworkDao { + + final SearchBuilder networkIdSearch; + final SearchBuilder environmentIdSearch; + + protected GloboNetworkNetworkDaoImpl() { + super(); + + networkIdSearch = createSearchBuilder(); + networkIdSearch.and("network_id", networkIdSearch.entity().getNetworkId(), Op.EQ); + networkIdSearch.done(); + + environmentIdSearch = createSearchBuilder(); + environmentIdSearch.and("napi_environment_id", environmentIdSearch.entity().getGloboNetworkEnvironmentId(), Op.EQ); + environmentIdSearch.done(); + } + + @Override + public GloboNetworkNetworkVO findByNetworkId(long networkId) { + SearchCriteria sc = networkIdSearch.create(); + sc.setParameters("network_id", networkId); + return findOneBy(sc); + } + + @Override + public List listByEnvironmentId(long napiEnvironmentId) { + SearchCriteria sc = environmentIdSearch.create(); + sc.setParameters("napi_environment_id", napiEnvironmentId); + return listBy(sc); + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDao.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDao.java new file mode 100644 index 000000000000..f8ad43eeda17 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDao.java @@ -0,0 +1,36 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import com.globo.globonetwork.cloudstack.GloboNetworkVipAccVO; + +public interface GloboNetworkVipAccDao extends GenericDao { + + GloboNetworkVipAccVO findGloboNetworkVipAcc(long vipId, long accountId, long networkId); + + List listByNetworks(List networkIdList); + + GloboNetworkVipAccVO findGloboNetworkVip(long vipId, long networkId); + + List findByNetwork(long networkId); + + List findByVipId(long vipId); + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDaoImpl.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDaoImpl.java new file mode 100644 index 000000000000..b17aeb6be22c --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/dao/GloboNetworkVipAccDaoImpl.java @@ -0,0 +1,108 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.dao; + +import java.util.List; + +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +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.globo.globonetwork.cloudstack.GloboNetworkVipAccVO; + +@Component +@DB +public class GloboNetworkVipAccDaoImpl extends GenericDaoBase implements GloboNetworkVipAccDao { + + final SearchBuilder allParamsSearch; + + final SearchBuilder networkSearch; + + final SearchBuilder byNetwork; + + final SearchBuilder byVip; + + final SearchBuilder byNetworkAndVip; + + protected GloboNetworkVipAccDaoImpl() { + super(); + + allParamsSearch = createSearchBuilder(); + allParamsSearch.and("napi_vip_id", allParamsSearch.entity().getGloboNetworkVipId(), Op.EQ); + allParamsSearch.and("account_id", allParamsSearch.entity().getAccountId(), Op.EQ); + allParamsSearch.and("network_id", allParamsSearch.entity().getNetworkId(), Op.EQ); + allParamsSearch.done(); + + networkSearch = createSearchBuilder(); + networkSearch.and("network_id", networkSearch.entity().getNetworkId(), Op.IN); + networkSearch.done(); + + byNetwork = createSearchBuilder(); + byNetwork.and("network_id", byNetwork.entity().getNetworkId(), Op.EQ); + byNetwork.done(); + + byVip = createSearchBuilder(); + byVip.and("napi_vip_id", byVip.entity().getGloboNetworkVipId(), Op.EQ); + byVip.done(); + + byNetworkAndVip = createSearchBuilder(); + byNetworkAndVip.and("network_id", byNetworkAndVip.entity().getNetworkId(), Op.EQ); + byNetworkAndVip.and("napi_vip_id", byNetworkAndVip.entity().getGloboNetworkVipId(), Op.EQ); + byNetworkAndVip.done(); + } + + @Override + public GloboNetworkVipAccVO findGloboNetworkVipAcc(long vipId, long accountId, long networkId) { + SearchCriteria sc = allParamsSearch.create(); + sc.setParameters("napi_vip_id", vipId); + sc.setParameters("account_id", accountId); + sc.setParameters("network_id", networkId); + return findOneBy(sc); + } + + public List listByNetworks(List networkIdList) { + SearchCriteria sc = networkSearch.create(); + sc.setParameters("network_id", networkIdList.toArray(new Object[networkIdList.size()])); + return listBy(sc); + } + + @Override + public List findByNetwork(long networkId) { + SearchCriteria sc = byNetwork.create(); + sc.setParameters("network_id", networkId); + return listBy(sc); + } + + @Override + public GloboNetworkVipAccVO findGloboNetworkVip(long vipId, long networkId) { + SearchCriteria sc = byNetworkAndVip.create(); + sc.setParameters("napi_vip_id", vipId); + sc.setParameters("network_id", networkId); + return findOneBy(sc); + } + + @Override + public List findByVipId(long vipId) { + SearchCriteria sc = byVip.create(); + sc.setParameters("napi_vip_id", vipId); + return listBy(sc); + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/element/GloboNetworkElement.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/element/GloboNetworkElement.java new file mode 100644 index 000000000000..103f9ce9dfd6 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/element/GloboNetworkElement.java @@ -0,0 +1,308 @@ +package com.globo.globonetwork.cloudstack.element; + +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.cloud.network.as.AutoScaleCounter; +import com.cloud.network.dao.NetworkDao; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +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.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.ExternalLoadBalancerDeviceManager; +import com.cloud.network.ExternalLoadBalancerDeviceManagerImpl; +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.Networks.TrafficType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.LoadBalancingServiceProvider; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRulesManager; +import com.cloud.network.rules.LbStickinessMethod; +import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType; +import com.cloud.network.rules.LoadBalancerContainer; +import com.cloud.offering.NetworkOffering; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachineProfile; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import com.globo.globonetwork.cloudstack.resource.GloboNetworkResource; +import com.google.gson.Gson; + +@Component +public class GloboNetworkElement extends ExternalLoadBalancerDeviceManagerImpl implements LoadBalancingServiceProvider, IpDeployer, ExternalLoadBalancerDeviceManager { + private static final Logger s_logger = Logger.getLogger(GloboNetworkElement.class); + + private static final Map> capabilities = setCapabilities(); + + @Inject + DataCenterDao _dcDao; + @Inject + NetworkModel _networkManager; + @Inject + LoadBalancingRulesManager _lbManager; + @Inject + NetworkServiceMapDao _ntwkSrvcDao; + @Inject + GloboNetworkService _globoNetworkService; + @Inject + ResourceManager _resourceMgr; + @Inject + HostDao _hostDao; + + @Inject + NetworkDao networkDao; + + @Inject + LoadBalancingRulesManager _lbMgr; + + @Inject + PhysicalNetworkDao _physicalNetworkDao; + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + // Set capabilities for LB service + Map lbCapabilities = new HashMap(); + lbCapabilities.put(Capability.SupportedLBAlgorithms, "leastconn, roundrobin"); + lbCapabilities.put(Capability.SupportedLBIsolation, "dedicated, shared"); + lbCapabilities.put(Capability.SupportedProtocols, "tcp,udp,http"); + + // Specifies that load balancing rules can only be made with public IPs that aren't source NAT IPs + lbCapabilities.put(Capability.LoadBalancingSupportedIps, "additional"); + + // Support inline mode with firewall + lbCapabilities.put(Capability.InlineMode, "true"); + + //support only for public lb + lbCapabilities.put(Capability.LbSchemes, LoadBalancerContainer.Scheme.Public.toString()); + + LbStickinessMethod method; + List methodList = new ArrayList(); + method = new LbStickinessMethod(new StickinessMethodType("Cookie"), "This is cookie based sticky method"); + methodList.add(method); + method = new LbStickinessMethod(new StickinessMethodType("Source-ip"), "This is source based sticky method"); + methodList.add(method); + method = new LbStickinessMethod(new StickinessMethodType("Source-ip with persistence between ports"), "This is source based sticky method with stickiness between ports"); + methodList.add(method); + method = new LbStickinessMethod(new StickinessMethodType("Priority Failover"), "This is a priority failover stickiness"); + methodList.add(method); + + Gson gson = new Gson(); + String stickyMethodList = gson.toJson(methodList); + lbCapabilities.put(Capability.SupportedStickinessMethods, stickyMethodList); + + // Add auto scale capability to load balancer + AutoScaleCounter.AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounter.AutoScaleCounterType("cpu_used"); + AutoScaleCounter.AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounter.AutoScaleCounterType("memory_used"); + AutoScaleCounter.AutoScaleCounterType AutoScaleCounterConnections = new AutoScaleCounter.AutoScaleCounterType("active_connections"); + AutoScaleCounter.AutoScaleCounterType AutoScaleCounterMemoryNoCache = new AutoScaleCounter.AutoScaleCounterType("memory_free"); + + + List counterList = new ArrayList(); + counterList.add(new AutoScaleCounter(AutoScaleCounterCpu)); + counterList.add(new AutoScaleCounter(AutoScaleCounterMemory)); + counterList.add(new AutoScaleCounter(AutoScaleCounterConnections)); + counterList.add(new AutoScaleCounter(AutoScaleCounterMemoryNoCache)); + String autoScaleCounterList = gson.toJson(counterList); + lbCapabilities.put(Capability.AutoScaleCounters, autoScaleCounterList); + + // Healthcheck + lbCapabilities.put(Capability.HealthCheckPolicy, "true"); + + Map> capabilities = new HashMap>(); + capabilities.put(Service.Lb, lbCapabilities); + return capabilities; + } + + @Override + public Provider getProvider() { + return Provider.GloboNetwork; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + return true; + } + + protected boolean canHandle(Network network, Service service) { + s_logger.trace("Checking if GloboNetwork can handle service " + service.getName() + " on network " + network.getDisplayText()); + + DataCenter zone = _dcDao.findById(network.getDataCenterId()); + boolean handleInAdvanceZone = (zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Shared && network.getTrafficType() == TrafficType.Guest); + + if (!handleInAdvanceZone) { + s_logger.trace("Not handling network with Type " + network.getGuestType() + " and traffic type " + network.getTrafficType() + " in zone of type " + + zone.getNetworkType()); + return false; + } + + return (_networkManager.isProviderForNetwork(getProvider(), network.getId()) && _ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), service, + Network.Provider.GloboNetwork)); + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + return true; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + if (!canHandle(network, Service.Lb)) { + return false; + } + + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return _globoNetworkService.canEnable(provider.getPhysicalNetworkId()); + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + PhysicalNetwork pNtwk = _physicalNetworkDao.findById(provider.getPhysicalNetworkId()); + Host host = _hostDao.findByTypeNameAndZoneId(pNtwk.getDataCenterId(), Provider.GloboNetwork.getName(), Type.L2Networking); + if (host != null) { + _resourceMgr.deleteHost(host.getId(), true, false); + } + return true; + } + + @Override + public IpDeployer getIpDeployer(Network network) { + return this; + } + + @Override + public boolean applyLBRules(Network config, List rules) throws ResourceUnavailableException { + boolean returnValue = true; + boolean result = false; + for (LoadBalancingRule loadBalancingRule : rules) { + result = _globoNetworkService.applyLbRuleInGloboNetwork(config, loadBalancingRule); + // Make sure the method returns false if there is at least one false return + returnValue = returnValue && result; + } + return returnValue; + } + + @Override + public boolean validateLBRule(Network network, LoadBalancingRule rule) { + return _globoNetworkService.validateLBRule(network, rule); + } + + @Override + public boolean applyIps(Network network, List ipAddress, Set services) throws ResourceUnavailableException { + return true; + } + + @Override + public List updateHealthChecks(Network network, List lbrules) { + return null; + } + + @Override + public boolean handlesOnlyRulesInTransitionState() { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return true; + } + + @Override + public boolean verifyServicesCombination(Set services) { + return true; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, final StartupCommand[] startup, ServerResource resource, Map details, List hostTags) { + if (!(startup[0] instanceof StartupCommand && resource instanceof GloboNetworkResource)) { + return null; + } + host.setType(Host.Type.L2Networking); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + if (!host.getName().equals(Provider.GloboNetwork.getName())) { + return null; + } + return new DeleteHostAnswer(true); + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + return null; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/exception/CloudstackGloboNetworkException.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/exception/CloudstackGloboNetworkException.java new file mode 100644 index 000000000000..c1e216a6af9b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/exception/CloudstackGloboNetworkException.java @@ -0,0 +1,54 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.exception; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class CloudstackGloboNetworkException extends CloudRuntimeException { + + private static final long serialVersionUID = 678159764759471937L; + + private int napiCode; + private String napiDescription; + private String context; + + public CloudstackGloboNetworkException(int napiCode, String napiDescription) { + super(napiCode + " - " + napiDescription); + this.napiCode = napiCode; + this.napiDescription = napiDescription; + } + public CloudstackGloboNetworkException(int napiCode, String napiDescription, String context) { + super(napiCode + " - " + napiDescription); + this.napiCode = napiCode; + this.napiDescription = napiDescription; + this.context = context; + } + + public CloudstackGloboNetworkException(String message) { + super(message); + } + + public int getNapiCode() { + return napiCode; + } + + public String getNapiDescription() { + return napiDescription; + } + + public String getContext() { return this.context; } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/guru/GloboNetworkGuru.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/guru/GloboNetworkGuru.java new file mode 100644 index 000000000000..a1e532db2127 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/guru/GloboNetworkGuru.java @@ -0,0 +1,257 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.guru; + +import java.util.List; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.State; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.Networks.Mode; +import com.cloud.network.NetworkProfile; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetwork.IsolationMethod; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.guru.GuestNetworkGuru; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackWithException; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachineProfile; +import com.globo.globonetwork.cloudstack.GloboNetworkVipAccVO; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkVipAccDao; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; + +@Component +public class GloboNetworkGuru extends GuestNetworkGuru { + private static final Logger s_logger = Logger.getLogger(GloboNetworkGuru.class); + + // by default traffic type is TrafficType.Guest + + @Inject + GloboNetworkService _globoNetworkService; + @Inject + VpcVirtualNetworkApplianceManager _routerMgr; + @Inject + AccountManager _accountMgr; + @Inject + GloboNetworkVipAccDao _globoNetworkVipDao; + + protected NetworkType _networkType = NetworkType.Advanced; + + public GloboNetworkGuru() { + _isolationMethods = new IsolationMethod[] {new IsolationMethod("VLAN")}; + setName("GloboNetworkGuru"); + } + + public boolean isMyNetworkType(NetworkType networkType) { + return this._networkType.equals(networkType); + } + + public NetworkType getSupportedNetworkType() { + return this._networkType; + } + + @Override + protected boolean canHandle(NetworkOffering offering, NetworkType networkType, PhysicalNetwork physicalNetwork) { + + if (isMyNetworkType(networkType) && offering.getGuestType() == Network.GuestType.Shared && isMyTrafficType(offering.getTrafficType()) + && isMyIsolationMethod(physicalNetwork)) { + + if (_networkModel.isProviderEnabledInZone(physicalNetwork.getDataCenterId(), Provider.GloboNetwork.getName())) { + s_logger.debug("GloboNetwork can handle this network" + " with traffic type " + offering.getTrafficType() + " guest type " + Network.GuestType.Shared + + " network type " + networkType + " and physical network " + physicalNetwork); + return true; + } else { + s_logger.debug("GloboNetwork is not enabled for zone" + physicalNetwork.getDataCenterId()); + return false; + } + } else { + s_logger.debug("We only take care of " + getSupportedTrafficType() + " " + Network.GuestType.Shared + " networks" + " with isolation method " + getIsolationMethods() + + " and zone of type " + getSupportedNetworkType()); + return false; + } + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + DataCenter dc = _dcDao.findById(plan.getDataCenterId()); + PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); + + if (!canHandle(offering, dc.getNetworkType(), physnet)) { + return null; + } + + NetworkVO config = new NetworkVO(offering.getTrafficType(), Mode.Dhcp, BroadcastDomainType.Vlan, offering.getId(), State.Allocated, plan.getDataCenterId(), plan.getPhysicalNetworkId(), false); + + if (userSpecified != null) { + if ((userSpecified.getCidr() == null && userSpecified.getGateway() != null) || (userSpecified.getCidr() != null && userSpecified.getGateway() == null)) { + throw new InvalidParameterValueException("cidr and gateway must be specified together."); + } + + if ((userSpecified.getIp6Cidr() == null && userSpecified.getIp6Gateway() != null) || + (userSpecified.getIp6Cidr() != null && userSpecified.getIp6Gateway() == null)) { + throw new InvalidParameterValueException("cidrv6 and gatewayv6 must be specified together."); + } + + if (userSpecified.getCidr() != null) { + config.setCidr(userSpecified.getCidr()); + config.setGateway(userSpecified.getGateway()); + } + + if (userSpecified.getIp6Cidr() != null) { + config.setIp6Cidr(userSpecified.getIp6Cidr()); + config.setIp6Gateway(userSpecified.getIp6Gateway()); + } + + if (userSpecified.getBroadcastUri() != null) { + config.setBroadcastUri(userSpecified.getBroadcastUri()); + } + + if (userSpecified.getBroadcastDomainType() != null) { + config.setBroadcastDomainType(userSpecified.getBroadcastDomainType()); + } + } + + return config; + } + + @Override + public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapacityException { + s_logger.debug("Creating network " + network.getName() + " in equipment using GloboNetwork"); + + try { + _globoNetworkService.implementNetwork(network); + } catch (ConfigurationException e) { + throw new CloudRuntimeException("Unable to activate network " + network, e); + } + return super.implement(network, offering, dest, context); + } + + @Override + public void updateNicProfile(NicProfile profile, Network network) { + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + if (profile != null) { + profile.setIPv4Dns1(dc.getDns1()); + profile.setIPv4Dns2(dc.getDns2()); + profile.setIPv6Dns1(dc.getIp6Dns1()); + profile.setIPv6Dns2(dc.getIp6Dns2()); + } + } + + @Override + public NicProfile allocate(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, + InsufficientAddressCapacityException { + + NicProfile nicProf; + try { + nicProf = Transaction.execute(new TransactionCallbackWithException() { + + @Override + public NicProfile doInTransaction(TransactionStatus status) throws InsufficientNetworkCapacityException { + NicProfile nicProf = GloboNetworkGuru.super.allocate(network, nic, vm); + s_logger.debug("Registering NIC " + nic.toString() + " from VM " + vm.toString() + " in GloboNetwork"); + _globoNetworkService.registerNicInGloboNetwork(nic, vm, network); + return nicProf; + } + }); + } catch (InsufficientAddressCapacityException e) { + throw e; + } catch (InsufficientNetworkCapacityException e) { + s_logger.error(e.getMessage(), e); + throw new InsufficientVirtualNetworkCapacityException(e.getMessage(), e.getScope(), e.getId()); + } + return nicProf; + } + + @Override + public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + s_logger.debug("Asking GuestNetworkGuru to reserve nic " + nic.toString() + " for network " + network.getName()); + + super.reserve(nic, network, vm, dest, context); + + _globoNetworkService.validateNic(nic, vm, network); + + } + + @Override + public void deallocate(Network network, NicProfile nic, VirtualMachineProfile vm) { + s_logger.debug("Asking GuestNetworkGuru to deallocate NIC " + nic.toString() + " from VM " + vm.getInstanceName()); + + // Remove vm from any load balancer prior to deallocating it + if (_globoNetworkService.removeVmFromLoadBalancer(vm)) { + _globoNetworkService.unregisterNicInGloboNetwork(nic, vm); + super.deallocate(network, nic, vm); + } else { + throw new CloudRuntimeException("Could not remove VM from load balancers. Please remove it from any load balancer before continuing"); + } + } + + @Override + public void shutdown(NetworkProfile profile, NetworkOffering offering) { + List vips = _globoNetworkVipDao.findByNetwork(profile.getId()); + if (vips != null && !vips.isEmpty()) { + throw new CloudRuntimeException("There is VIPs related to this network. Network destroyed will be aborted. Delete VIP before."); + } + + try { + s_logger.debug("Removing networks from GloboNetwork"); + _globoNetworkService.removeNetworkFromGloboNetwork(profile); + + s_logger.debug("Asking GuestNetworkGuru to shutdown network " + profile.getName()); + // never call super.shutdown because it clear broadcastUri, and sometimes this + // make same networks without vlan + //super.shutdown(profile, offering); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException(e); + } + } + + @Override + public boolean trash(Network network, NetworkOffering offering) { + + s_logger.debug("Deallocating VLAN networks from GloboNetwork"); + _globoNetworkService.deallocateVlanFromGloboNetwork(network); + + s_logger.debug("VLAN networks released. Passing on to GuestNetworkGuru to trash network " + network.getName()); + return super.trash(network, offering); + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManager.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManager.java new file mode 100644 index 000000000000..aa27438afc16 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManager.java @@ -0,0 +1,311 @@ +package com.globo.globonetwork.cloudstack.manager; + +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerPortMapDao; +import com.cloud.network.dao.LoadBalancerPortMapVO; +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.LoadBalancerVMMapVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRulesManager; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.region.ha.GlobalLoadBalancingRulesService; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; +import com.globo.globonetwork.cloudstack.api.loadbalancer.LinkGloboLoadBalancerCmd; +import com.globo.globonetwork.cloudstack.api.loadbalancer.ListGloboLinkableLoadBalancersCmd; +import com.globo.globonetwork.cloudstack.api.loadbalancer.UnlinkGloboLoadBalancerCmd; +import com.globo.globonetwork.cloudstack.commands.LinkParentLbPoolsInChildLbCommand; +import com.globo.globonetwork.cloudstack.commands.UnlinkPoolsFromLbCommand; +import org.apache.cloudstack.api.command.user.loadbalancer.AssignToLoadBalancerRuleCmd; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Component +public class GloboLoadBalancerManager implements GloboLoadBalancerService, PluggableService { + + private static final Logger s_logger = Logger.getLogger(GloboLoadBalancerManager.class); + + @Inject + protected GloboNetworkService globoNetworkSvc; + + @Inject + protected LoadBalancingRulesService lbService; + + @Inject + LoadBalancingRulesManager _lbMgr; + + @Inject + protected GloboResourceConfigurationDao resourceConfigDao; + + @Inject + protected LoadBalancerVMMapDao _lb2VmMapDao; + + @Inject + public GlobalLoadBalancingRulesService _gslbService; + + + @Inject + LoadBalancerPortMapDao _lbPortMapDao; + @Inject + NetworkDao networkDao; + + @Inject + LoadBalancerDao lbDao; + + @Inject + FirewallRulesDao frDao; + + @Inject + GloboResourceConfigurationDao configDao; + + @Override + @DB + public LoadBalancer linkLoadBalancer(Long childLbid, Long parentLbid) { + LoadBalancer childLb = checkIfLBAlreadyIsLinked(childLbid); + checkIfSourceLBHasVms(childLb); + + LoadBalancer parentLb = checkIfLBAlreadyIsLinked(parentLbid); + + if (isLbDsr(childLb)) { + throw new CloudRuntimeException("Load balancer " + childLb.getName() + " is dsr! Can not link lb dsr!"); + } + if (isLbDsr(parentLb)) { + throw new CloudRuntimeException("Load balancer " + parentLb.getName() + " is dsr! Can not be linked!"); + } + + LoadBalancingRule lbRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) childLb); + LoadBalancingRule parentRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) parentLb); + + removeUnnecessaryNetworks(lbRule, parentRule); + lbRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) childLb); + copyNetworksFromTarget(lbRule, parentRule); + + + LinkParentLbPoolsInChildLbCommand command = new LinkParentLbPoolsInChildLbCommand(); + + Long sourceVipId = getVipIdApplyLbIfNeed(childLb); + command.setChildLb(childLb.getUuid(), childLb.getName(), sourceVipId); + + Long targetVipId = getVipIdApplyLbIfNeed(parentLb); + command.setParentLb(parentLb.getUuid(), parentLb.getName(), targetVipId); + + NetworkVO network = networkDao.findById(lbRule.getNetworkId()); + globoNetworkSvc.callCommand(command, network.getDataCenterId()); + + registerLink(childLb, parentLb); + + + copyPorts(parentRule, (LoadBalancerVO)childLb); + + return childLb; + } + + private boolean isLbDsr(LoadBalancer lb) { + List configs = configDao.getConfiguration(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.dsr); + + if (configs.size() > 0) { + return configs.get(0).getBooleanValue(); + } + return false; + } + + private void copyPorts(LoadBalancingRule fromLb, LoadBalancerVO toLb) { + List loadBalancerPortMaps = _lbPortMapDao.listByLoadBalancerId(toLb.getId()); + for (LoadBalancerPortMapVO lBPortMap : loadBalancerPortMaps) { + _lbPortMapDao.remove(lBPortMap.getId()); + } + + loadBalancerPortMaps = _lbPortMapDao.listByLoadBalancerId(fromLb.getId()); + for (LoadBalancerPortMapVO lBPortMap : loadBalancerPortMaps) { + + _lbPortMapDao.persist(new LoadBalancerPortMapVO(toLb.getId(), lBPortMap.getPublicPort(), lBPortMap.getPrivatePort())); + } + + toLb.setDefaultPortEnd(fromLb.getDefaultPortEnd()); + toLb.setDefaultPortStart(fromLb.getDefaultPortStart()); + + lbDao.update(toLb.getId(), toLb); + + FirewallRuleVO firewall = frDao.findById(toLb.getId()); + FirewallRuleVO fromFirewall = frDao.findById(fromLb.getId()); + firewall.setSourcePortEnd(fromFirewall.getSourcePortEnd()); + firewall.setSourcePortStart(fromFirewall.getSourcePortStart()); + + frDao.update(firewall.getId(), firewall); + + } + + public LoadBalancer copyVmsAndNetworks(Long fromLbId, Long toLbid) { + LoadBalancer lb = checkIfLBAlreadyIsLinked(toLbid); + checkIfSourceLBHasVms(lb); + + LoadBalancer fromLb = checkIfLBAlreadyIsLinked(fromLbId); + + LoadBalancingRule lbRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) lb); + LoadBalancingRule targetRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) fromLb); + + removeUnnecessaryNetworks(lbRule, targetRule); + lbRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) lb); + copyNetworksFromTarget(lbRule, targetRule); + + lbRule = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) lb); + copyVmsFromTarget(lbRule, targetRule); + + return lb; + } + + protected void copyVmsFromTarget(LoadBalancingRule lbRule, LoadBalancingRule targetLb) { + + List vmIds = new ArrayList(); + + for (LoadBalancingRule.LbDestination lbDestination : targetLb.getDestinations()) { + vmIds.add(lbDestination.getInstanceId()); + + } + + AssignToLoadBalancerRuleCmd cmd = new AssignToLoadBalancerRuleCmd(); + cmd.setVirtualMachineIds(vmIds); + Map> vmIdIpListMap = cmd.getVmIdIpListMap(); + + lbService.assignToLoadBalancer(lbRule.getId(), vmIds, vmIdIpListMap); + } + + private void removeUnnecessaryNetworks(LoadBalancingRule lbRule, LoadBalancingRule targetRule) { + + List lbRuleNetworks = lbRule.getAdditionalNetworks(); //ignore main network, it can not be removed + List targetNetworks = targetRule.getAllNetworks(); + + lbRuleNetworks.removeAll(targetNetworks); + + if (!lbRuleNetworks.isEmpty()) { + lbService.removeNetworksFromLoadBalancer(lbRule.getId(), lbRuleNetworks); + } + + } + + private void copyNetworksFromTarget(LoadBalancingRule lbRule, LoadBalancingRule targetRule) { + List lbRuleNetworks = getNetworksToAddIntoSource(lbRule, targetRule); + + if (!lbRuleNetworks.isEmpty()){ + lbService.assignNetworksToLoadBalancer(lbRule.getId(), lbRuleNetworks); + } + } + + protected List getNetworksToAddIntoSource(LoadBalancingRule lbRule, LoadBalancingRule targetRule) { + List lbRuleNetworks = lbRule.getAllNetworks(); + List targetNetworks = targetRule.getAllNetworks(); + + targetNetworks.removeAll(lbRuleNetworks); + return targetNetworks; + } + + + @Override + public List findLoadBalancerChilds(LoadBalancerVO lb) { + return configDao.getConfigsByValue(GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer, lb.getUuid()); + } + + @Override + @DB + public LoadBalancer unlinkLoadBalancer(Long lbid) { + LoadBalancer lb = lbService.findById(lbid); + + Long vipId = getVipIdApplyLbIfNeed(lb); + String region = GloboNetworkManager.GloboNetworkRegion.value(); + UnlinkPoolsFromLbCommand command = new UnlinkPoolsFromLbCommand(lbid, vipId, lb.getName(), region); + + NetworkVO byId = networkDao.findById(lb.getNetworkId()); + globoNetworkSvc.callCommand(command, byId.getDataCenterId()); + + GloboResourceConfiguration linkedConfig = globoNetworkSvc.getGloboResourceConfiguration(lb.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + resourceConfigDao.remove(linkedConfig.getId().toString()); + + return lb; + } + + private void checkIfLinkWillCreateARecursiveExecution(LoadBalancer lb, LoadBalancer targetLb) { + GloboResourceConfiguration linkedConfig = globoNetworkSvc.getGloboResourceConfiguration(lb.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + + } + + + private Long getVipIdApplyLbIfNeed(LoadBalancer lb) { + GloboNetworkIpDetailVO vipIp = globoNetworkSvc.getNetworkApiVipIp(lb); + Long vipId = vipIp.getGloboNetworkVipId(); + + if (vipId == null) { + try { + s_logger.debug("LB " + lb.getName() + " is not applied, trying to apply in networkAPI..."); + NetworkVO network = networkDao.findById(lb.getNetworkId()); + LoadBalancingRule loadBalancerRuleToApply = _lbMgr.getLoadBalancerRuleToApply((LoadBalancerVO) lb); + globoNetworkSvc.applyLbRuleInGloboNetwork(network, loadBalancerRuleToApply); + + vipIp = globoNetworkSvc.getNetworkApiVipIp(lb); + vipId = vipIp.getGloboNetworkVipId(); + + return vipId; + } catch (Exception e) { + throw new CloudRuntimeException("Load balancer '" + lb.getName() + "' is not applied, unexpected error while trying to apply this load balancer in NetworkAPI. Please, contact your system administrator.", e); + } + } + return vipId; + } + + protected GloboResourceConfiguration registerLink(LoadBalancer lb, LoadBalancer targetLb) { + GloboResourceConfigurationVO config = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.linkedLoadBalancer, targetLb.getUuid()); + + config = resourceConfigDao.persist(config); + + return config; + } + + protected void checkIfSourceLBHasVms(LoadBalancer lb) { + List loadBalancerVMMapVOS = _lb2VmMapDao.listByLoadBalancerId(lb.getId()); + + if (loadBalancerVMMapVOS.size() > 0) { + throw new CloudRuntimeException("Can not link load balancers. Please,remove all virtual machines(" + loadBalancerVMMapVOS.size() + ") from load balancer " + lb.getName() + " before link to another one."); + } + } + + /* + throws an exception if lb already has link with another load balancer, else return source lb instance + */ + protected LoadBalancer checkIfLBAlreadyIsLinked(Long sourceLbId) { + LoadBalancer lb = lbService.findById(sourceLbId); + + GloboResourceConfiguration linkedConfig = globoNetworkSvc.getGloboResourceConfiguration(lb.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + if (linkedConfig != null) { + LoadBalancer target = lbService.findByUuid(linkedConfig.getValue()); + throw new CloudRuntimeException("Load balancer '" + lb.getName() + "'(" + lb.getUuid() + ") already has link with load balancer '"+ target.getName() + "'(" + target.getUuid() +")."); + } + + return lb; + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(ListGloboLinkableLoadBalancersCmd.class); + cmdList.add(LinkGloboLoadBalancerCmd.class); + cmdList.add(UnlinkGloboLoadBalancerCmd.class); + return cmdList; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerService.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerService.java new file mode 100644 index 000000000000..977292a3d12e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerService.java @@ -0,0 +1,17 @@ +package com.globo.globonetwork.cloudstack.manager; + +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.rules.LoadBalancer; +import java.util.List; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; + +public interface GloboLoadBalancerService { + + LoadBalancer linkLoadBalancer(Long sourceLbid, Long targetLbid); + + LoadBalancer unlinkLoadBalancer(Long lbid); + + LoadBalancer copyVmsAndNetworks(Long fromLbId, Long toLbid); + + List findLoadBalancerChilds(LoadBalancerVO lb); +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkManager.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkManager.java new file mode 100644 index 000000000000..a43980d4d011 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkManager.java @@ -0,0 +1,2910 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.manager; + +import com.cloud.domain.Domain; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerOptionsDao; +import com.cloud.network.dao.LoadBalancerOptionsVO; +import com.cloud.network.dao.LoadBalancerPortMapDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerPortMapVO; +import com.cloud.network.dao.NetworkDao; +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.LoadBalancerVO; +import com.cloud.network.dao.UserIpv6AddressDao; +import com.cloud.utils.exception.UserCloudRuntimeException; +import com.cloud.utils.net.Ip; +import com.cloud.vm.NicVO; + +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; +import com.globo.globodns.cloudstack.element.GloboDnsTO; +import com.globo.globonetwork.cloudstack.api.CreateGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.DeleteGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.GetGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboLbNetworksCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkExpectedHealthchecksCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkPoolsCmd; +import com.globo.globonetwork.cloudstack.api.UpdateGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.loadbalancer.CreateGloboLoadBalancerCmd; +import com.globo.globonetwork.cloudstack.api.loadbalancer.DeleteGloboLoadBalancerCmd; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.CheckDSREnabled; +import com.globo.globonetwork.cloudstack.commands.CreatePoolCommand; +import com.globo.globonetwork.cloudstack.commands.DeletePoolCommand; +import com.globo.globonetwork.cloudstack.commands.GetPoolLBByIdCommand; +import com.globo.globonetwork.cloudstack.commands.ListExpectedHealthchecksCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolLBCommand; +import com.globo.globonetwork.cloudstack.commands.UpdatePoolCommand; +import com.globo.globonetwork.cloudstack.commands.ValidateVipUpdateCommand; +import com.globo.globonetwork.cloudstack.response.CheckDSREnabledResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.math.BigInteger; +import java.net.URI; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkLBCacheGroupsCmd; +import com.globo.globonetwork.cloudstack.api.RegisterDnsForResourceCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkPoolOptionsCmd; +import com.globo.globonetwork.cloudstack.commands.ListGloboNetworkLBCacheGroupsCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolOptionsCommand; +import com.globo.globonetwork.cloudstack.response.GloboNetworkCacheGroupsResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse; +import com.globo.globonetwork.cloudstack.api.GetGloboResourceConfigurationCmd; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +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.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpDao; +import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.PortableIpRangeDao; +import org.apache.cloudstack.region.PortableIpRangeVO; +import org.apache.cloudstack.region.PortableIpVO; +import org.apache.log4j.Logger; +import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.stereotype.Component; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.CloudException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.IpAddressManager; +import com.cloud.network.Network; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkService; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.UserIpv6AddressVO; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.guru.NetworkGuru; +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.LoadBalancingRulesManager; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.org.Grouping; +import com.cloud.projects.Project; +import com.cloud.projects.ProjectManager; +import com.cloud.resource.ResourceManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainManager; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.Journal; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GlobalLock; +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.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.Nic; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; +import com.globo.globodns.cloudstack.element.GloboDnsElementService; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.globo.globonetwork.cloudstack.GloboNetworkNetworkVO; +import com.globo.globonetwork.cloudstack.GloboNetworkVipAccVO; +import com.globo.globonetwork.cloudstack.api.AcquireNewIpForLbInGloboNetworkCmd; +import com.globo.globonetwork.cloudstack.api.AddGloboNetworkEnvironmentCmd; +import com.globo.globonetwork.cloudstack.api.AddGloboNetworkHostCmd; +import com.globo.globonetwork.cloudstack.api.AddGloboNetworkLBEnvironmentCmd; +import com.globo.globonetwork.cloudstack.api.AddNetworkViaGloboNetworkCmd; +import com.globo.globonetwork.cloudstack.api.DeleteNetworkInGloboNetworkCmd; +import com.globo.globonetwork.cloudstack.api.DisassociateIpAddrFromGloboNetworkCmd; +import com.globo.globonetwork.cloudstack.api.ImportGloboNetworkLoadBalancerCmd; +import com.globo.globonetwork.cloudstack.api.ListAllEnvironmentsFromGloboNetworkCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkCapabilitiesCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkEnvironmentsCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkLBEnvironmentsCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkRealsCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboNetworkVipsCmd; +import com.globo.globonetwork.cloudstack.api.RemoveGloboNetworkEnvironmentCmd; +import com.globo.globonetwork.cloudstack.api.RemoveGloboNetworkLBEnvironmentCmd; +import com.globo.globonetwork.cloudstack.commands.AcquireNewIpForLbCommand; +import com.globo.globonetwork.cloudstack.commands.ActivateNetworkCommand; + +import com.globo.globonetwork.cloudstack.commands.CreateNewVlanInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.DeallocateVlanFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetVipInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetVlanInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GloboNetworkErrorAnswer; +import com.globo.globonetwork.cloudstack.commands.ListAllEnvironmentsFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RegisterEquipmentAndIpInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.ReleaseIpFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveNetworkInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveVipFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.UnregisterEquipmentAndIpInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkEnvironmentDao; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkIpDetailDao; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkLoadBalancerEnvironmentDAO; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkNetworkDao; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkVipAccDao; +import com.globo.globonetwork.cloudstack.exception.CloudstackGloboNetworkException; +import com.globo.globonetwork.cloudstack.resource.GloboNetworkResource; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAllEnvironmentResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAllEnvironmentResponse.Environment; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAndIPResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse.Real; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVlanResponse; + +@Component +public class GloboNetworkManager implements GloboNetworkService, PluggableService, Configurable { + + private static final Logger s_logger = Logger.getLogger(GloboNetworkManager.class); + + static final int NUMBER_OF_RESERVED_IPS_FROM_START = 1; + static final int NUMBER_OF_RESERVED_IPS_BEFORE_END = 2; + static final int NUMBER_OF_RESERVED_IPS_FOR_LB_FROM_START = 1; + static final int NUMBER_OF_RESERVED_IPS_FOR_LB_BEFORE_END = 2; + + private static final ConfigKey GloboNetworkConnectionTimeout = new ConfigKey("Network", String.class, "globonetwork.connectiontimeout", "120000", + "GloboNetwork connection timeout (in milliseconds)", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkReadTimeout = new ConfigKey("Network", String.class, "globonetwork.readtimeout", "120000", + "GloboNetwork read timeout (in milliseconds)", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkNumberOfRetries = new ConfigKey("Network", String.class, "globonetwork.numberofretries", "0", + "GloboNetwork number of retries", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkVmEquipmentGroup = new ConfigKey("Network", Long.class, "globonetwork.vm.equipmentgroup", "", + "Equipment group to be used when registering a VM NIC in GloboNetwork", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmUser = new ConfigKey("Network", Long.class, "globonetwork.model.vm.user", "83", + "GloboNetwork model id to be used for User VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmDomainRouter = new ConfigKey("Network", Long.class, "globonetwork.model.vm.domain.router", "84", + "GloboNetwork model id to be used for Domain Router VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmConsoleProxy = new ConfigKey("Network", Long.class, "globonetwork.model.vm.console.proxy", "85", + "GloboNetwork model id to be used for Console Proxy VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmSecondaryStorageVm = new ConfigKey("Network", Long.class, "globonetwork.model.vm.secondary.storage", "86", + "GloboNetwork model id to be used for Secondary Storage VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmElasticIpVm = new ConfigKey("Network", Long.class, "globonetwork.model.vm.elastic.ip", "87", + "GloboNetwork model id to be used for Elastic IP VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmElasticLoadBalancerVm = new ConfigKey("Network", Long.class, "globonetwork.model.vm.elastic.load.balancer", "88", + "GloboNetwork model id to be used for Elastic Load Balancer VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmInternalLoadBalancerVm = new ConfigKey("Network", Long.class, "globonetwork.model.vm.internal.load.balancer", + "89", "GloboNetwork model id to be used for Internal Load Balancer VMs", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkModelVmUserBareMetal = new ConfigKey("Network", Long.class, "globonetwork.model.vm.user.bare.metal", "90", + "GloboNetwork model id to be used for User Bare Metal", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkDomainPattern = new ConfigKey("Network", String.class, "globonetwork.domain.pattern", "", + "Domain pattern to ensure in all networks created with GloboNetwork", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkDomainSuffix = new ConfigKey("Network", String.class, "globonetwork.domain.suffix", "", + "Domain suffix to ensure in all networks created with GloboNetwork (empty you are free to create in any domain)", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkVIPServerUrl = new ConfigKey("Network", String.class, "globonetwork.vip.server.url", "", + "Server URL to generate a new VIP request", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkLBLockTimeout = new ConfigKey("Network", Integer.class, "globonetwork.loadbalancer.lock.timeout", "60", + "GloboNetwork Loadbalancer lock timeout (in seconds). This option avoid concurrent operations.", true, ConfigKey.Scope.Global); + private static final ConfigKey GloboNetworkLBAllowedSuffixes = new ConfigKey("Network", String.class, "globonetwork.lb.allowed.suffixes", "", + "Allowed domain suffixes for load balancers created with GloboNetwork. List of domain names separated by commas", true, ConfigKey.Scope.Global); + + private static final ConfigKey minSubnetMaskInBIts = new ConfigKey("Network", Integer.class, "globonetwork.min.subnetinbits", "24", + "Min subnet mask in bits for allowed to create network in globonetwork, use 24 for a /24 subnet", true, ConfigKey.Scope.Global); + + private static final ConfigKey maxSubnetMaskInBIts = new ConfigKey("Network", Integer.class, "globonetwork.min.subnetinbits", "29", + "Max subnet mask in bits for allowed to create network in globonetwork, use 29 for a /29 subnet", true, ConfigKey.Scope.Global); + + public static final ConfigKey GloboNetworkRegion = new ConfigKey("Network", String.class, "globonetwork.region", "", + "Current region", true, ConfigKey.Scope.Global); + + + // DAOs + @Inject + DomainDao _domainDao; + @Inject + HostDao _hostDao; + @Inject + DataCenterDao _dcDao; + @Inject + HostPodDao _hostPodDao; + @Inject + VlanDao _vlanDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + NetworkOfferingDao _networkOfferingDao; + @Inject + ConfigurationDao _configDao; + @Inject + UserDao _userDao; + @Inject + NetworkDao _ntwkDao; + @Inject + NicDao _nicDao; + @Inject + NetworkServiceMapDao _ntwkSrvcDao; + @Inject + GloboNetworkNetworkDao _globoNetworkNetworkDao; + @Inject + GloboNetworkEnvironmentDao _globoNetworkEnvironmentDao; + @Inject + GloboNetworkVipAccDao _globoNetworkVipAccDao; + @Inject + GloboNetworkLoadBalancerEnvironmentDAO _globoNetworkLBEnvironmentDao; + @Inject + VMInstanceDao _vmDao; + + @Inject + UserVmDao _userVmDao; + + @Inject + IPAddressDao _ipAddrDao; + @Inject + PortableIpRangeDao _portableIpRangeDao; + @Inject + GloboNetworkIpDetailDao _globoNetworkIpDetailDao; + @Inject + PortableIpDao _portableIpDao; + @Inject + UserIpv6AddressDao _ipv6AddrDao; + @Inject + LoadBalancerPortMapDao _lbPortMapDao; + @Inject + GloboResourceConfigurationDao _globoResourceConfigurationDao; + @Inject + LoadBalancerDao _loadBalancerDao; + + // Managers + @Inject + NetworkModel _networkManager; + @Inject + AgentManager _agentMgr; + @Inject + ConfigurationManager _configMgr; + @Inject + ResourceManager _resourceMgr; + @Inject + DomainManager _domainMgr; + @Inject + NetworkOrchestrationService _networkMgr; + @Inject + AccountManager _accountMgr; + @Inject + ProjectManager _projectMgr; + @Inject + NetworkService _ntwSvc; + @Inject + IpAddressManager _ipAddrMgr; + @Inject + LoadBalancingRulesManager _lbMgr; + @Inject + LoadBalancingRulesService _lbService; + @Inject + GloboDnsElementService _globoDnsService; + @Inject + LoadBalancerOptionsDao _lbOptionsDao; + + @Override + public boolean canEnable(Long physicalNetworkId) { + if (physicalNetworkId == null) { + return false; + } + List list = _globoNetworkEnvironmentDao.listByPhysicalNetworkId(physicalNetworkId); + if (list.isEmpty()) { + throw new CloudRuntimeException("Before enabling GloboNetwork you must add an environment to your physical interface"); + } + return true; + } + + @Override + @DB + public Network createNetwork(String name, String displayText, Long zoneId, Long networkOfferingId, Long napiEnvironmentId, String networkDomain, ACLType aclType, + String accountName, Long projectId, Long domainId, Boolean subdomainAccess, Boolean displayNetwork, Long aclId, Boolean isIpv6, Long subnet) throws ResourceAllocationException, + ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { + + if ( isIpv6 && subnet != null ){ + throw new CloudRuntimeException("Subnet should be null when IPv6 is true! subnet: " + subnet + " isIPv6: " + isIpv6); + } + + Account caller = CallContext.current().getCallingAccount(); + + if ((accountName != null && domainId != null) || projectId != null) { + _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); + } + + DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Specified zone id was not found"); + } + int min = minSubnetMaskInBIts.value(); + int max = maxSubnetMaskInBIts.value(); + if ( subnet != null && (subnet < min || subnet > max)){ + throw new CloudRuntimeException("Subnet should be equals or greater than " + min + " and equals or less than " + max + " (" + min + " => subnet =< " + max + "). Value: " + subnet); + } + + Long physicalNetworkId = null; + if (napiEnvironmentId != null) { + GloboNetworkEnvironmentVO napiEnvironmentVO = null; + for (PhysicalNetwork pNtwk : _physicalNetworkDao.listByZone(zoneId)) { + napiEnvironmentVO = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(pNtwk.getId(), napiEnvironmentId); + if (napiEnvironmentVO != null) { + break; + } + } + if (napiEnvironmentVO == null) { + throw new InvalidParameterValueException("Unable to find a relationship between GloboNetwork environment and physical network"); + } + physicalNetworkId = napiEnvironmentVO.getPhysicalNetworkId(); + } else { + throw new InvalidParameterValueException("GloboNetwork enviromentId was not found"); + } + + // to avoid create a new vlan in GloboNetworkAPI, check first if networkDomain is valid + if (isSupportedCustomNetworkDomain()) { + String domainSuffix = GloboNetworkDomainSuffix.value(); + if (StringUtils.isNotBlank(domainSuffix) && !networkDomain.endsWith(domainSuffix)) { + throw new InvalidParameterValueException("Network domain need ends with " + domainSuffix); + } + } + + Answer answer = createNewVlan(zoneId, name, displayText, napiEnvironmentId, isIpv6, subnet); + + GloboNetworkVlanResponse response = (GloboNetworkVlanResponse)answer; + Long napiVlanId = response.getVlanId(); + + Network network = null; + + try { + network = this.createNetworkFromGloboNetworkVlan(napiVlanId, napiEnvironmentId, zoneId, networkOfferingId, physicalNetworkId, networkDomain, aclType, accountName, + projectId, domainId, subdomainAccess, displayNetwork, aclId); + } catch (Exception e) { + // Exception when creating network in Cloudstack. Roll back transaction in GloboNetwork + s_logger.error("Reverting network creation in GloboNetwork due to error creating network", e); + this.deallocateVlanFromGloboNetwork(zoneId, napiVlanId); + + throw new ResourceAllocationException(e.getLocalizedMessage(), ResourceType.network); + } + + // if the network offering has persistent set to true, implement the + // network + NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); + if (ntwkOff.getIsPersistent()) { + try { + if (network.getState() == Network.State.Setup) { + s_logger.debug("Network id=" + network.getId() + " is already provisioned"); + return network; + } + DeployDestination dest = new DeployDestination(zone, null, null, null); + UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); + Journal journal = new Journal.LogJournal("Implementing " + network, s_logger); + ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), journal, callerUser, caller); + s_logger.debug("Implementing network " + network + " as a part of network provision for persistent network"); + @SuppressWarnings("unchecked") + Pair implementedNetwork = (Pair)_networkMgr.implementNetwork(network.getId(), dest, context); + if (implementedNetwork.first() == null) { + s_logger.warn("Failed to provision the network " + network); + } + network = implementedNetwork.second(); + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to implement persistent guest network " + network + "due to ", ex); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement persistent guest network"); + e.addProxyObject(network.getUuid(), "networkId"); + throw e; + } + } + + return network; + } + + private Network createNetworkFromGloboNetworkVlan(final Long vlanId, final Long napiEnvironmentId, Long zoneId, final Long networkOfferingId, final Long physicalNetworkId, + final String networkDomain, final ACLType aclType, String accountName, Long projectId, final Long domainId, final Boolean subdomainAccess, + final Boolean displayNetwork, Long aclId) throws CloudException, ResourceUnavailableException, ResourceAllocationException, ConcurrentOperationException, + InsufficientCapacityException { + + final Account caller = CallContext.current().getCallingAccount(); + + final Account owner; + if ((accountName != null && domainId != null) || projectId != null) { + owner = _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); + } else { + owner = caller; + } + + // Only domain and account ACL types are supported in Action. + if (aclType == null || !(aclType == ACLType.Domain || aclType == ACLType.Account)) { + throw new InvalidParameterValueException("AclType should be " + ACLType.Domain + " or " + ACLType.Account + " for network of type " + Network.GuestType.Shared); + } + + final boolean isDomainSpecific = true; + + // Validate network offering + final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); + if (ntwkOff == null || ntwkOff.isSystemOnly()) { + InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id"); + if (ntwkOff != null) { + ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); + } + throw ex; + } else if (GuestType.Shared != ntwkOff.getGuestType()) { + InvalidParameterValueException ex = new InvalidParameterValueException("GloboNetwork can handle only network offering with guest type shared"); + if (ntwkOff != null) { + ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); + } + throw ex; + } + // validate physical network and zone + // Check if physical network exists + final PhysicalNetwork pNtwk; + if (physicalNetworkId != null) { + pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("invalid physicalNetworkId " + physicalNetworkId); + } + + if (zoneId == null) { + zoneId = pNtwk.getDataCenterId(); + } + + final DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Specified zone id was not found"); + } + + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isAdmin(caller.getAccountId())) { + // 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 (domainId != null) { + DomainVO domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Unable to find domain by specified id"); + } + _accountMgr.checkAccess(caller, domain); + } + + if (_configMgr.isOfferingForVpc(ntwkOff)) { + throw new InvalidParameterValueException("Network offering can't be used for VPC networks"); + } + + // CallContext.register(CallContext.current().getCallingUserId(), owner.getAccountId()); + + /////// GloboNetwork specific code /////// + + // Get VlanInfo from GloboNetwork + GetVlanInfoFromGloboNetworkCommand cmd = new GetVlanInfoFromGloboNetworkCommand(); + cmd.setVlanId(vlanId); + + final GloboNetworkVlanResponse vlanResponse = (GloboNetworkVlanResponse)callCommand(cmd, zoneId); + + String networkAddress = vlanResponse.getNetworkAddress(); + final String netmask = vlanResponse.getMask(); + // FIXME This does not work with IPv6! + final String cidr = networkAddress + "/" + vlanResponse.getBlock(); + + String gatewayStr = null; + String startIPStr = null; + String endIPStr = null; + final Long vlanNum = vlanResponse.getVlanNum(); + + String startIPv6Str = null; + String endIPv6Str = null; + String ip6GatewayStr = null; + + if (vlanResponse.isv6()) { + com.googlecode.ipv6.IPv6Network ipv6Network = com.googlecode.ipv6.IPv6Network.fromString(cidr); + com.googlecode.ipv6.IPv6Address ipv6Start = ipv6Network.getFirst().add(1); + ip6GatewayStr = ipv6Start.toString(); + startIPv6Str = ipv6Start.add(NUMBER_OF_RESERVED_IPS_FROM_START).toString(); + + com.googlecode.ipv6.IPv6Address ipv6End = ipv6Network.getLast(); + endIPv6Str = ipv6End.subtract(NUMBER_OF_RESERVED_IPS_BEFORE_END).toString(); + } else { + String ranges[] = NetUtils.ipAndNetMaskToRange(networkAddress, netmask); + gatewayStr = ranges[0]; + startIPStr = NetUtils.long2Ip(NetUtils.ip2Long(ranges[0]) + NUMBER_OF_RESERVED_IPS_FROM_START); + endIPStr = NetUtils.long2Ip(NetUtils.ip2Long(ranges[1]) - NUMBER_OF_RESERVED_IPS_BEFORE_END); + } + + final String startIP = startIPStr; + final String endIP = endIPStr; + final String gateway = gatewayStr; + + final String startIPv6 = startIPv6Str; + final String endIPv6 = endIPv6Str; + final String ip6Gateway = ip6GatewayStr; + final String ip6Cidr = cidr; + + s_logger.info("Creating network with name " + vlanResponse.getVlanName() + " (" + vlanResponse.getVlanId() + "), network " + networkAddress + " gateway " + gateway + + " startIp " + startIP + " endIp " + endIP + " cidr " + cidr + " startIPv6 " + startIPv6 + " end IPv6 " + endIPv6 + " IPv6 gateway " + ip6Gateway); + /////// End of GloboNetwork specific code /////// + + Network network = Transaction.execute(new TransactionCallbackWithException() { + + @Override + public Network doInTransaction(TransactionStatus status) throws CloudException { + Boolean newSubdomainAccess = subdomainAccess; + Long sharedDomainId = null; + Domain domain = null; + if (isDomainSpecific) { + if (domainId != null) { + sharedDomainId = domainId; + domain = _domainDao.findById(domainId); + } else { + sharedDomainId = owner.getDomainId(); + newSubdomainAccess = true; + } + } + + String newNetworkDomain = networkDomain; + if (!isSupportedCustomNetworkDomain()) { + // overwrite networkDomain + newNetworkDomain = generateNetworkDomain(zone, vlanResponse); + } + + Network network = _networkMgr.createGuestNetwork(networkOfferingId.longValue(), vlanResponse.getVlanName(), vlanResponse.getVlanDescription(), gateway, cidr, + String.valueOf(vlanResponse.getVlanNum()), false, newNetworkDomain, owner, sharedDomainId, pNtwk, zone.getId(), aclType, newSubdomainAccess, null, // vpcId, + ip6Gateway, ip6Cidr, displayNetwork, null, null // isolatedPvlan + ); + + // Save relashionship with napi and network + GloboNetworkNetworkVO napiNetworkVO = new GloboNetworkNetworkVO(vlanId, network.getId(), napiEnvironmentId); + napiNetworkVO = _globoNetworkNetworkDao.persist(napiNetworkVO); + + // if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { + // Create vlan ip range + _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, false, (Long)null, startIP, endIP, gateway, netmask, + vlanNum.toString(), false, domain, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + // } + return network; + } + }); + return network; + } + + protected String generateNetworkDomain(DataCenter zone, GloboNetworkVlanResponse vlan) { + Map context = new HashMap(); + context.put("zone", zone); + context.put("vlan", vlan); + + String newNetworkDomain = formatter(GloboNetworkDomainPattern.value(), context); + newNetworkDomain += GloboNetworkDomainSuffix.value().startsWith(".") ? GloboNetworkDomainSuffix.value() : "." + GloboNetworkDomainSuffix.value(); + return newNetworkDomain; + } + + /** + * Replace variables in a string template: #{obj.property}. + * @see {@linktourl http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html} + * @param template + * @param context + * @return + */ + protected String formatter(String template, Map context) { + StandardEvaluationContext evalContext = new StandardEvaluationContext(context); + evalContext.addPropertyAccessor(new MapAccessor()); + + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression(template, new TemplateParserContext()); + String formatted = exp.getValue(evalContext, String.class); + return formatted; + } + + protected GloboNetworkVlanResponse createNewVlan(Long zoneId, String name, String description, Long globoNetworkEnvironmentId, Boolean isIpv6, Long subnet) { + + CreateNewVlanInGloboNetworkCommand cmd = new CreateNewVlanInGloboNetworkCommand(); + cmd.setVlanName(name); + cmd.setVlanDescription(description); + cmd.setGloboNetworkEnvironmentId(globoNetworkEnvironmentId); + cmd.setIsIpv6(isIpv6); + cmd.setSubnet(subnet); + + return (GloboNetworkVlanResponse)callCommand(cmd, zoneId); + } + @Override + public Answer callCommand(Command cmd, Long zoneId) { + return callCommand(cmd, zoneId, true); + } + + private Answer callCommand(Command cmd, Long zoneId, boolean raisesExceptionWhenNoAnswer) { + HostVO napiHost = getGloboNetworkHost(zoneId); + if (napiHost == null) { + throw new CloudstackGloboNetworkException("Could not find the GloboNetwork resource"); + } + + Answer answer = _agentMgr.easySend(napiHost.getId(), cmd); + if (answer == null || !answer.getResult()) { + + if (answer instanceof GloboNetworkErrorAnswer) { + GloboNetworkErrorAnswer napiAnswer = (GloboNetworkErrorAnswer)answer; + throw new CloudstackGloboNetworkException(napiAnswer.getNapiCode(), napiAnswer.getNapiDescription(), napiAnswer.getNdcContext()); + } else { + if (raisesExceptionWhenNoAnswer) { + String msg = "Error executing command " + cmd + ". Maybe GloboNetwork Host is down"; + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + } + } + + return answer; + } + @Override + public HostVO getGloboNetworkHost(Long zoneId) { + return _hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboNetwork.getName(), Type.L2Networking); + } + + @Override + public Network validateNic(NicProfile nicProfile, VirtualMachineProfile vm, Network network) throws InsufficientVirtualNetworkCapacityException, + InsufficientAddressCapacityException { + + GetVlanInfoFromGloboNetworkCommand cmd = new GetVlanInfoFromGloboNetworkCommand(); + cmd.setVlanId(getGloboNetworkVlanId(network.getId())); + + String msg = "Unable to get VLAN " + getGloboNetworkVlanId(network.getId()) + " info from GloboNetwork"; + Answer answer = this.callCommand(cmd, network.getDataCenterId()); + if (answer == null || !answer.getResult()) { + msg = answer == null ? msg : answer.getDetails(); + throw new InsufficientVirtualNetworkCapacityException(msg, Nic.class, nicProfile.getId()); + } + + GloboNetworkVlanResponse vlanResponse = (GloboNetworkVlanResponse) answer; + + String networkAddress = vlanResponse.getNetworkAddress(); + String netmask = vlanResponse.getMask(); + + BigInteger ip; + BigInteger ipRangeStart; + BigInteger ipRangeEnd; + + if (vlanResponse.isv6()) { + com.googlecode.ipv6.IPv6Address ipv6 = com.googlecode.ipv6.IPv6Address.fromString(nicProfile.getIPv6Address()); + try { + ip = new BigInteger(ipv6.toInetAddress().getAddress()); + + com.googlecode.ipv6.IPv6Network ipv6Network = com.googlecode.ipv6.IPv6Network.fromAddressAndMask( + com.googlecode.ipv6.IPv6Address.fromString(networkAddress), + com.googlecode.ipv6.IPv6NetworkMask.fromAddress(com.googlecode.ipv6.IPv6Address.fromString(netmask))); + + com.googlecode.ipv6.IPv6Address ipv6Start = ipv6Network.getFirst(); + ipRangeStart = new BigInteger(ipv6Start.toInetAddress().getAddress()); + + com.googlecode.ipv6.IPv6Address ipv6End = ipv6Network.getLast(); + ipRangeEnd = new BigInteger(ipv6End.toInetAddress().getAddress()); + } catch (UnknownHostException ex) { + throw new InvalidParameterValueException("Nic IP " + nicProfile.getIPv6Address() + " is invalid"); + } + } else { + ip = BigInteger.valueOf(NetUtils.ip2Long(nicProfile.getIPv4Address())); + String ranges[] = NetUtils.ipAndNetMaskToRange(networkAddress, netmask); + ipRangeStart = BigInteger.valueOf(NetUtils.ip2Long(ranges[0])); + ipRangeEnd = BigInteger.valueOf(NetUtils.ip2Long(ranges[1])); + } + + if (!(ip.compareTo(ipRangeStart) >= 0 && ip.compareTo(ipRangeEnd) <= 0)) { + throw new InvalidParameterValueException("Nic IP " + nicProfile.getIPv4Address() + " does not belong to network " + networkAddress + " in vlanId " + cmd.getVlanId()); + } + + // everything is ok + return network; + } + + @Override + public void implementNetwork(Network network) throws ConfigurationException { + Long vlanId = getGloboNetworkVlanId(network.getId()); + if (vlanId == null) { + throw new CloudRuntimeException("Inconsistency. Network " + network.getName() + " there is not relation with GloboNetwork"); + } + + GloboNetworkVlanResponse vlanResponse = getVlanFromGloboNetwork(network, vlanId); + if (!vlanResponse.isActive()) { + // Create network in equipment + ActivateNetworkCommand activateCmd = new ActivateNetworkCommand(vlanResponse.getNetworkId(), vlanResponse.isv6()); + Answer cmdAnswer = callCommand(activateCmd, network.getDataCenterId()); + if (cmdAnswer == null || !cmdAnswer.getResult()) { + throw new CloudRuntimeException("Unable to create network in GloboNetwork: VlanId " + vlanId + " networkId " + vlanResponse.getNetworkId()); + } + s_logger.info("Network ready to use: VlanId " + vlanId + " networkId " + vlanResponse.getNetworkId()); + } else { + s_logger.warn("Network already created in GloboNetwork: VlanId " + vlanId + " networkId " + vlanResponse.getNetworkId()); + } + } + + /** + * Returns VlanId (in GloboNetwork) given an Network. If network is not + * associated with GloboNetwork, null will be returned. + * + * @param networkId + * @return + */ + private Long getGloboNetworkVlanId(Long networkId) { + if (networkId == null) { + return null; + } + GloboNetworkNetworkVO vo = _globoNetworkNetworkDao.findByNetworkId(networkId); + if (vo == null) { + return null; + } + return vo.getGloboNetworkVlanId(); + } + + /** + * Get the number of vlan associate with {@code network}. + * + * @param broadcastUri + * @return + */ + private Integer getVlanNum(URI broadcastUri) { + if (broadcastUri == null) { + return null; + } + try { + Integer vlanNum = Integer.valueOf(broadcastUri.getHost()); + return vlanNum; + } catch (NumberFormatException nfe) { + String msg = "Invalid Vlan number in broadcast URI " + broadcastUri; + s_logger.error(msg); + throw new CloudRuntimeException(msg, nfe); + } + } + + @Override + @DB + public GloboNetworkEnvironmentVO addGloboNetworkEnvironment(Long physicalNetworkId, String name, Long globoNetworkEnvironmentId) { + + if (name == null || name.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid name: " + name); + } + + // validate physical network and zone + // Check if physical network exists + PhysicalNetwork pNtwk = null; + if (physicalNetworkId != null) { + pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId); + } + + Long zoneId = pNtwk.getDataCenterId(); + + // now, check if environment exists in GloboNetwork + if (globoNetworkEnvironmentId != null) { + Environment environment = getEnvironment(physicalNetworkId, globoNetworkEnvironmentId); + if (environment == null) { + throw new InvalidParameterValueException("Unable to find in GloboNetwork an enviroment having the specified environment id"); + } + } else { + throw new InvalidParameterValueException("Invalid GloboNetwork environmentId: " + globoNetworkEnvironmentId); + } + + // Check if there is a environment with same id or name in this zone. + List globoNetworkEnvironments = listGloboNetworkEnvironmentsFromDB(null, zoneId); + for (GloboNetworkEnvironmentVO globoNetworkEnvironment : globoNetworkEnvironments) { + if (globoNetworkEnvironment.getName().equalsIgnoreCase(name)) { + throw new InvalidParameterValueException("GloboNetwork environment with name " + name + " already exists in zone " + zoneId); + } + if (globoNetworkEnvironment.getGloboNetworkEnvironmentId() == globoNetworkEnvironmentId) { + throw new InvalidParameterValueException("GloboNetwork environment with environmentId " + globoNetworkEnvironmentId + " already exists in zoneId " + zoneId); + } + } + + GloboNetworkEnvironmentVO napiEnvironmentVO = new GloboNetworkEnvironmentVO(physicalNetworkId, name, globoNetworkEnvironmentId); + _globoNetworkEnvironmentDao.persist(napiEnvironmentVO); + return napiEnvironmentVO; + } + + @Override + @DB + public GloboNetworkLoadBalancerEnvironment addGloboNetworkLBEnvironment(final String name, Long physicalNetworkId, Long globoNetworkEnvironmentId, + final Long globoNetworkLBEnvironmentId) throws ResourceAllocationException { + + if (name == null || name.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid name: " + name); + } + + // validate physical network and zone + // Check if physical network exists + PhysicalNetwork pNtwk = null; + if (physicalNetworkId != null) { + pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId); + } + + // Check if there is a environment with same id or name in this zone. + final GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(pNtwk.getId(), globoNetworkEnvironmentId); + if (globoNetworkEnvironment == null) { + throw new InvalidParameterValueException("Could not find a relationship between GloboNetwork Environment " + globoNetworkEnvironmentId + " and physical network " + + physicalNetworkId); + } + + // Check if there is a LB environment with same id or name in this zone. + List globoNetworkLBEnvironments = listGloboNetworkLBEnvironmentsFromDB(pNtwk.getId(), null, globoNetworkEnvironmentId); + for (GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironment : globoNetworkLBEnvironments) { + if (globoNetworkLBEnvironment.getName().equalsIgnoreCase(name)) { + throw new InvalidParameterValueException("LB environment with name " + name + " already exists."); + } + if (globoNetworkLBEnvironment.getGloboNetworkLoadBalancerEnvironmentId() == globoNetworkLBEnvironmentId) { + throw new InvalidParameterValueException("Relationship between Environment " + globoNetworkEnvironmentId + " and LB Network " + globoNetworkLBEnvironmentId + + " already exists."); + } + } + + try { + GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironmentVO = Transaction + .execute(new TransactionCallbackWithException() { + + @Override + public GloboNetworkLoadBalancerEnvironment doInTransaction(TransactionStatus status) throws CloudException { + GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironmentVO = new GloboNetworkLoadBalancerEnvironment(name, globoNetworkEnvironment.getId(), + globoNetworkLBEnvironmentId); + _globoNetworkLBEnvironmentDao.persist(globoNetworkLBEnvironmentVO); + return globoNetworkLBEnvironmentVO; + } + }); + + return globoNetworkLBEnvironmentVO; + + } catch (CloudException e) { + // Exception when defining IP ranges in Cloudstack + throw new ResourceAllocationException(e.getLocalizedMessage(), ResourceType.public_ip); + } + } + + @Override + @DB + public Host addGloboNetworkHost(Long physicalNetworkId, String username, String password, String url) { + + if (username == null || username.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid username: " + username); + } + + if (password == null || password.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid password: " + password); + } + + if (url == null || url.trim().isEmpty()) { + throw new InvalidParameterValueException("Invalid url: " + url); + } + + // validate physical network and zone + // Check if physical network exists + PhysicalNetwork pNtwk = null; + if (physicalNetworkId != null) { + pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId); + } + + final Long zoneId = pNtwk.getDataCenterId(); + + final Map params = new HashMap(); + params.put("guid", "globonetwork-" + String.valueOf(zoneId)); + params.put("zoneId", String.valueOf(zoneId)); + params.put("name", Provider.GloboNetwork.getName()); + + String readTimeout = GloboNetworkReadTimeout.value(); + String connectTimeout = GloboNetworkConnectionTimeout.value(); + String numberOfRetries = GloboNetworkNumberOfRetries.value(); + + params.put("url", url); + params.put("username", username); + params.put("password", password); + params.put("readTimeout", readTimeout); + params.put("connectTimeout", connectTimeout); + params.put("numberOfRetries", numberOfRetries); + + final Map hostDetails = new HashMap(); + hostDetails.putAll(params); + + Host host = Transaction.execute(new TransactionCallback() { + + @Override + public Host doInTransaction(TransactionStatus status) { + GloboNetworkResource resource = new GloboNetworkResource(); + + try { + resource.configure(Provider.GloboNetwork.getName(), hostDetails); + + Host host = _resourceMgr.addHost(zoneId, resource, resource.getType(), params); + return host; + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e); + } + } + }); + return host; + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(AcquireNewIpForLbInGloboNetworkCmd.class); + cmdList.add(CreateGloboLoadBalancerCmd.class); + cmdList.add(DeleteGloboLoadBalancerCmd.class); + cmdList.add(AddGloboNetworkEnvironmentCmd.class); + cmdList.add(AddGloboNetworkHostCmd.class); + cmdList.add(AddGloboNetworkLBEnvironmentCmd.class); + cmdList.add(AddNetworkViaGloboNetworkCmd.class); + cmdList.add(DeleteNetworkInGloboNetworkCmd.class); + cmdList.add(DisassociateIpAddrFromGloboNetworkCmd.class); + cmdList.add(ImportGloboNetworkLoadBalancerCmd.class); + cmdList.add(ListAllEnvironmentsFromGloboNetworkCmd.class); + cmdList.add(ListGloboNetworkCapabilitiesCmd.class); + cmdList.add(ListGloboNetworkEnvironmentsCmd.class); + cmdList.add(ListGloboNetworkLBEnvironmentsCmd.class); + cmdList.add(ListGloboNetworkRealsCmd.class); + cmdList.add(ListGloboNetworkPoolOptionsCmd.class); + cmdList.add(ListGloboNetworkVipsCmd.class); + cmdList.add(RemoveGloboNetworkEnvironmentCmd.class); + cmdList.add(RemoveGloboNetworkLBEnvironmentCmd.class); + cmdList.add(ListGloboNetworkLBCacheGroupsCmd .class); + cmdList.add(ListGloboNetworkPoolsCmd.class); + cmdList.add(ListGloboNetworkExpectedHealthchecksCmd.class); + cmdList.add(GetGloboNetworkPoolCmd.class); + cmdList.add(UpdateGloboNetworkPoolCmd.class); + cmdList.add(CreateGloboNetworkPoolCmd.class); + cmdList.add(DeleteGloboNetworkPoolCmd.class); + cmdList.add(ListGloboLbNetworksCmd.class); + cmdList.add(RegisterDnsForResourceCmd.class); + cmdList.add(GetGloboResourceConfigurationCmd.class); + return cmdList; + } + + @Override + public List listAllEnvironmentsFromGloboNetwork(Long physicalNetworkId) { + + // validate physical network and zone + // Check if physical network exists + PhysicalNetwork pNtwk = null; + if (physicalNetworkId != null) { + pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + } else { + throw new InvalidParameterValueException("Invalid physicalNetworkId: " + physicalNetworkId); + } + + Long zoneId = pNtwk.getDataCenterId(); + + ListAllEnvironmentsFromGloboNetworkCommand cmd = new ListAllEnvironmentsFromGloboNetworkCommand(); + + Answer answer = callCommand(cmd, zoneId); + + List environments = ((GloboNetworkAllEnvironmentResponse)answer).getEnvironmentList(); + return environments; + } + + /** + * Get Environment object from environmentId. + * @param environmentId + * @return Return null if environment was not found. + */ + protected Environment getEnvironment(Long physicaNetworkId, Long environmentId) { + if (environmentId == null) { + return null; + } + + Environment resultEnvironment = null; + for (Environment environment : listAllEnvironmentsFromGloboNetwork(physicaNetworkId)) { + if (environmentId.equals(environment.getId())) { + resultEnvironment = environment; + break; + } + } + return resultEnvironment; + } + + private void handleNetworkUnavailableError(CloudstackGloboNetworkException e) { + if (e.getNapiCode() == 116) { + // If this is the return code, it means that the vlan/network no longer exists in GloboNetwork + // and we should continue to remove it from CloudStack + s_logger.warn("Inconsistency between CloudStack and GloboNetwork"); + return; + } else { + // Otherwise, there was a different error and we should abort the operation + throw e; + } + } + + public boolean destroyGloboNetwork(long networkId, boolean forced) { + + Network network = _ntwSvc.getNetwork(networkId); + if (network == null) { + InvalidParameterValueException ex = new InvalidParameterValueException("unable to find network with specified id"); + ex.addProxyObject(String.valueOf(networkId), "networkId"); + throw ex; + } + + GloboNetworkNetworkVO napiNetworkVO = _globoNetworkNetworkDao.findByNetworkId(network.getId()); + if (napiNetworkVO == null) { + InvalidParameterValueException ex = new InvalidParameterValueException("Only networks managed by GloboNetwork may be deleted by this method"); + ex.addProxyObject(String.valueOf(networkId), "networkId"); + throw ex; + } + + Account caller = CallContext.current().getCallingAccount(); + User userCaller = CallContext.current().getCallingUser(); + String contextId = CallContext.current().getContextId(); + + // Perform permission check + _accountMgr.checkAccess(caller, null, true, network); + + // ACS with allow exclusion of shared network by admin users, so after check + // permission, let's change to system account to perform destroy network + + try { + CallContext.register(_accountMgr.getSystemUser(), _accountMgr.getSystemAccount(), contextId); + CallContext.current().putContextParameter(Network.class.getName(), network.getUuid()); + return _ntwSvc.deleteNetwork(networkId, forced); + } finally { + // restore context + CallContext.register(userCaller, caller, contextId); + } + } + + @Override + public void removeNetworkFromGloboNetwork(Network network) { + try { + // Make sure the VLAN is valid + Vlan vlan = this.getVlanInfoFromGloboNetwork(network); + //Get Network type (ipv4 or ipv6) and network ID from Globo Network API + GloboNetworkVlanResponse vlanResponse = getVlanFromGloboNetwork(network, vlan.getId()); + RemoveNetworkInGloboNetworkCommand cmd = new RemoveNetworkInGloboNetworkCommand(); + cmd.setNetworkId(vlanResponse.getNetworkId()); + cmd.setIsIpv6(vlanResponse.isv6()); + + this.callCommand(cmd, network.getDataCenterId()); + } catch (CloudstackGloboNetworkException e) { + handleNetworkUnavailableError(e); + } + } + + protected GloboNetworkVlanResponse getVlanFromGloboNetwork(Network network, Long vlanId) { + GetVlanInfoFromGloboNetworkCommand cmd = new GetVlanInfoFromGloboNetworkCommand(); + cmd.setVlanId(vlanId); + return (GloboNetworkVlanResponse) callCommand(cmd, network.getDataCenterId()); + } + + @Override + @DB + public void deallocateVlanFromGloboNetwork(Network network) { + + try { + GloboNetworkNetworkVO napiNetworkVO = _globoNetworkNetworkDao.findByNetworkId(network.getId()); + if (napiNetworkVO != null) { + this.deallocateVlanFromGloboNetwork(network.getDataCenterId(), napiNetworkVO.getGloboNetworkVlanId()); + _globoNetworkNetworkDao.remove(napiNetworkVO.getId()); + } + + } catch (CloudstackGloboNetworkException e) { + handleNetworkUnavailableError(e); + } + } + + public void deallocateVlanFromGloboNetwork(Long zoneId, Long vlanId) { + + DeallocateVlanFromGloboNetworkCommand cmd = new DeallocateVlanFromGloboNetworkCommand(); + cmd.setVlanId(vlanId); + + this.callCommand(cmd, zoneId); + } + + @Override + public List listGloboNetworkEnvironmentsFromDB(Long physicalNetworkId, Long zoneId) { + List globoNetworkEnvironmentsVOList; + + if (physicalNetworkId != null) { + // Check if physical network exists + PhysicalNetwork pNtwk = _physicalNetworkDao.findById(physicalNetworkId); + if (pNtwk == null) { + throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id"); + } + + globoNetworkEnvironmentsVOList = _globoNetworkEnvironmentDao.listByPhysicalNetworkId(physicalNetworkId); + + } else if (zoneId != null) { + // Check if zone exists + DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Specified zone id was not found"); + } + + globoNetworkEnvironmentsVOList = new ArrayList(); + for (PhysicalNetworkVO physicalNetwork : _physicalNetworkDao.listByZone(zoneId)) { + List partialResult = _globoNetworkEnvironmentDao.listByPhysicalNetworkId(physicalNetwork.getId()); + if (partialResult != null) { + globoNetworkEnvironmentsVOList.addAll(partialResult); + } + } + } else { + globoNetworkEnvironmentsVOList = _globoNetworkEnvironmentDao.listAll(); + } + + return globoNetworkEnvironmentsVOList; + } + + @Override + public List listGloboNetworkLBEnvironmentsFromDB(Long physicalNetworkId, Long networkId, Long globoNetworkEnvironmentId) { + + Long glbEnvId = null; + if (networkId != null) { + // Retrieve glbEnvId from network + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(networkId); + if (globoNetworkNetworkVO == null) { + throw new InvalidParameterValueException("Unable to find mapping for networkId " + networkId); + } + glbEnvId = globoNetworkNetworkVO.getGloboNetworkEnvironmentId(); + } else if (globoNetworkEnvironmentId != null) { + glbEnvId = globoNetworkEnvironmentId; + } else { + throw new InvalidParameterValueException("NetworkId or GloboNetworkEnvironmentId is required"); + } + + // Retrieve napiEnvironment from DB + GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(physicalNetworkId, glbEnvId); + + if (globoNetworkEnvironment == null) { + // No physical network/environment pair registered in the database. + throw new InvalidParameterValueException("Unable to find a relationship between physical network=" + physicalNetworkId + " and GloboNetwork environment=" + + globoNetworkEnvironmentId); + } + + List globoNetworkLBEnvironmentVOList; + + if (glbEnvId != null) { + globoNetworkLBEnvironmentVOList = _globoNetworkLBEnvironmentDao.listByEnvironmentRefId(globoNetworkEnvironment.getId()); + + } else { + globoNetworkLBEnvironmentVOList = _globoNetworkLBEnvironmentDao.listAll(); + } + + return globoNetworkLBEnvironmentVOList; + } + + @Override + @DB + public boolean removeGloboNetworkEnvironment(Long physicalNetworkId, Long globoNetworkEnvironmentId) { + + // Check if there are any networks in this GloboNetwork environment + List associationList = _globoNetworkNetworkDao.listByEnvironmentId(globoNetworkEnvironmentId); + + if (!associationList.isEmpty()) { + throw new InvalidParameterValueException("There are active networks on environment " + globoNetworkEnvironmentId + + ". Please delete them before removing this environment."); + } + + // Retrieve napiEnvironment from DB + GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(physicalNetworkId, globoNetworkEnvironmentId); + + if (globoNetworkEnvironment == null) { + // No physical network/environment pair registered in the database. + throw new InvalidParameterValueException("Unable to find a relationship between physical network=" + physicalNetworkId + " and GloboNetwork environment=" + + globoNetworkEnvironmentId); + } + + boolean result = _globoNetworkEnvironmentDao.remove(globoNetworkEnvironment.getId()); + + return result; + } + + @Override + @DB + public boolean removeGloboNetworkLBEnvironment(Long physicalNetworkId, Long globoNetworkEnvironmentId, Long globoNetworkLBEnvironmentId) { + + // Retrieve napiEnvironment from DB + GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(physicalNetworkId, globoNetworkEnvironmentId); + + if (globoNetworkEnvironment == null) { + // No physical network/environment pair registered in the database. + throw new InvalidParameterValueException("Unable to find a relationship between physical network=" + physicalNetworkId + " and GloboNetwork environment=" + + globoNetworkEnvironmentId); + } + + // Retrieve LB Environment from DB + final GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironmentVO = _globoNetworkLBEnvironmentDao.findByEnvironmentRefAndLBNetwork(globoNetworkEnvironment.getId(), + globoNetworkLBEnvironmentId); + if (globoNetworkLBEnvironmentVO == null) { + throw new InvalidParameterValueException("Unable to find a relationship between environment " + globoNetworkEnvironmentId + " and LB network " + + globoNetworkLBEnvironmentId); + } + + List ipDetailVOList = _globoNetworkIpDetailDao.listByLBEnvironmentRef(globoNetworkLBEnvironmentVO.getGloboNetworkLoadBalancerEnvironmentId()); + if (!ipDetailVOList.isEmpty()) { + throw new InvalidParameterValueException("There are Load Balancers in environment '" + globoNetworkLBEnvironmentVO.getName() + "'"); + } + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + _globoNetworkLBEnvironmentDao.remove(globoNetworkLBEnvironmentVO.getId()); + + // never remove portable ip range because can be used in others zones. +// if (_portableIpRangeDao.findById(globoNetworkLBNetworkVO.getPortableIpRangeId()) != null) { +// if portable ip range was removed, is ok. +// _configMgr.deletePortableIpRange(globoNetworkLBNetworkVO.getPortableIpRangeId()); +// } + } + }); + return true; + } + + @Override + public Vlan getVlanInfoFromGloboNetwork(Network network) { + GetVlanInfoFromGloboNetworkCommand cmd = new GetVlanInfoFromGloboNetworkCommand(); + Long vlanId = getGloboNetworkVlanId(network.getId()); + cmd.setVlanId(vlanId); + + GloboNetworkVlanResponse response = (GloboNetworkVlanResponse)callCommand(cmd, network.getDataCenterId()); + + Vlan vlan = new Vlan(); + vlan.setId(response.getVlanId()); + vlan.setName(response.getVlanName()); + vlan.setVlanNum(response.getVlanNum()); + vlan.setDescription(response.getVlanDescription()); + + return vlan; + } + + @Override + public void registerNicInGloboNetwork(NicProfile nic, VirtualMachineProfile vm, Network network) { + + String msg = "Unable to register nic " + nic + " from VM " + vm + "."; + if (vm == null || nic == null) { + throw new CloudRuntimeException(msg + " Invalid nic, virtual machine or network."); + } + + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(network.getId()); + if (globoNetworkNetworkVO == null) { + throw new CloudRuntimeException(msg + " Could not obtain mapping for network in GloboNetwork."); + } + + Long equipmentGroup = GloboNetworkVmEquipmentGroup.value(); + if (equipmentGroup == null || "".equals(equipmentGroup)) { + throw new CloudRuntimeException(msg + " Invalid equipment group for VM. Check your GloboNetwork global options."); + } + + Long equipmentModel = null; + switch (vm.getType()) { + case DomainRouter: + equipmentModel = GloboNetworkModelVmDomainRouter.value(); + break; + case ConsoleProxy: + equipmentModel = GloboNetworkModelVmConsoleProxy.value(); + break; + case SecondaryStorageVm: + equipmentModel = GloboNetworkModelVmSecondaryStorageVm.value(); + break; + case ElasticIpVm: + equipmentModel = GloboNetworkModelVmElasticIpVm.value(); + break; + case ElasticLoadBalancerVm: + equipmentModel = GloboNetworkModelVmElasticLoadBalancerVm.value(); + break; + case InternalLoadBalancerVm: + equipmentModel = GloboNetworkModelVmInternalLoadBalancerVm.value(); + break; + case UserBareMetal: + equipmentModel = GloboNetworkModelVmUserBareMetal.value(); + break; + default: + equipmentModel = GloboNetworkModelVmUser.value(); + break; + } + if (equipmentModel == null) { + throw new CloudRuntimeException(msg + " Invalid equipment model for VM of type " + vm.getType() + ". Check your GloboNetwork global options."); + } + + RegisterEquipmentAndIpInGloboNetworkCommand cmd = new RegisterEquipmentAndIpInGloboNetworkCommand(); + if (nic.getIPv4Address() != null) { + cmd.setNicIp(nic.getIPv4Address()); + } else { + cmd.setNicIp(nic.getIPv6Address()); + } + + SimpleDateFormat format = new SimpleDateFormat("dd-MM-YYYY-HH:mm:ss"); + String description = "ACS_IP_" + GloboNetworkRegion.value()+ "_"+ format.format(new Date()); + + cmd.setNicDescription(description); + cmd.setVmName(getEquipNameFromUuid(vm.getUuid())); + cmd.setVlanId(globoNetworkNetworkVO.getGloboNetworkVlanId()); + cmd.setEnvironmentId(globoNetworkNetworkVO.getGloboNetworkEnvironmentId()); + cmd.setEquipmentGroupId(Long.valueOf(equipmentGroup)); + cmd.setEquipmentModelId(Long.valueOf(equipmentModel)); + + Answer answer = this.callCommand(cmd, vm.getVirtualMachine().getDataCenterId()); + if (answer == null || !answer.getResult()) { + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + } + + private String getEquipNameFromUuid(String uuid) { + String instanceNamePrefix = _configDao.getValue(Config.InstanceName.key()); + String equipName = ""; + if (instanceNamePrefix == null || instanceNamePrefix.equals("")) { + equipName = uuid; + } else { + equipName = instanceNamePrefix + "-" + uuid; + } + return equipName; + } + + @Override + public void unregisterNicInGloboNetwork(NicProfile nic, VirtualMachineProfile vm) { + + String msg = "Unable to unregister nic " + nic + " from VM " + vm + "."; + if (vm == null || nic == null) { + throw new CloudRuntimeException(msg + " Invalid nic or virtual machine."); + } + + Long equipmentGroup = GloboNetworkVmEquipmentGroup.value(); + if (equipmentGroup == null) { + throw new CloudRuntimeException(msg + " Invalid equipment group for VM. Check your GloboNetwork global options."); + } + + UnregisterEquipmentAndIpInGloboNetworkCommand cmd = new UnregisterEquipmentAndIpInGloboNetworkCommand(); + if (nic.getIPv4Address() != null) { + cmd.setNicIp(nic.getIPv4Address()); + cmd.setIsv6(false); + } else { + cmd.setNicIp(nic.getIPv6Address()); + cmd.setIsv6(true); + } + cmd.setVmName(getEquipNameFromUuid(vm.getUuid())); + + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(nic.getNetworkId()); + if (globoNetworkNetworkVO != null) { + cmd.setEnvironmentId(globoNetworkNetworkVO.getGloboNetworkEnvironmentId()); + } + + Answer answer = this.callCommand(cmd, vm.getVirtualMachine().getDataCenterId()); + if (answer == null || !answer.getResult()) { + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + } + + @Override + public boolean removeVmFromLoadBalancer(VirtualMachineProfile vm) { + return _lbMgr.removeVmFromLoadBalancers(vm.getId()); + } + + @Override + public List listGloboNetworkVips(Long projectId) { + + Account caller = CallContext.current().getCallingAccount(); + List permittedAccounts = new ArrayList(); + + permittedAccounts.add(caller.getId()); + + if (projectId != null) { + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by specified id"); + } + if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) { + // getProject() returns type ProjectVO. + InvalidParameterValueException ex = new InvalidParameterValueException("Account " + caller + " cannot access specified project id"); + ex.addProxyObject(project.getUuid(), "projectId"); + throw ex; + } + + permittedAccounts.add(project.getProjectAccountId()); + } + + // FIXME Improve this search by creating a custom criteria + List zones = _dcDao.listAllZones(); + List allowedNetworks = new ArrayList(); + for (DataCenterVO zone : zones) { + for (Long accountId : permittedAccounts) { + allowedNetworks.addAll(_ntwkDao.listNetworksByAccount(accountId, zone.getId(), Network.GuestType.Shared, false)); + } + } + + List networkIds = new ArrayList(); + for (NetworkVO networkVO : allowedNetworks) { + networkIds.add(networkVO.getId()); + } + + if (networkIds.isEmpty()) { + return new ArrayList(); + } + + // Get all vip Ids related to networks + List globoNetworkVipAccList = _globoNetworkVipAccDao.listByNetworks(networkIds); + + Map vips = new HashMap(); + for (GloboNetworkVipAccVO globoNetworkVipAcc : globoNetworkVipAccList) { + + Network network = _ntwkDao.findById(globoNetworkVipAcc.getNetworkId()); + + GloboNetworkVipResponse vip; + + if (vips.get(globoNetworkVipAcc.getGloboNetworkVipId()) == null) { + + // Vip is not in the returning map yet, get all info from GloboNetwork + boolean isv6 = this.isNetworkv6(network); + + GetVipInfoFromGloboNetworkCommand cmd = new GetVipInfoFromGloboNetworkCommand(globoNetworkVipAcc.getGloboNetworkVipId(), isv6); + Answer answer = this.callCommand(cmd, network.getDataCenterId()); + String msg = "Could not list VIPs from GloboNetwork"; + if (answer == null || !answer.getResult()) { + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + vip = ((GloboNetworkVipResponse)answer); + + vips.put(globoNetworkVipAcc.getGloboNetworkVipId(), vip); + + } else { + // Vip is already in the returning map + vip = vips.get(globoNetworkVipAcc.getGloboNetworkVipId()); + } + + if (vip.getNetworkIds() == null) { + vip.setNetworkIds(new ArrayList()); + } + vip.getNetworkIds().add(network.getUuid()); + + } + return new ArrayList(vips.values()); + } + + @Override + public List listGloboNetworkReals(Long vipId) { + if (vipId == null) { + throw new InvalidParameterValueException("Invalid VIP id"); + } + + List globoNetworkVips = _globoNetworkVipAccDao.findByVipId(vipId); + + if (globoNetworkVips == null) { + throw new CloudRuntimeException("Could not find VIP " + vipId); + } + + List reals = new ArrayList(); + + if (globoNetworkVips.isEmpty()) { + return reals; + } + + // We need a network to call commands, any network associated to this VIP will do + Network network = _ntwkDao.findById(globoNetworkVips.get(0).getNetworkId()); + + if (network == null) { + throw new CloudRuntimeException("Could not find network with networkId " + globoNetworkVips.get(0).getNetworkId()); + } + + boolean isv6 = this.isNetworkv6(network); + + GetVipInfoFromGloboNetworkCommand cmd = new GetVipInfoFromGloboNetworkCommand(vipId, isv6); + Answer answer = this.callCommand(cmd, network.getDataCenterId()); + String msg = "Could not find VIP from GloboNetwork"; + if (answer == null || !answer.getResult()) { + msg = answer == null ? msg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + GloboNetworkVipResponse vip = ((GloboNetworkVipResponse)answer); + + for (Real real : vip.getReals()) { + for (GloboNetworkVipAccVO globoNetworkVipVO : globoNetworkVips) { + network = _ntwkDao.findById(globoNetworkVipVO.getNetworkId()); + + // If real's IP is not within network range, skip it + if (isv6) { + if (!NetUtils.isIp6InNetwork(real.getIp(), network.getCidr())); { + continue; + } + } else { + if (!NetUtils.isIpWithInCidrRange(real.getIp(), network.getCidr())) { + continue; + } + } + + VMInstanceVO realVM = _vmDao.findByUuid(real.getVmName()); + if (realVM != null) { + Nic nic = _nicDao.findByNtwkIdAndInstanceId(globoNetworkVipVO.getNetworkId(), realVM.getId()); + if (nic != null) { + real.setNic(String.valueOf(nic.getId())); + real.setNetwork(network.getName()); + + // User VM name rather than UUID + real.setVmName(realVM.getHostName()); + } + } + } + + reals.add(real); + } + + return reals; + } + + @Override + public String getConfigComponentName() { + return GloboNetworkManager.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] {GloboNetworkVIPServerUrl, GloboNetworkConnectionTimeout, GloboNetworkLBLockTimeout, GloboNetworkReadTimeout, GloboNetworkNumberOfRetries, + GloboNetworkVmEquipmentGroup, GloboNetworkModelVmUser, GloboNetworkModelVmDomainRouter, GloboNetworkModelVmConsoleProxy, GloboNetworkModelVmSecondaryStorageVm, + GloboNetworkModelVmElasticIpVm, GloboNetworkModelVmElasticLoadBalancerVm, GloboNetworkModelVmInternalLoadBalancerVm, GloboNetworkModelVmUserBareMetal, + GloboNetworkDomainSuffix, GloboNetworkDomainPattern, GloboNetworkLBAllowedSuffixes, GloboNetworkRegion}; + } + + protected PortableIpRange getPortableIpRange(Long zoneId, Integer vlanNumber, String networkCidr, String networkGateway) throws ResourceAllocationException, + ConcurrentOperationException, InvalidParameterValueException, InsufficientCapacityException { + + // There is no relationship between zone and regin yet + Integer regionId = 1; + for (PortableIpRange portableIpRange : _portableIpRangeDao.listByRegionId(regionId)) { + // compare only gateway because vlan doesn't matters for public ip range + if (portableIpRange.getGateway().equals(networkGateway)) { + return portableIpRange; + } + } + return null; + } + + protected PortableIpRangeVO createPortableIpRange(Long zoneId, Integer vlanNumber, String networkCidr, String networkGateway) throws ResourceAllocationException, + ResourceUnavailableException, ConcurrentOperationException, InvalidParameterValueException, InsufficientCapacityException { + // FIXME! Portable IP and Portable IP Range are not IPv6 ready + + // There is not relationship between zoneId and region yet. + //DataCenter dc = _dcDao.findById(zoneId); + Integer regionId = 1; + + String vlan = BroadcastDomainType.Vlan.toUri(vlanNumber).toString(); + String networkMask = NetUtils.getCidrNetmask(networkCidr); + String networkAddress = NetUtils.getCidr(networkCidr).first(); + long size = NetUtils.getCidrSize(networkMask); + String startIP = NetUtils.getIpRangeStartIpFromCidr(networkAddress, size); + String endIP = NetUtils.getIpRangeEndIpFromCidr(networkAddress, size); + + // shift start ips by NUMBER_OF_RESERVED_IPS_FROM_START and NUMBER_OF_RESERVED_IPS_BEFORE_END + startIP = NetUtils.long2Ip(NetUtils.ip2Long(startIP) + NUMBER_OF_RESERVED_IPS_FOR_LB_FROM_START); + endIP = NetUtils.long2Ip(NetUtils.ip2Long(endIP) - NUMBER_OF_RESERVED_IPS_FOR_LB_BEFORE_END); + + PortableIpRangeVO portableIpRange = _configMgr.createPortableIpRange(regionId, startIP, endIP, networkGateway, networkMask, vlan); + return portableIpRange; + } + + @Override + public PublicIp acquireLbIp(final Long networkId, Long projectId, Long lbEnvironmentId) throws ResourceAllocationException, ResourceUnavailableException, + ConcurrentOperationException, InvalidParameterValueException, InsufficientCapacityException { + + // First of all, check user permission + final Account caller = CallContext.current().getCallingAccount(); + + final Network network; + if (networkId != null) { + network = _ntwkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Unable to find a network having the specified network id"); + } + } else { + throw new InvalidParameterValueException("Invalid networkId: " + networkId); + } + // Perform account permission check on network + _accountMgr.checkAccess(caller, null, true, network); + + // If project was set, this IP belongs to that project + // Otherwise, the caller is the owner + final Account owner; + if (projectId != null) { + Project project = _projectMgr.getProject(projectId); + if (project != null) { + owner = _accountMgr.getAccount(project.getProjectAccountId()); + } else { + throw new InvalidParameterValueException("Could not find project with id " + projectId); + } + } else { + owner = caller; + } + + // Retrieve glbEnvId from network + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(networkId); + if (globoNetworkNetworkVO == null) { + throw new InvalidParameterValueException("Unable to find mapping for networkId " + networkId); + } + Long glbEnvId = globoNetworkNetworkVO.getGloboNetworkEnvironmentId(); + + // Retrieve napiEnvironment from DB + GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(network.getPhysicalNetworkId(), glbEnvId); + + if (globoNetworkEnvironment == null) { + // No physical network/environment pair registered in the database. + throw new InvalidParameterValueException("Unable to find a relationship between physical network=" + network.getPhysicalNetworkId() + " and GloboNetwork environment=" + + glbEnvId); + } + + final GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironment = _globoNetworkLBEnvironmentDao.findById(lbEnvironmentId); + if (globoNetworkLBEnvironment == null) { + throw new InvalidParameterValueException("Could not find LB network " + lbEnvironmentId); + } + + boolean isv6 = this.isNetworkv6(network); + + Long gnLoadBalancerEnvironment = globoNetworkLBEnvironment.getGloboNetworkLoadBalancerEnvironmentId(); + AcquireNewIpForLbCommand cmd = new AcquireNewIpForLbCommand(gnLoadBalancerEnvironment, isv6); + String description = getLbIpDescription(projectId, owner); + cmd.setDescription(description); + + final GloboNetworkAndIPResponse globoNetwork = (GloboNetworkAndIPResponse)this.callCommand(cmd, network.getDataCenterId()); + + try { + // FIXME! PublicIp class is not IPv6 ready + PublicIp publicIp = Transaction.execute(new TransactionCallbackWithException() { + + @Override + public PublicIp doInTransaction(TransactionStatus status) throws CloudException { + Long zoneId = network.getDataCenterId(); + + // ensure portable ip range exists + String networkGateway = GloboNetworkManager.this.getNetworkGateway(globoNetwork.getNetworkAddress(), globoNetwork.getNetworkMask(), globoNetwork.isv6()); + PortableIpRange portableIpRange = getPortableIpRange(zoneId, globoNetwork.getVlanNum(), globoNetwork.getNetworkCidr(), networkGateway); + if (portableIpRange == null) { + portableIpRange = createPortableIpRange(zoneId, globoNetwork.getVlanNum(), globoNetwork.getNetworkCidr(), networkGateway); + } + + IPAddressVO ip = (IPAddressVO)_ipAddrMgr.allocatePortableIp(owner, caller, zoneId, networkId, null, globoNetwork.getIp()); + + GloboNetworkIpDetailVO gnIpDetail = new GloboNetworkIpDetailVO(ip.getId(), globoNetwork.getIpId()); + gnIpDetail.setGloboNetworkEnvironmentRefId(globoNetworkLBEnvironment.getId()); + _globoNetworkIpDetailDao.persist(gnIpDetail); + + VlanVO vlan = _vlanDao.findById(ip.getVlanId()); + return PublicIp.createFromAddrAndVlan(ip, vlan); + } + }); + return publicIp; + + } catch (CloudException e) { + // Exception when allocating new IP in Cloudstack. Roll back transaction in GloboNetwork + s_logger.error("[LB_PORTABLE_IP] Reverting IP allocation in GloboNetwork due to error allocating IP", e); + ReleaseIpFromGloboNetworkCommand cmdRelease = new ReleaseIpFromGloboNetworkCommand(globoNetwork.getIp(), gnLoadBalancerEnvironment, globoNetwork.isv6()); + this.callCommand(cmdRelease, network.getDataCenterId()); + throw new ResourceAllocationException(e.getLocalizedMessage(), ResourceType.public_ip); + } + } + + private String getLbIpDescription(Long projectId, Account owner) { + SimpleDateFormat format = new SimpleDateFormat("dd-MM-YYYY"); + String id = projectId != null ? projectId.toString() : ""; + return "ACS_LB_IP_" + GloboNetworkRegion.value()+ "_A"+ owner.getId() + "_P" + id + "_" + format.format(new Date()); + } + + private String getNetworkGateway(String network, String mask, boolean isv6) { + if (network == null || mask == null) { + return null; + } + + if (isv6) { + com.googlecode.ipv6.IPv6Network ipv6Network = com.googlecode.ipv6.IPv6Network.fromAddressAndMask( + com.googlecode.ipv6.IPv6Address.fromString(network), + com.googlecode.ipv6.IPv6NetworkMask.fromAddress(com.googlecode.ipv6.IPv6Address.fromString(mask))); + return ipv6Network.getFirst().toString(); + } else { + return NetUtils.getIpRangeStartIpFromCidr(network, NetUtils.getCidrSize(mask)); + } + } + + @Override + public boolean disassociateIpAddrFromGloboNetwork(final long ipId) { + final IPAddressVO ipVO = _ipAddrDao.findById(ipId); + final UserIpv6AddressVO ipv6VO = _ipv6AddrDao.findById(ipId); + if (ipVO == null && ipv6VO == null) { + throw new InvalidParameterValueException("Unable to find ip address with id=" + ipId); + } + + final boolean isv6 = ipv6VO != null; + + try { + boolean result = Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { + + @Override + public void doInTransactionWithoutResult(TransactionStatus status) throws CloudException { + GloboNetworkIpDetailVO gnIpDetail = _globoNetworkIpDetailDao.findByIp(ipId); + + _ipAddrMgr.releasePortableIpAddress(ipId); // FIXME This will not deallocate portable IPv6 ips. PortableIp is not IPv6 ready + + String ipAddress = isv6 ? ipv6VO.getAddress() : ipVO.getAddress().addr(); + long dataCenterId = isv6 ? ipv6VO.getDataCenterId() : ipVO.getDataCenterId(); + + if (gnIpDetail != null && gnIpDetail.getGloboNetworkEnvironmentRefId() != null) { + GloboNetworkLoadBalancerEnvironment gnLbNetworkVO = _globoNetworkLBEnvironmentDao.findById(gnIpDetail.getGloboNetworkEnvironmentRefId()); + if (gnLbNetworkVO == null) { + throw new InvalidParameterValueException("Could not find mapping between lb environment " + gnIpDetail.getGloboNetworkEnvironmentRefId()); + } + + ReleaseIpFromGloboNetworkCommand cmdRelease = new ReleaseIpFromGloboNetworkCommand(ipAddress, gnLbNetworkVO + .getGloboNetworkLoadBalancerEnvironmentId(), isv6); + GloboNetworkManager.this.callCommand(cmdRelease, dataCenterId); + _globoNetworkIpDetailDao.remove(gnIpDetail.getId()); + } else { + s_logger.warn("Ip " + ipAddress + " is not associate with GloboNetwork"); + } + } + }); + + return result; + } catch (CloudException e) { + throw new CloudRuntimeException("Could not disassociate IP address", e); + } + } + + @Override + public boolean applyLbRuleInGloboNetwork(final Network network, final LoadBalancingRule rule) throws ResourceUnavailableException { + if (network == null || rule == null) { + return false; + } + + String lockName = "globonetworklb-" + rule.getSourceIp().addr(); + final ReentrantLock lock = GlobalLock.getReentrantLock(lockName); + try { + if (!lock.tryLock(GloboNetworkLBLockTimeout.value(), TimeUnit.SECONDS)) { + throw new ResourceUnavailableException(String.format("Failed to acquire lock for load balancer %s", rule.getUuid()), DataCenter.class, network.getDataCenterId()); + } + + IPAddressVO ipAddress = _ipAddrDao.findByIpAndNetworkId(rule.getNetworkId(), rule.getSourceIp().addr()); + if (ipAddress == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " is not associate with network " + rule.getNetworkId()); + } + + GloboNetworkIpDetailVO globoNetworkIpDetail = _globoNetworkIpDetailDao.findByIp(ipAddress.getId()); + if (globoNetworkIpDetail == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " (id " + ipAddress.getId() + ") is not associate with globo network"); + } else if (globoNetworkIpDetail.getGloboNetworkEnvironmentRefId() == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " (id " + ipAddress.getId() + ") can't be used to load balancing"); + } + + GloboNetworkLoadBalancerEnvironment lbEnvironment = _globoNetworkLBEnvironmentDao.findById(globoNetworkIpDetail.getGloboNetworkEnvironmentRefId()); + if (lbEnvironment == null) { + throw new InvalidParameterValueException("Could not find mapping between lb environment " + globoNetworkIpDetail.getGloboNetworkEnvironmentRefId()); + } + + Account account = _accountMgr.getAccount(network.getAccountId()); + + if (rule.getState().equals(FirewallRule.State.Revoke)) { + removeLBFromGloboNetwork(network, rule, globoNetworkIpDetail); + } else { + saveLBInGloboNetwork(network, rule, account, globoNetworkIpDetail, lbEnvironment); + } + + } catch (CloudstackGloboNetworkException ex) { + String context = CallContext.current().getNdcContext(); + String integrationContext = ex.getContext(); + String msg = "Integration problem with NetworkAPI, please contact your system administrator. Context: " + context + ", Integration context: " + integrationContext + ", name=" + rule.getName(); + throw new UserCloudRuntimeException(msg); + } catch (InvalidParameterValueException e){ + throw new UserCloudRuntimeException(e.getMessage(), e); + } catch (Exception e) { + // Convert all exceptions to ResourceUnavailable to user have feedback of what happens. All others exceptions only show 'error' + s_logger.error("[load balancer "+ rule.getName()+ "] Error applying load balancer rules. Uuid: " + rule.getUuid(), e); + String context = CallContext.current().getNdcContext(); + throw new UserCloudRuntimeException("Error applying load balancer. Context: "+ context+ ", lb name=" + rule.getName(), e); + } finally { + if (lock != null && lock.isHeldByCurrentThread()) { + lock.unlock(); + } + GlobalLock.releaseReentrantLock(lockName); + } + + return true; + } + + private void saveLBInGloboNetwork(Network network, LoadBalancingRule rule, Account account, GloboNetworkIpDetailVO globoNetworkIpDetail, GloboNetworkLoadBalancerEnvironment lbEnvironment) throws ResourceUnavailableException { + if(rule.isDsr()){ + validateEnvironmentSupportsDSR(network, lbEnvironment); + } + validateSticknessPolicy(rule); + validateHealthCheckPolicies(rule); + List ports = getServicePorts(rule); + List realList = getReals(rule.getDestinations(), ports); + + if (isDnsProviderEnabledFor(network)) { + manageLoadBalancerDomainNameRegistry(network, rule); + } else { + s_logger.warn("Creating Load Balancer without registering DNS because network offering does not have GloboDNS as provider"); + } + + final ApplyVipInGloboNetworkCommand cmd = createApplyVipInGloboNetworkCommand(rule, account, globoNetworkIpDetail, lbEnvironment, ports, realList); + + GloboNetworkVipResponse answer = (GloboNetworkVipResponse) this.callCommand(cmd, network.getDataCenterId()); + if (globoNetworkIpDetail.getGloboNetworkVipId() == null) { + globoNetworkIpDetail.setGloboNetworkVipId(answer.getId()); + _globoNetworkIpDetailDao.persist(globoNetworkIpDetail); + } + } + + private void validateEnvironmentSupportsDSR(Network network, GloboNetworkLoadBalancerEnvironment lbEnvironment) { + CheckDSREnabled cmd = new CheckDSREnabled(lbEnvironment.getGloboNetworkLoadBalancerEnvironmentId()); + CheckDSREnabledResponse answer = (CheckDSREnabledResponse) this.callCommand(cmd, network.getDataCenterId()); + if(!answer.getResult() || !answer.isDsrEnabled()){ + throw new InvalidParameterValueException(answer.getDetails()); + } + } + + protected void validateSticknessPolicy(LoadBalancingRule rule) { + if (rule.getStickinessPolicies() == null || rule.getStickinessPolicies().size() > 1) { + throw new InvalidParameterValueException("Invalid stickness policy, list should contain only one"); + } + } + + protected void validateHealthCheckPolicies(LoadBalancingRule rule) { + if (rule.getHealthCheckPolicies() != null) { + int numberOfpolicies = 0; + for (LbHealthCheckPolicy healthcheckpolicy : rule.getHealthCheckPolicies()) { + if (!healthcheckpolicy.isRevoked()) { + numberOfpolicies++; + } + } + if (numberOfpolicies > 1) { + throw new InvalidParameterValueException("Invalid healthcheck policy, list should contain at maximum one"); + } + } + } + + protected List getServicePorts(LoadBalancingRule rule) { + List ports = new ArrayList<>(); + ports.add(rule.getSourcePortStart() + ":" + rule.getDefaultPortStart()); + if (rule.getAdditionalPortMap() != null) { + ports.addAll(rule.getAdditionalPortMap()); + } + return ports; + } + + protected List getReals(List destinations, List ports) { + List realList = new ArrayList<>(); + if(destinations != null) { + for (LbDestination destination : destinations) { + VMInstanceVO vm = _vmDao.findById(destination.getInstanceId()); + if (vm != null) { + Real real = new Real(); + real.setIp(destination.getIpAddress()); + real.setVmName(getEquipNameFromUuid(vm.getUuid())); + real.setPorts(ports); + real.setRevoked(destination.isRevoked()); + + GloboNetworkNetworkVO destinationNetwork = _globoNetworkNetworkDao.findByNetworkId(destination.getNetworkId()); + if (destinationNetwork == null) { + throw new InvalidParameterValueException("Could not obtain mapping for network " + destination.getNetworkId() + " and VM " + destination.getInstanceId() + " in GloboNetwork."); + } + real.setEnvironmentId(destinationNetwork.getGloboNetworkEnvironmentId()); + realList.add(real); + } else { + throw new InvalidParameterValueException("Could not find VM with id " + destination.getInstanceId()); + } + } + } + return realList; + } + + protected ApplyVipInGloboNetworkCommand createApplyVipInGloboNetworkCommand(LoadBalancingRule rule, Account account, GloboNetworkIpDetailVO globoNetworkIpDetail, GloboNetworkLoadBalancerEnvironment lbEnvironment, List ports, List realList) { + final ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setRegion(GloboNetworkRegion.value()); + buildHealthcheck(cmd, rule); + + cmd.setVipId(globoNetworkIpDetail.getGloboNetworkVipId()); // Vip Id null means new vip, otherwise vip will be updated. + cmd.setHost(rule.getName()); + cmd.setCache(rule.getCache()); + cmd.setServiceDownAction(rule.getServiceDownAction()); + cmd.setDsr(rule.isDsr()); + cmd.setHealthCheckDestination(rule.getHealthcheckDestination()); + cmd.setIpv4(rule.getSourceIp().addr()); + cmd.setVipEnvironmentId(lbEnvironment.getGloboNetworkLoadBalancerEnvironmentId()); + cmd.setPorts(ports); + cmd.setBusinessArea(account.getAccountName()); + cmd.setServiceName(rule.getName()); + cmd.setMethodBal(rule.getAlgorithm()); + cmd.setPersistencePolicy(rule.getStickinessPolicies() == null || rule.getStickinessPolicies().isEmpty() ? null : rule.getStickinessPolicies().get(0)); + cmd.setRuleState(rule.getState()); + cmd.setRealList(realList); + return cmd; + } + + private void removeLBFromGloboNetwork(Network network, LoadBalancingRule rule, GloboNetworkIpDetailVO globoNetworkIpDetail) throws ResourceUnavailableException { + if (isDnsProviderEnabledFor(network)) { + manageLoadBalancerDomainNameRegistry(network, rule); + } else { + s_logger.warn("Creating Load Balancer without registering DNS because network offering does not have GloboDNS as provider"); + } + + final RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + cmd.setVipId(globoNetworkIpDetail.getGloboNetworkVipId()); + cmd.setKeepIp(true); + _globoResourceConfigurationDao.removeConfigurations(rule.getUuid(), GloboResourceType.LOAD_BALANCER); + this.callCommand(cmd, network.getDataCenterId()); + + globoNetworkIpDetail.setGloboNetworkVipId(null); + _globoNetworkIpDetailDao.persist(globoNetworkIpDetail); + } + + private void buildHealthcheck(ApplyVipInGloboNetworkCommand cmd, LoadBalancingRule rule) { + LbHealthCheckPolicy lbHealthCheckPolicy = rule.getHealthCheckPolicies() == null || rule.getHealthCheckPolicies().isEmpty() ? null : rule.getHealthCheckPolicies().get(0); + String healthcheck = lbHealthCheckPolicy != null ? lbHealthCheckPolicy.getpingpath() : null; + + HealthCheckHelper healthcheckBuilder = HealthCheckHelper.build(rule.getName(), rule.getHealthCheckType(), healthcheck, rule.getExpectedHealthCheck()); + + if (healthcheckBuilder.getExpectedHealthCheck() != null && !healthcheckBuilder.getExpectedHealthCheck().equals(rule.getExpectedHealthCheck()) || + healthcheckBuilder.getHealthCheckType() != null && !healthcheckBuilder.getHealthCheckType().equals(rule.getHealthCheckType()) + ) { + s_logger.info("ExpectedHealthcheck Actual: " + cmd.getExpectedHealthcheck() + " Old " + rule.getExpectedHealthCheck()); + s_logger.info("Type Actual: " + cmd.getHealthcheckType() + " Old " + rule.getHealthCheckType()); + + List lbOptions = _lbOptionsDao.listByLoadBalancerId(rule.getId()); + if (lbOptions.size() > 0) { + for (LoadBalancerOptionsVO lbOpt : lbOptions) { + if (lbOpt.getLoadBalancerId() == rule.getId()) { + lbOpt.setExpectedHealthCheck(healthcheckBuilder.getExpectedHealthCheck()); + lbOpt.setHealthCheckType(healthcheckBuilder.getHealthCheckType()); + _lbOptionsDao.persist(lbOpt); + } + } + } + } + + cmd.setExpectedHealthcheck(healthcheckBuilder.getExpectedHealthCheck()); + cmd.setHealthcheckType(healthcheckBuilder.getHealthCheckType()); + cmd.setHealthcheck(healthcheckBuilder.getHealthCheck()); + } + + private boolean isDnsProviderEnabledFor(Network network) { + return _networkManager.isProviderForNetwork(Provider.GloboDns, network.getId()) && + _networkManager.isProviderEnabledInPhysicalNetwork(network.getPhysicalNetworkId(), Provider.GloboDns.getName()); + } + + public void registerLoadbalancerDomainName(String resourceUuId) { + LoadBalancerVO lb = this._loadBalancerDao.findByUuid(resourceUuId); + + Network network = _ntwkDao.findById(lb.getNetworkId()); + Long sourceIpAddressId = lb.getSourceIpAddressId(); + IPAddressVO ipAddress = _ipAddrDao.findById(sourceIpAddressId); + String lbDomain = getLbDomain(lb.getName()); + String lbRecord = getLbRecord(lb.getName(), lbDomain); + Ip ip = ipAddress.getAddress(); + try { + GloboDnsTO globoDns = new GloboDnsTO(network.getDataCenterId(), lb.getUuid(), lbRecord, lbDomain, ip.addr()); + _globoDnsService.createDnsRecordForLoadBalancer(globoDns, false); + + } catch (Exception ex) { + s_logger.error("Error while registering load balancer's domain name", ex); + throw new CloudRuntimeException("Error while registering load balancer's domain name", ex); + } + } + + @Override + public void registerDnsForResource(String uuid, GloboResourceType resourceType) { + if (GloboResourceType.LOAD_BALANCER.equals(resourceType)){ + registerLoadbalancerDomainName(uuid); + } else if (GloboResourceType.VM_NIC.equals(resourceType)) { + registerNicVmDomain(uuid); + } + } + + private void registerNicVmDomain(String uuid) { + NicVO nic = _nicDao.findByUuid(uuid); + UserVmVO vm = _userVmDao.findById(nic.getInstanceId()); + Network network = _networkService.getNetwork(nic.getNetworkId()); + + s_logger.debug("VM : " + vm.getHostName() + "nic: " + nic.getIPv4Address()); + + boolean isIpv6 = nic.getIPv4Address() == null; + String ipAddress = isIpv6 ? nic.getIPv6Address() : nic.getIPv4Address(); + + + boolean forceDomainRegister = true; //here user is forcing to register, so if fail, the user should get exception + boolean result = _globoDnsService.registerVmDomain(network.getDataCenterId(), nic.getUuid(), + vm.getHostName(), ipAddress, network.getNetworkDomain(), isIpv6, forceDomainRegister); + + if (!result) { + throw new CloudRuntimeException("Error during register vm domain for nic " + uuid + ". Contact the System Administrator."); + } + + } + + @Override + public GloboResourceConfigurationVO getGloboResourceConfiguration(String uuid, GloboResourceType resourceType, GloboResourceKey key) { + + List globoConfigurations = _globoResourceConfigurationDao.getConfiguration(resourceType, uuid, key); + if (globoConfigurations.size() > 0) { + return globoConfigurations.get(0); + } + + return null; + } + + public List getGloboResourceConfigs(String uuid, GloboResourceType resourceType) { + List configs = _globoResourceConfigurationDao.getConfiguration(resourceType, uuid); + return configs; + } + + protected void manageLoadBalancerDomainNameRegistry(Network network, LoadBalancingRule rule) throws ResourceUnavailableException { + try { + GloboResourceConfigurationVO skipDnsErrorCmd = _globoResourceConfigurationDao.getFirst(GloboResourceType.LOAD_BALANCER, rule.getUuid(), GloboResourceKey.skipDnsError); + boolean skipDnsError = skipDnsErrorCmd != null ? skipDnsErrorCmd.getBooleanValue() : false; + + // First of all, find the correct LB domain and LB record + String lbDomain = getLbDomain(rule.getName()); + + if (lbDomain == null) { + throw new ResourceUnavailableException("Load balancer name/domain is invalid", DataCenter.class, network.getDataCenterId()); + } + + String lbRecord = getLbRecord(rule.getName(), lbDomain); + + if ((rule.getState() == FirewallRule.State.Add || rule.getState() == FirewallRule.State.Active)) { + if (_globoDnsService.validateDnsRecordForLoadBalancer(lbDomain, lbRecord, rule.getSourceIp().addr(), network.getDataCenterId(), skipDnsError)) { + GloboDnsTO globoDns = new GloboDnsTO(network.getDataCenterId(), rule.getUuid(), lbRecord, lbDomain, rule.getSourceIp().addr()); + _globoDnsService.createDnsRecordForLoadBalancer(globoDns, skipDnsError); + } + } else if (rule.getState() == FirewallRule.State.Revoke) { + _globoDnsService.removeDnsRecordForLoadBalancer(rule.getUuid(), lbDomain, lbRecord, rule.getSourceIp().addr(), network.getDataCenterId()); + } + } catch(Exception ex){ + s_logger.error("Error while registering load balancer's domain name", ex); + throw new CloudRuntimeException("Error while registering load balancer's domain name", ex); + } + } + + protected String getLbDomain(String lbFullName) { + List allowedDomains = listAllowedLbSuffixes(); + String lbDomain = null; + for (String allowedDomain : allowedDomains) { + // Remove any whitespaces + allowedDomain = allowedDomain.trim(); + // Insert the '.' before the domain, if it's not there yet + allowedDomain = allowedDomain.startsWith(".") ? allowedDomain : "." + allowedDomain; + if (lbFullName.endsWith(allowedDomain)) { + lbDomain = allowedDomain.substring(1); // Remove '.' again for later use in GloboDNS provider + break; + } + } + return lbDomain; + } + + private String getLbRecord(String fullLbName, String lbDomain) { + return fullLbName.substring(0, fullLbName.indexOf(lbDomain) - 1); // -1 is to remove '.' between record and domain + } + + @Override + public boolean validateLBRule(Network network, LoadBalancingRule rule) { + // Validate params + if (network == null || rule == null) { + return false; + } + + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(network.getId()); + if (globoNetworkNetworkVO == null) { + throw new InvalidParameterValueException("GloboNetwork Load Balancer work only with networks managed by GloboNetwork. NetworkId=" + network.getId()); + } + + // check additional networks + if (rule.getAdditionalNetworks() != null) { + for (Long networkId : rule.getAdditionalNetworks()) { + GloboNetworkNetworkVO additionalGNNetworkVO = _globoNetworkNetworkDao.findByNetworkId(networkId); + if (additionalGNNetworkVO == null) { + throw new InvalidParameterValueException("GloboNetwork Load Balancer work only with networks managed by GloboNetwork. NetworkId=" + network.getId()); + } + } + } + + this.validatePortMaps(rule); + + IPAddressVO ipVO = _ipAddrDao.findByIpAndNetworkId(rule.getNetworkId(), rule.getSourceIp().addr()); + if (ipVO == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " is not associate with network " + rule.getNetworkId()); + } + + GloboNetworkIpDetailVO gnIpDetail = _globoNetworkIpDetailDao.findByIp(ipVO.getId()); + if (gnIpDetail == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " (id " + ipVO.getId() + ") is not associate with globo network"); + } else if (gnIpDetail.getGloboNetworkEnvironmentRefId() == null) { + throw new InvalidParameterValueException("Ip " + rule.getSourceIp().addr() + " (id " + ipVO.getId() + ") can't be used to load balancing"); + } + + // Stickness/Persistence + if (rule.getStickinessPolicies() != null && rule.getStickinessPolicies().size() > 1) { + throw new InvalidParameterValueException("Invalid stickness policy, list should contain only one"); + } + + // Healthcheck + if (rule.getHealthCheckPolicies() != null && rule.getHealthCheckPolicies().size() > 1) { + throw new InvalidParameterValueException("Invalid healthcheck policy, list should contain only one"); + } + + // Get VIP info + if (gnIpDetail.getGloboNetworkVipId() != null) { + ValidateVipUpdateCommand cmd = new ValidateVipUpdateCommand(gnIpDetail.getGloboNetworkVipId(), rule.getName(), rule.getSourceIp().addr()); + Answer answer = this.callCommand(cmd, network.getDataCenterId(), false); + if(!answer.getResult()){ + throw new InvalidParameterValueException(answer.getDetails()); + } + } + + if (isDnsProviderEnabledFor(network)) { + // First of all, find the correct LB domain and LB record + String lbDomain = getLbDomain(rule.getName()); + + if (lbDomain == null) { + // That means there was no match + // LB cannot be created + throw new InvalidParameterValueException("Load balancer name " + rule.getName() + " is not in the allowed list for domains."); + } + + // Finally, validate LB record in GloboDNS + String lbRecord = getLbRecord(rule.getName(), lbDomain); + + return _globoDnsService.validateDnsRecordForLoadBalancer(lbDomain, lbRecord, rule.getSourceIp().addr(), network.getDataCenterId(), rule.isSkipDnsError()); + } else { + s_logger.warn("Allowing creation of Load Balancer without registering DNS because network offering does not have GloboDNS as provider"); + } + + return true; + } + + @Override + public List listGloboNetworkLBCacheGroups(Long lbEnvironmentIdRef, Long networkId) { + if (lbEnvironmentIdRef == null) { + throw new InvalidParameterValueException("Invalid LB Environment ID"); + } + + GloboNetworkLoadBalancerEnvironment gnLbEnv = _globoNetworkLBEnvironmentDao.findById(lbEnvironmentIdRef); + if (gnLbEnv == null) { + throw new InvalidParameterValueException("Could not find mapping between lb environment " + gnLbEnv.getGloboNetworkEnvironmentRefId()); + } + + if (networkId == null) { + throw new InvalidParameterValueException("Invalid Network ID"); + } + + Network network = _networkManager.getNetwork(networkId); + if (network == null) { + throw new InvalidParameterValueException("Cannot find network with ID : " + networkId); + } + + ListGloboNetworkLBCacheGroupsCommand cmd = new ListGloboNetworkLBCacheGroupsCommand(); + cmd.setLBEnvironmentId(gnLbEnv.getGloboNetworkLoadBalancerEnvironmentId()); + + Answer answer = callCommand(cmd, network.getDataCenterId()); + + List lbCacheGroups = ((GloboNetworkCacheGroupsResponse)answer).getCacheGroups(); + return lbCacheGroups; + + } + + protected void validatePortMaps(LoadBalancingRule rule) { + List portsAlreadyMapped = new ArrayList<>(); + portsAlreadyMapped.add(rule.getSourcePortStart()); + if (rule.getAdditionalPortMap() != null) { + for (String portMap : rule.getAdditionalPortMap()) { + String[] portMapArray = portMap.split(":"); + if (portMapArray.length != 2) { + throw new InvalidParameterValueException("Additional port mapping is invalid, should be in the form '80:8080,443:8443'"); + } + Integer lbPort; + Integer realPort; + try { + lbPort = Integer.valueOf(portMapArray[0].trim()); + realPort = Integer.valueOf(portMapArray[1].trim()); + }catch(NumberFormatException e){ + throw new InvalidParameterValueException("Additional port mapping is invalid. Only numbers are permitted"); + } + if (portsAlreadyMapped.contains(lbPort)) { + throw new InvalidParameterValueException("Additional port mapping is invalid. Duplicated Load Balancer port"); + } + portsAlreadyMapped.add(lbPort); + } + } + } + + @Override + @DB + public LoadBalancer importGloboNetworkLoadBalancer(Long lbId, final Long networkId, Long projectId) { + // Validate params + if (lbId == null) { + throw new InvalidParameterValueException("Invalid load balancer ID"); + } + if (networkId == null) { + throw new InvalidParameterValueException("Invalid network ID"); + } + final Network network = _ntwkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Could not find a network with id " + networkId); + } + + // First of all, check user permission + final Account caller = CallContext.current().getCallingAccount(); + // Perform account permission check on network + _accountMgr.checkAccess(caller, null, true, network); + + // If project was set, this LB belongs to that project + // Otherwise, the caller is the owner + final Account owner; + if (projectId != null) { + Project project = _projectMgr.getProject(projectId); + if (project != null) { + owner = _accountMgr.getAccount(project.getProjectAccountId()); + } else { + throw new InvalidParameterValueException("Could not find project with id " + projectId); + } + } else { + owner = caller; + } + + // Get VIP info from GloboNetwork + boolean isv6 = this.isNetworkv6(network); + + GetVipInfoFromGloboNetworkCommand cmd = new GetVipInfoFromGloboNetworkCommand(lbId, isv6); + Answer answer = this.callCommand(cmd, network.getDataCenterId()); + + if (answer != null && answer.getResult()) { + final GloboNetworkVipResponse globoNetworkLB = (GloboNetworkVipResponse)answer; + + // Check LB IP address + PortableIpVO portableVO = _portableIpDao.findByIpAddress(globoNetworkLB.getIp()); + if (portableVO == null) { + throw new CloudRuntimeException("Portable IP " + globoNetworkLB.getIp() + " is not registered within Cloudstack"); + } else if (portableVO.getState() != PortableIp.State.Free) { + throw new CloudRuntimeException("IP " + globoNetworkLB.getIp() + " is not free to be used in Cloudstack"); + } + + if (globoNetworkLB.getPorts() == null || globoNetworkLB.getPorts().size() == 0) { + throw new CloudRuntimeException("Invalid port mapping for LB. It is necessary to have at least 1 port mapping for a load balancer"); + } + final String[] globoNetworkPorts = globoNetworkLB.getPorts().get(0).split(":"); + if (globoNetworkPorts[0] == "" || globoNetworkPorts[1] == "") { + throw new CloudRuntimeException("Invalid port mapping for LB: " + globoNetworkLB.getPorts().get(0)); + } + final List additionalPortMapList = (globoNetworkLB.getPorts().size() > 1) ? globoNetworkLB.getPorts().subList(1, globoNetworkLB.getPorts().size()) : null; + + final String algorithm; + if ("least-conn".equals(globoNetworkLB.getMethod())) { + algorithm = "leastconn"; + } else if ("round-robin".equals(globoNetworkLB.getMethod())) { + algorithm = "roundrobin"; + } else { + throw new CloudRuntimeException("Invalid balancing method: " + globoNetworkLB.getMethod()); + } + + final String cache = globoNetworkLB.getCache(); + + final String lbPersistence; + if ("cookie".equals(globoNetworkLB.getPersistence())) { + lbPersistence = "Cookie"; + } else if ("source-ip".equals(globoNetworkLB.getPersistence())) { + lbPersistence = "Source-ip"; + } else if ("source-ip com persist. entre portas".equals(globoNetworkLB.getPersistence())) { + lbPersistence = "Source-ip with persistence between ports"; + } else { + lbPersistence = null; + } + + try { + // Allocate IP in Cloudstack + Long zoneId = network.getDataCenterId(); + IPAddressVO ip = (IPAddressVO)_ipAddrMgr.allocatePortableIp(owner, caller, zoneId, networkId, null, globoNetworkLB.getIp()); + VlanVO vlan = _vlanDao.findById(ip.getVlanId()); + PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, vlan); + + // register ip + // Retrieve glbEnvId from network + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(networkId); + if (globoNetworkNetworkVO == null) { + throw new InvalidParameterValueException("Unable to find mapping for networkId " + networkId); + } + Long glbEnvId = globoNetworkNetworkVO.getGloboNetworkEnvironmentId(); + + // Retrieve napiEnvironment from DB + GloboNetworkEnvironmentVO globoNetworkEnvironment = _globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(network.getPhysicalNetworkId(), + glbEnvId); + + if (globoNetworkEnvironment == null) { + // No physical network/environment pair registered in the database. + throw new InvalidParameterValueException("Unable to find a relationship between physical network=" + network.getPhysicalNetworkId() + + " and GloboNetwork environment=" + glbEnvId); + } + + GloboNetworkLoadBalancerEnvironment globoNetworkLBEnvironment = _globoNetworkLBEnvironmentDao.findByEnvironmentRefAndLBNetwork( + globoNetworkEnvironment.getId(), globoNetworkLB.getLbEnvironmentId()); + if (globoNetworkLBEnvironment == null) { + throw new InvalidParameterValueException("Could not find LB environment " + globoNetworkLB.getLbEnvironmentId()); + } + + GloboNetworkIpDetailVO gnIpDetail = new GloboNetworkIpDetailVO(ip.getId(), globoNetworkLB.getIpId()); + gnIpDetail.setGloboNetworkEnvironmentRefId(globoNetworkLBEnvironment.getId()); + gnIpDetail.setGloboNetworkVipId(globoNetworkLB.getId()); + _globoNetworkIpDetailDao.persist(gnIpDetail); + + // Create LB + LoadBalancer lb = _lbMgr.createPublicLoadBalancer(null, globoNetworkLB.getName().toLowerCase(), globoNetworkLB.getDetails(), Integer.parseInt(globoNetworkPorts[0], 10), + Integer.parseInt(globoNetworkPorts[1], 10), publicIp.getId(), NetUtils.TCP_PROTO, algorithm, false, CallContext.current(), null, Boolean.TRUE, additionalPortMapList, cache, null, null, null, null, true, false); + + // If healthcheck is TCP, do nothing; otherwise, create the healthcheck policy + if (globoNetworkLB.getHealthcheckType() != null && "HTTP".equals(globoNetworkLB.getHealthcheckType())) { + // Default values for timeout and threshold, since those are not used by GloboNetwork + _lbService.validateAndPersistLbHealthcheckPolicy(lb.getId(), globoNetworkLB.getHealthcheck(), null, 2, 5, 2, 1, true); + } + + if (lbPersistence != null) { + _lbService.validateAndPersistLbStickinessPolicy(lb.getId(), lbPersistence, lbPersistence, null, null, true); + } + + // Assign VMs that are managed in Cloudstack + List instancesToAdd = new ArrayList(); + Map> vmIdIpMap = new HashMap>(); + for (GloboNetworkVipResponse.Real real : globoNetworkLB.getReals()) { + // For each real, find it in Cloudstack and add it to LB + // If it's not found, ignore it (VM not managed by Cloudstack) + Nic nic = _nicDao.findByIp4AddressAndNetworkId(real.getIp(), networkId); + if (nic == null) { + continue; + } + instancesToAdd.add(nic.getInstanceId()); + vmIdIpMap.put(nic.getInstanceId(), Arrays.asList(nic.getIPv4Address())); + } + _lbService.assignToLoadBalancer(lb.getId(), instancesToAdd, vmIdIpMap); + + + return lb; + + } catch (CloudException e) { + throw new CloudRuntimeException("There was an error when allocating ip " + globoNetworkLB.getIp() + " in Cloudstack"); + } + } + return null; + } + + @Override + public String getDomainSuffix() { + return GloboNetworkDomainSuffix.value(); + } + + @Override + public boolean isSupportedCustomNetworkDomain() { + return !StringUtils.isNotBlank(GloboNetworkDomainPattern.value()); + } + + @Override + public List getAllZonesThatProviderAreEnabled() { + List zonesEnabled = new ArrayList(); + for (DataCenter dc : _dcDao.listEnabledZones()) { + if (_networkManager.isProviderEnabledInZone(dc.getId(), Provider.GloboNetwork.getName())) { + zonesEnabled.add(dc); + } + } + return zonesEnabled; + } + + private boolean isNetworkv6(Network network) { + return network.getIp6Gateway() != null; + } + + @Override + public List listAllowedLbSuffixes() { + String allowedDomainsOpt = GloboNetworkLBAllowedSuffixes.value(); + List allowedDomains = new ArrayList(); + + for (String allowedDomain : allowedDomainsOpt.trim().split(",")) { + // Remove any whitespaces + allowedDomain = allowedDomain.trim(); + if (!allowedDomain.isEmpty()) { + // Insert the '.' before the domain, if it's not there yet + allowedDomain = allowedDomain.startsWith(".") ? allowedDomain : "." + allowedDomain; + allowedDomains.add(allowedDomain); + } + } + + Collections.sort(allowedDomains, new Comparator() { + @Override + public int compare(String o1, String o2) { + return -1*((Integer)o1.length()).compareTo(o2.length()); + } + }); + + return allowedDomains; + } + + @Override + public List listPoolOptions(Long lbEnvironmentId, Long networkId, String type) { + if (lbEnvironmentId == null) { + throw new InvalidParameterValueException("Invalid LB Environment ID"); + } + + GloboNetworkLoadBalancerEnvironment lbEnvironment = _globoNetworkLBEnvironmentDao.findById(lbEnvironmentId); + if (lbEnvironment == null) { + throw new InvalidParameterValueException("Could not find mapping to LB environment " + lbEnvironmentId); + } + + if (networkId == null) { + throw new InvalidParameterValueException("Invalid Network ID"); + } + + Network network = _networkManager.getNetwork(networkId); + if (network == null) { + throw new InvalidParameterValueException("Cannot find network with ID : " + networkId); + } + + Answer answer = callCommand(new ListPoolOptionsCommand(lbEnvironment.getGloboNetworkLoadBalancerEnvironmentId(), type), network.getDataCenterId()); + + return ((GloboNetworkPoolOptionResponse)answer).getPoolOptions(); + } + + @Override + public List listAllPoolByVipId(Long lbId, Long zoneId) { + if (lbId == null) { + throw new InvalidParameterValueException("Invalid LB ID"); + } + + LoadBalancer lb = _lbService.findById(lbId); + GloboNetworkIpDetailVO networkDetail = getNetworkApiVipIp(lb); + //if the lb is not created in networkApi the networkDetail is not created yet + if ( networkDetail == null || networkDetail.getGloboNetworkVipId() == null) { + return new ArrayList(); + } + + ListPoolLBCommand command = new ListPoolLBCommand(networkDetail.getGloboNetworkVipId()); + + + Answer answer = callCommand(command, zoneId); + + //error + if ( answer == null || !answer.getResult() ) { + String msg = answer == null ? "Could not list pools lb from networkApi" : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + + GloboNetworkPoolResponse poolResponse = (GloboNetworkPoolResponse) answer; + + + return poolResponse.getPools(); + + } + + @Override + public GloboNetworkPoolResponse.Pool createPool(CreateGloboNetworkPoolCmd cmd) { + LoadBalancer lb = _lbService.findById(cmd.getLbId()); + if (lb == null){ + throw new CloudRuntimeException("Could not find Load balancer with id: " + cmd.getLbId()); + } + validatePortPair(lb, cmd.getPublicPort(), cmd.getPrivatePort()); + + GloboNetworkIpDetailVO ipDetail = getNetworkApiVipIp(lb); + GloboNetworkLoadBalancerEnvironment lbEnvironment = _globoNetworkLBEnvironmentDao.findById(ipDetail.getGloboNetworkEnvironmentRefId()); + if (lbEnvironment == null) { + throw new InvalidParameterValueException("Could not find mapping between lb environment " + ipDetail.getGloboNetworkEnvironmentRefId()); + } + List options = _lbOptionsDao.listByLoadBalancerId(cmd.getLbId()); + + String lbIpAddress = _lbMgr.getSourceIp(lb).addr(); + String lockName = "globonetworklb-" + lbIpAddress; + final ReentrantLock lock = GlobalLock.getReentrantLock(lockName); + try { + if (!lock.tryLock(GloboNetworkLBLockTimeout.value(), TimeUnit.SECONDS)) { + throw new ResourceUnavailableException(String.format("Failed to acquire lock for load balancer %s", lb.getUuid()), DataCenter.class, cmd.getZoneId()); + } + List reals = this.getReals(_lbMgr.getExistingDestinations(cmd.getLbId()), null); + + CreatePoolCommand command = new CreatePoolCommand(); + command.setVipId(ipDetail.getGloboNetworkVipId()); + command.setVipName(lb.getName()); + command.setVipIp(lbIpAddress); + command.setVipEnvironment(lbEnvironment.getGloboNetworkLoadBalancerEnvironmentId()); + command.setPublicPort(cmd.getPublicPort()); + command.setPrivatePort(cmd.getPrivatePort()); + command.setBalacingAlgorithm(lb.getAlgorithm()); + command.setReals(reals); + command.setRegion(GloboNetworkRegion.value()); + command.setL4protocol(cmd.getL4()); + command.setL7protocol(cmd.getL7()); + if(options != null && !options.isEmpty()){ + command.setServiceDownAction(options.get(0).getServiceDownAction()); + } + + Answer answer = callCommand(command, cmd.getZoneId()); + handleAnswerIfFail(answer, "Could not create pool"); + + _lbPortMapDao.persist(new LoadBalancerPortMapVO(lb.getId(),cmd.getPublicPort(), cmd.getPrivatePort())); + + return ((GloboNetworkPoolResponse) answer).getPool(); + } catch (CloudstackGloboNetworkException ex) { + String context = CallContext.current().getNdcContext(); + String integrationContext = ex.getContext(); + String msg = "Integration problem with NetworkAPI, please contact your system administrator. Context: " + context + ", Integration context: " + integrationContext + ", name=" + lb.getName(); + throw new UserCloudRuntimeException(msg); + } catch (Exception e) { + throw new UserCloudRuntimeException("Error adding new pool to load balancer. Context: "+ CallContext.current().getNdcContext() + ", lb name=" + lb.getName(), e); + } finally { + if (lock != null && lock.isHeldByCurrentThread()) { + lock.unlock(); + } + GlobalLock.releaseReentrantLock(lockName); + } + } + + @Override + public void deletePool(DeleteGloboNetworkPoolCmd cmd) { + LoadBalancer lb = _lbService.findById(cmd.getLbId()); + if (lb == null){ + throw new CloudRuntimeException("Could not find Load balancer with id: " + cmd.getLbId()); + } + GloboNetworkIpDetailVO ipDetail = getNetworkApiVipIp(lb); + GloboNetworkPoolResponse.Pool poolToRemove = findPoolById(cmd.getLbId(), cmd.getZoneId(), cmd.getPoolId()); + if(poolToRemove.getVipPort().equals(lb.getSourcePortStart()) && poolToRemove.getPort().equals(lb.getDefaultPortStart())){ + throw new InvalidParameterValueException("Default Load balancer port cannot be removed"); + } + + String lockName = "globonetworklb-" + _lbMgr.getSourceIp(lb).addr(); + final ReentrantLock lock = GlobalLock.getReentrantLock(lockName); + try { + if (!lock.tryLock(GloboNetworkLBLockTimeout.value(), TimeUnit.SECONDS)) { + throw new ResourceUnavailableException(String.format("Failed to acquire lock for load balancer %s", lb.getUuid()), DataCenter.class, cmd.getZoneId()); + } + + removePortMapping(lb, poolToRemove); + DeletePoolCommand command = new DeletePoolCommand(ipDetail.getGloboNetworkVipId(), cmd.getPoolId(), poolToRemove.getVipPort()); + Answer answer = callCommand(command, cmd.getZoneId()); + handleAnswerIfFail(answer, "Could not remove pool"); + } catch (CloudstackGloboNetworkException ex) { + String context = CallContext.current().getNdcContext(); + String integrationContext = ex.getContext(); + String msg = "Integration problem with NetworkAPI, please contact your system administrator. Context: " + context + ", Integration context: " + integrationContext + ", name=" + lb.getName(); + throw new UserCloudRuntimeException(msg); + } catch (Exception e) { + throw new UserCloudRuntimeException("Error removing port from load balancer. Context: "+ CallContext.current().getNdcContext() + ", lb name=" + lb.getName(), e); + } finally { + if (lock != null && lock.isHeldByCurrentThread()) { + lock.unlock(); + } + GlobalLock.releaseReentrantLock(lockName); + } + } + + protected GloboNetworkPoolResponse.Pool findPoolById(Long lbId, Long zoneId, Long poolId) { + List pools = listAllPoolByVipId(lbId, zoneId); + GloboNetworkPoolResponse.Pool poolToRemove = null; + for(GloboNetworkPoolResponse.Pool pool : pools){ + if(pool.getId().equals(poolId)){ + poolToRemove = pool; + break; + } + } + if(poolToRemove == null){ + throw new InvalidParameterValueException("Pool not found"); + } + return poolToRemove; + } + + protected void removePortMapping(LoadBalancer lb, GloboNetworkPoolResponse.Pool poolToRemove) { + List portMaps = _lbPortMapDao.listByLoadBalancerId(lb.getId()); + for(LoadBalancerPortMapVO portMap : portMaps){ + if(portMap.getPublicPort() == poolToRemove.getVipPort() && portMap.getPrivatePort() == poolToRemove.getPort()){ + _lbPortMapDao.remove(portMap.getId()); + } + } + } + + private void validatePortPair(LoadBalancer lb, Integer publicPort, Integer privatePort) { + List lbPortMaps = _lbPortMapDao.listByLoadBalancerId(lb.getId()); + lbPortMaps.add(new LoadBalancerPortMapVO(0L, lb.getSourcePortStart(), lb.getDefaultPortStart())); + + for (LoadBalancerPortMapVO lbPortMap : lbPortMaps) { + if(publicPort.equals(lbPortMap.getPublicPort())){ + if(privatePort.equals(lbPortMap.getPrivatePort())) { + throw new InvalidParameterValueException("This public/private port pair already exists."); + } + throw new InvalidParameterValueException("This public port already exists."); + } + } + + GloboResourceConfigurationVO config = _globoResourceConfigurationDao.getFirst(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.dsr); + if(config != null && config.getBooleanValue()){ + if(!publicPort.equals(privatePort)){ + throw new InvalidParameterValueException("In DSR load balancer the public port must always be the same as private port."); + } + } + } + + @Override + public List updatePools(List poolIds, Long lbId, Long zoneId, String healthcheckType, + String healthcheck, String expectedHealthcheck, Integer maxConn, + Protocol.L4 l4protocol, Protocol.L7 l7protocol, boolean redeploy) { + validateUpdatePool(poolIds, lbId, zoneId, healthcheckType, maxConn ); + + LoadBalancer loadBalancer = _lbService.findById(lbId); + if ( loadBalancer == null ){ + throw new CloudRuntimeException("Can not find Load balancer with id: " + lbId); + } + + + GloboNetworkIpDetailVO networkApiVipIp = getNetworkApiVipIp(loadBalancer); + + + HealthCheckHelper healthCheckHelper = HealthCheckHelper.build(loadBalancer.getName(), healthcheckType, healthcheck, expectedHealthcheck); + + UpdatePoolCommand command = new UpdatePoolCommand(poolIds, + healthCheckHelper.getHealthCheckType(), + healthCheckHelper.getHealthCheck(), + healthCheckHelper.getExpectedHealthCheck(), maxConn, loadBalancer.getName()); + + command.setL4Protocol(l4protocol); + command.setL7Protocol(l7protocol); + command.setRedeploy(redeploy); + command.setVipId(networkApiVipIp.getGloboNetworkVipId()); + Answer answer = callCommand(command, zoneId); + handleAnswerIfFail(answer, "Could not update pools " + poolIds); + + GloboNetworkPoolResponse poolResponse = (GloboNetworkPoolResponse)answer; + + return poolResponse.getPools(); + } + + private void validateUpdatePool(List poolIds, Long lbId, Long zoneId, String healthcheckType, Integer maxConn) { + if (lbId == null) { + throw new InvalidParameterValueException("Invalid LB ID: " + lbId); + } + if (zoneId == null) { + throw new InvalidParameterValueException("Invalid zone ID: " + zoneId); + } + if (healthcheckType == null) { + throw new InvalidParameterValueException("Invalid healthcheckType: " + healthcheckType); + } + if (maxConn == null || maxConn < 0) { + throw new InvalidParameterValueException("Invalid maxconn: " + maxConn); + } + + if (poolIds == null || poolIds.isEmpty()) { + throw new InvalidParameterValueException("Invalid pools ID. poolIds: " + poolIds); + } + } + + private void handleAnswerIfFail(Answer answer, String genericMsg) { + //error + if ( answer == null || !answer.getResult() ) { + String msg = answer == null ? genericMsg : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + } + @Override + public GloboNetworkIpDetailVO getNetworkApiVipIp(LoadBalancer lb) { + Ip sourceIp = _lbMgr.getSourceIp(lb); + + IPAddressVO ipVO = _ipAddrDao.findByIpAndNetworkId(lb.getNetworkId(), sourceIp.addr()); + if (ipVO == null) { + throw new InvalidParameterValueException("Ip " + sourceIp.addr() + " is not associate with network " + lb.getNetworkId()); + } + + GloboNetworkIpDetailVO gnIpDetail = _globoNetworkIpDetailDao.findByIp(ipVO.getId()); + + return gnIpDetail; + } + + @Override + public GloboNetworkPoolResponse.Pool getPoolById(Long poolId, Long zoneId) { + if (poolId == null) { + throw new InvalidParameterValueException("Invalid Pool ID: " + poolId); + } + if (zoneId == null) { + throw new InvalidParameterValueException("Invalid Zone ID: " + zoneId); + } + + GetPoolLBByIdCommand command = new GetPoolLBByIdCommand(poolId); + + Answer answer = callCommand(command, zoneId); + + //error + if ( answer == null || !answer.getResult() ) { + String msg = answer == null ? "Coud not get pool by id: " + poolId + " in zone: " + zoneId + "from networkApi" : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + + GloboNetworkPoolResponse response = (GloboNetworkPoolResponse)answer; + + return response.getPool(); + } + + @Override + public List listAllExpectedHealthchecks() { + + List dataCenterVOs = _dcDao.listEnabledZones(); + if (dataCenterVOs != null && dataCenterVOs.size() == 0) { + throw new InvalidParameterValueException("No zones enabled to execute command listAllExpectedHealthchecks"); + } + + Long zoneId = dataCenterVOs.get(0).getId(); + + ListExpectedHealthchecksCommand command = new ListExpectedHealthchecksCommand(); + + GloboNetworkExpectHealthcheckResponse answer = (GloboNetworkExpectHealthcheckResponse)callCommand(command, zoneId); + + if ( answer == null || !answer.getResult() ) { + String msg = answer == null ? "Coud not list expected healthchecks in zone: " + zoneId + "from networkApi" : answer.getDetails(); + throw new CloudRuntimeException(msg); + } + + return answer.getExpectedHealthchecks(); + } + + @Inject + NetworkService _networkService; + @Override + public Pair, Integer> searchForLbNetworks(ListGloboLbNetworksCmd cmd) { + List networks = _networkService.searchForAllNetworks(cmd); + + List networksToReturn = new ArrayList(); + for (Network network : networks){ + List lbEnvs = listGloboNetworkLBEnvironmentsFromDB(network.getPhysicalNetworkId(), network.getId(), null); + if (lbEnvs != null && lbEnvs.size() > 0) { + networksToReturn.add(network); + } + } + + //Now apply pagination + List wPagination = StringUtils.applyPagination(networksToReturn, cmd.getStartIndex(), cmd.getPageSizeVal()); + if (wPagination != null) { + Pair, Integer> listWPagination = new Pair, Integer>(wPagination, networksToReturn.size()); + return listWPagination; + } + + return new Pair, Integer>(networksToReturn, networksToReturn.size()); + } + + + @Inject + NetworkDao _networksDao = null; + + @Override + public List listLinkableLoadBalancers(Long lbid, Long projectId) { + LoadBalancer lb = _loadBalancerDao.findById(lbid); + if (lb == null) { + throw new CloudRuntimeException("Load balancer that you would like to link with another does not exist!"); + } + + GloboResourceConfigurationVO resource = getGloboResourceConfiguration(lb.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + + if (resource != null) { + String lbUuid = resource.getValue(); + LoadBalancer lblinked = _loadBalancerDao.findByUuid(lbUuid); + throw new CloudRuntimeException("This load balancer is already linked with load balancer: " + lblinked.getName()); + } + + GloboNetworkNetworkVO globoNetworkNetworkVO = _globoNetworkNetworkDao.findByNetworkId(lb.getNetworkId()); + Long envId = globoNetworkNetworkVO.getGloboNetworkEnvironmentId(); + + List lbs = _loadBalancerDao.listLinkables(lb.getUuid(), envId, lb.getAccountId()); + removeLinkedLbs(lbs); + + return lbs; + } + + private void removeLinkedLbs(List lbs) { + List lbsToRemove = new ArrayList<>(); + for (LoadBalancerVO loadBalancerVO : lbs) { + GloboResourceConfigurationVO linkedLbConfig = getGloboResourceConfiguration(loadBalancerVO.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer); + GloboResourceConfigurationVO isDsr = getGloboResourceConfiguration(loadBalancerVO.getUuid(), GloboResourceType.LOAD_BALANCER, GloboResourceKey.dsr); + if (linkedLbConfig != null || (isDsr != null && isDsr.getBooleanValue())) { + lbsToRemove.add(loadBalancerVO); + } + } + lbs.removeAll(lbsToRemove); + } + + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkService.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkService.java new file mode 100644 index 000000000000..063152aeaa24 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/GloboNetworkService.java @@ -0,0 +1,282 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.manager; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.host.HostVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.utils.Pair; +import com.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; +import com.globo.globonetwork.cloudstack.api.CreateGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.DeleteGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.ListGloboLbNetworksCmd; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.List; + +import javax.naming.ConfigurationException; + +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; + +import com.cloud.dc.DataCenter; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachineProfile; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAllEnvironmentResponse.Environment; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; + +public interface GloboNetworkService { + + /** + * Add a GloboNetwork Environment to an specific zone. + * @param physicalNetworkId + * @param name Name of the relationship, for example, BACKEND, FRONTEND + * @param napiEnvironmentId + * @return + */ + public GloboNetworkEnvironmentVO addGloboNetworkEnvironment(Long physicalNetworkId, String name, Long napiEnvironmentId); + + /** + * Add a GloboNetwork VIP Environment to a GloboNetwork Environment. + * @param name Name of the relationship + * @param physicalNetworkId + * @param napiEnvironmentId + * @param globoNetworkLBEnvironmentId + * @return + */ + public GloboNetworkLoadBalancerEnvironment addGloboNetworkLBEnvironment(String name, Long physicalNetworkId, Long napiEnvironmentId, Long globoNetworkLBEnvironmentId) + throws ResourceAllocationException; + + /** + * Create a new network in sync with GloboNetwork. + * + * @param zoneId + * @param networkOfferingId + * @param physicalNetworkId + * @param networkDomain + * @param aclType + * @param accountName + * @param projectId + * @param domainId + * @param subdomainAccess + * @param displayNetwork + * @param aclId + * @return + */ + public Network createNetwork(String name, String displayText, Long zoneId, Long networkOfferingId, Long napiEnvironmentId, String networkDomain, ACLType aclType, + String accountName, Long projectId, Long domainId, Boolean subdomainAccess, Boolean displayNetwork, Long aclId, Boolean isIpv6, Long subnet) throws ResourceAllocationException, + ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException; + + /** + * Validate if nicProfile in compatible with Network and destination dest. + * + * @param NicProfile + * @param network + * @return + * @throws InsufficientVirtualNetworkCapacityException + * @throws InsufficientAddressCapacityException + */ + public Network validateNic(NicProfile nicProfile, VirtualMachineProfile vm, Network network) throws InsufficientVirtualNetworkCapacityException, + InsufficientAddressCapacityException; + + /** + * Ensure network is created and active in GloboNetwork. If network is not created, create it. + * @param network + * @throws ConfigurationException + */ + public void implementNetwork(Network network) throws ConfigurationException; + + /** + * Remove network from GloboNetwork and inactivate the vlan + * @param network + */ + public void removeNetworkFromGloboNetwork(Network network); + + /** + * Deallocate Vlan from GloboNetwork. + * The vlan must have been previously inactivated with the 'removeNetworkFromGloboNetwork' method. + * @param network + */ + public void deallocateVlanFromGloboNetwork(Network network); + + /** + * List GloboNetwork environments from database with optional parameter physicalNetworkId or zoneId. If all parameters are null + * all GloboNetwork environments from database are returned. + * @param physicalNetworkId + * @param zoneId + */ + public List listGloboNetworkEnvironmentsFromDB(Long physicalNetworkId, Long zoneId); + + /** + * List GloboNetwork VIP environments from database according to a specific GloboNetwork Environment ID. + * If this parameter is not passed, all GloboNetwork VIP environments from database are returned. + * @param physicalNetworkId + * @param globoNetworkEnvironmentId + */ + public List listGloboNetworkLBEnvironmentsFromDB(Long physicalNetworkId, Long networkId, Long globoNetworkEnvironmentId); + + /** + * List all environments from GloboNetwork + * @param zoneId + * @return + */ + public List listAllEnvironmentsFromGloboNetwork(Long zoneId); + + boolean canEnable(Long physicalNetworkId); + + /** + * Removes the relationship between physical network and GloboNetwork environment + * @param physicalNetworkId + * @param globoNetworkEnvironmentId + * @return + */ + public boolean removeGloboNetworkEnvironment(Long physicalNetworkId, Long globoNetworkEnvironmentId); + + /** + * Removes the relationship between VIP Environment and GloboNetwork environment + * @param physicalNetworkId + * @param globoNetworkEnvironmentId + * @param globoNetworkLBNetworkId + * @return + */ + public boolean removeGloboNetworkLBEnvironment(Long physicalNetworkId, Long globoNetworkEnvironmentId, Long globoNetworkLBEnvironmentId); + + /** + * Add GloboNetwork host details (provider) to CloudStack + * @param physicalNetworkId + * @param username + * @param password + * @param url + */ + public Host addGloboNetworkHost(Long physicalNetworkId, String username, String password, String url); + + /** + * Retrieve VLAN info from GloboNetwork + * @param network + * @return + */ + public Vlan getVlanInfoFromGloboNetwork(Network network); + + /** + * Register VM NIC in GloboNetwork + * @param nic + * @param vm + * @param network + */ + public void registerNicInGloboNetwork(NicProfile nic, VirtualMachineProfile vm, Network network); + + /** + * Unregister NIC in GloboNetwork + * @param nic + * @param vm + */ + public void unregisterNicInGloboNetwork(NicProfile nic, VirtualMachineProfile vm); + + public boolean removeVmFromLoadBalancer(VirtualMachineProfile vm); + + /** + * Domain suffix for all networks. + * @return + */ + public String getDomainSuffix(); + + /** + * List all GloboNetwork VIPs associated to an account/project + * @param projectId + * @return + */ + public List listGloboNetworkVips(Long projectId); + + /** + * If custom network domain is supported + * @return + */ + public boolean isSupportedCustomNetworkDomain(); + + public List getAllZonesThatProviderAreEnabled(); + + public List listAllowedLbSuffixes(); + + public boolean destroyGloboNetwork(long networkId, boolean forced); + + public List listGloboNetworkReals(Long vipId); + + public boolean applyLbRuleInGloboNetwork(Network network, LoadBalancingRule rule) throws ResourceUnavailableException; + + public PublicIp acquireLbIp(Long networkId, Long projectId, Long lbEnvironmentId) throws ResourceAllocationException, ResourceUnavailableException, + ConcurrentOperationException, InvalidParameterValueException, InsufficientCapacityException; + + public boolean disassociateIpAddrFromGloboNetwork(long ipId); + + GloboResourceConfigurationVO getGloboResourceConfiguration(String uuid, GloboResourceType resourceType, GloboResourceKey key); + + public List getGloboResourceConfigs(String uuid, GloboResourceType resourceType); + + public boolean validateLBRule(Network network, LoadBalancingRule rule); + + public LoadBalancer importGloboNetworkLoadBalancer(Long lbId, Long networkId, Long projectId); + + public List listGloboNetworkLBCacheGroups(Long lbEnvironmentId, Long networkId); + + public List listPoolOptions(Long lbEnvironmentId, Long networkId, String type); + + public List listAllPoolByVipId(Long id, Long zoneUuid); + + public GloboNetworkPoolResponse.Pool getPoolById(Long poolId, Long zoneId); + + public List updatePools(List poolIds, Long vipId, Long zoneId, String healthcheckType, + String healthcheck, String expectedHealthcheck, Integer maxConn, + Protocol.L4 l4protocol, Protocol.L7 l7protocol, boolean redeploy); + + public List listAllExpectedHealthchecks(); + + Pair,Integer> searchForLbNetworks(ListGloboLbNetworksCmd listGloboLbNetworksCmd); + + public void registerDnsForResource(String id, GloboResourceType resourceType); + + GloboNetworkPoolResponse.Pool createPool(CreateGloboNetworkPoolCmd createGloboNetworkPoolCmd); + + void deletePool(DeleteGloboNetworkPoolCmd deleteGloboNetworkPoolCmd); + + List listLinkableLoadBalancers(Long lbid, Long projectId); + + GloboNetworkIpDetailVO getNetworkApiVipIp(LoadBalancer lb); + + HostVO getGloboNetworkHost(Long zoneId); + + Answer callCommand(Command cmd, Long zoneId); + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/HealthCheckHelper.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/HealthCheckHelper.java new file mode 100644 index 000000000000..667d7d2b42d1 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/HealthCheckHelper.java @@ -0,0 +1,137 @@ +package com.globo.globonetwork.cloudstack.manager; + +import java.io.Serializable; + +/** + * Created by lucas.castro on 3/10/16. + */ +public class HealthCheckHelper implements Serializable{ + + private String host; + private String healthCheckType; + private String healthCheck; + private String expectedHealthCheck; + + private static final String DEFAULT_EXPECT_FOR_HTTP_HEALTHCHECK = "WORKING"; + + private HealthCheckHelper(String host) { + this.host = host; + } + + public String getHealthCheckType() { + return healthCheckType; + } + + public String getHealthCheck() { + return healthCheck; + } + + public String getExpectedHealthCheck() { + return expectedHealthCheck; + } + + protected HealthCheckHelper build(String healthcheckType, String healthcheck, String expectedHealthcheck) { + this.healthCheck = this.buildHealthCheckString(healthcheck, host); + + //old version, client just pass healthcheckpath + if (healthcheck != null && !healthcheck.isEmpty() && + ((expectedHealthcheck == null || expectedHealthcheck.isEmpty()) || + (healthcheckType == null || healthcheckType.isEmpty())) + ) + { + this.healthCheckType = HealthCheckType.HTTP.name(); + this.expectedHealthCheck = DEFAULT_EXPECT_FOR_HTTP_HEALTHCHECK; + return this; + } + + //new version + if ( healthcheckType != null ){ + this.healthCheckType = HealthCheckType.valueOf(healthcheckType).name(); + + if (HealthCheckType.isLayer4(healthCheckType)) { + this.expectedHealthCheck = null; + this.healthCheck = null; + } else { + this.expectedHealthCheck = expectedHealthcheck != null ? expectedHealthcheck : DEFAULT_EXPECT_FOR_HTTP_HEALTHCHECK; + } + } else { + this.healthCheckType = HealthCheckType.TCP.name(); + this.expectedHealthCheck = null; + this.healthCheck = null; + } + + return this; + } + + protected String buildHealthCheckString(String path, String host) { + if (path == null || path.equals("") || host == null) { + return ""; + } + if (path.startsWith("GET") || path.startsWith("POST")) { + return path; + } + return "GET " + path + " HTTP/1.0\\r\\nHost: " + host + "\\r\\n\\r\\n"; + } + + + public static void validate(String host, String healthehckType, String healthcheck, String expectedHealthcheck){ + + + if ( healthehckType != null && HealthCheckType.isLayer4(healthehckType)){ + + if (expectedHealthcheck != null && !expectedHealthcheck.isEmpty()) { + throw new IllegalArgumentException("Health check validation error: when health check type is TCP/UDP 'Expected health check' should be empty! type: " + healthehckType + ", 'expected health check': " + expectedHealthcheck); + } + + if (healthcheck != null && !healthcheck.isEmpty()) { + throw new IllegalArgumentException("Health check validation error: When health check type is TCP/UDP 'HealthCheck request' should be empty! type: " + healthehckType + ", 'health check request': " + healthcheck); + } + } + } + + public static HealthCheckHelper build(String host, String healthehckType, String healthCheck, String expectedHealthCheck) { + HealthCheckHelper healthCheckHelper = new HealthCheckHelper(host); + validate(host, healthehckType, healthCheck, expectedHealthCheck); + return healthCheckHelper.build(healthehckType, healthCheck, expectedHealthCheck); + } + + public static String getL4Protocol(String healthcheckType, Integer vipPort) { + if(HealthCheckType.isLayer7(healthcheckType)){ + return HealthCheckType.TCP.name(); + } else{ + return healthcheckType.toUpperCase(); + } + } + + public static String getL7Protocol(String healthcheckType, Integer vipPort) { + if(!HealthCheckType.isLayer7(healthcheckType)){ + return "Outros"; + } else if (vipPort == 443 || vipPort == 8443) { + return HealthCheckType.HTTPS.name(); + } else if (vipPort == 80 || vipPort == 8080) { + return HealthCheckType.HTTP.name(); + } else{ + return healthcheckType.toUpperCase(); + } + } + + public enum HealthCheckType { + HTTP(7), HTTPS(7), TCP(4), UDP(4); + + private int layer; + + private HealthCheckType(int layer){ + this.layer = layer; + } + + public static boolean isLayer4(String healthCheckType) { + HealthCheckType healthType = HealthCheckType.valueOf(healthCheckType); + return healthType.layer == 4; + } + public static boolean isLayer7(String healthCheckType) { + HealthCheckType healthType = HealthCheckType.valueOf(healthCheckType); + return healthType.layer == 7; + } + } +} + diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/Protocol.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/Protocol.java new file mode 100644 index 000000000000..51381d6192f4 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/manager/Protocol.java @@ -0,0 +1,119 @@ +package com.globo.globonetwork.cloudstack.manager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class Protocol { + + + public enum L4 { + TCP("TCP", Arrays.asList(L7.values())), + UDP("UDP", Collections.singletonList(L7.OTHERS)); + + private String networkApiOptionValue; + private List l7s; + + L4(String networkApiOptionValue, List l7values) { + this.networkApiOptionValue = networkApiOptionValue; + this.l7s = l7values; + } + + public static String getNetworkAPIValues() { + + List values = new ArrayList<>(); + for (L7 protocol : Protocol.L7.values()) { + values.add(protocol.networkApiOptionValue); + } + return values.toString(); + } + + + public String getNetworkApiOptionValue() { + return networkApiOptionValue; + } + + public List getL7s() { + return l7s; + } + + public static L4 valueOfFromNetworkAPI(String value) { + for (L4 protocol : Protocol.L4.values()) { + + if (protocol.networkApiOptionValue.equals(value)) { + return protocol; + } + } + return null; + } + } + + + public enum L7{ + HTTP("HTTP"), + HTTPS("HTTPS"), + OTHERS("Outros"); + + private String networkApiOptionValue; + L7(String networkApiOptionValue) { + this.networkApiOptionValue = networkApiOptionValue; + } + + public static String getNetworkAPIValues() { + + List values = new ArrayList<>(); + for (L7 protocol : Protocol.L7.values()) { + values.add(protocol.networkApiOptionValue); + } + return values.toString(); + } + + public String getNetworkApiOptionValue() { + return networkApiOptionValue; + } + + public static L7 valueOfFromNetworkAPI(String value) { + for (L7 protocol : Protocol.L7.values()) { + + if (protocol.networkApiOptionValue.equals(value)) { + return protocol; + } + } + return null; + } + + } + + public static boolean validValueL7(String value) { + + for (L7 l7 : L7.values()) { + if (l7.networkApiOptionValue.equals(value)) { + return true; + } + } + return false; + + } + + public static boolean validValueL4(String value) { + + for (L4 l4 : L4.values()) { + if (l4.name().equals(value)) { + return true; + } + } + return false; + + } + + public static boolean validProtocols(L4 l4, L7 l7) { + + if (l4.getL7s().contains(l7)) { + return true; + } + + return false; + } +} + diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/GloboNetworkResource.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/GloboNetworkResource.java new file mode 100644 index 000000000000..cc0cbae9e9f0 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/GloboNetworkResource.java @@ -0,0 +1,1276 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.resource; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.globo.globonetwork.client.exception.GloboNetworkErrorCodeException; +import com.globo.globonetwork.client.model.OptionVipV3; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.cloudstack.commands.CreatePoolCommand; +import com.globo.globonetwork.cloudstack.commands.DeletePoolCommand; +import com.globo.globonetwork.cloudstack.commands.GloboNetworkResourceCommand; +import com.globo.globonetwork.cloudstack.commands.ValidateVipUpdateCommand; +import com.globo.globonetwork.cloudstack.response.CheckDSREnabledResponse; +import com.globo.globonetwork.client.api.ExpectHealthcheckAPI; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.model.healthcheck.ExpectHealthcheck; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.commands.CheckDSREnabled; +import com.globo.globonetwork.cloudstack.commands.GetPoolLBByIdCommand; +import com.globo.globonetwork.cloudstack.commands.ListExpectedHealthchecksCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolLBCommand; +import com.globo.globonetwork.cloudstack.manager.HealthCheckHelper; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.naming.ConfigurationException; + +import com.globo.globonetwork.client.model.OptionVip; +import com.globo.globonetwork.client.model.PoolOption; +import com.globo.globonetwork.client.model.VipPoolMap; +import com.globo.globonetwork.cloudstack.commands.ListGloboNetworkLBCacheGroupsCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolOptionsCommand; +import com.globo.globonetwork.cloudstack.response.GloboNetworkCacheGroupsResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; +import com.cloud.utils.component.ManagerBase; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.Environment; +import com.globo.globonetwork.client.model.Equipment; +import com.globo.globonetwork.client.model.Ip; +import com.globo.globonetwork.client.model.Network; +import com.globo.globonetwork.client.model.VipEnvironment; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.commands.AcquireNewIpForLbCommand; +import com.globo.globonetwork.cloudstack.commands.ActivateNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.CreateNewVlanInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.DeallocateVlanFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetNetworkFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetVipInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetVlanInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GloboNetworkErrorAnswer; +import com.globo.globonetwork.cloudstack.commands.ListAllEnvironmentsFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RegisterEquipmentAndIpInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.ReleaseIpFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveNetworkInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveVipFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.UnregisterEquipmentAndIpInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAllEnvironmentResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkAndIPResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse.Real; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVlanResponse; +import sun.net.util.IPAddressUtil; + +public class GloboNetworkResource extends ManagerBase implements ServerResource { + private String _zoneId; + + private String _guid; + + private String _name; + + private String _username; + private String _url; + private String _password; + + private Integer readTimeout = 2*60000; + private Integer connectTimeout = 1*60000; + private Integer numberOfRetries = 0; + + private static final Logger s_logger = Logger.getLogger(GloboNetworkResource.class); + + private static final long NETWORK_TYPE = 6; // Rede invalida de equipamentos + + private static final Long EQUIPMENT_TYPE = 10L; + + private static final Integer DEFAULT_REALS_PRIORITY = 0; + + private static final Integer DEFAULT_REAL_WEIGHT = 0; + + private static final Integer DEFAULT_MAX_CONN = 0; + private static final int DEFAULT_REAL_STATUS = 7; + + protected enum LbAlgorithm { + RoundRobin("round-robin"), LeastConn("least-conn"); + + String globoNetworkBalMethod; + + LbAlgorithm(String globoNetworkBalMethod) { + this.globoNetworkBalMethod = globoNetworkBalMethod; + } + + public String getGloboNetworkBalMethod() { + return globoNetworkBalMethod; + } + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + + try { + _zoneId = (String)params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _guid = (String)params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find guid"); + } + + _name = (String)params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + _url = (String)params.get("url"); + if (_url == null) { + throw new ConfigurationException("Unable to find url"); + } + + _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"); + } + + + if (params.containsKey("readTimeout")) { + readTimeout = Integer.valueOf((String)params.get("readTimeout")); + } + + if (params.containsKey("connectTimeout")) { + connectTimeout = Integer.valueOf((String)params.get("connectTimeout")); + } + + if (params.containsKey("numberOfRetries")) { + numberOfRetries = Integer.valueOf((String)params.get("numberOfRetries")); + } + + return true; + } catch (NumberFormatException e) { + s_logger.error("Invalid number in configuration parameters", e); + throw new ConfigurationException("Invalid number in configuration parameters: " + e); + } + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public Type getType() { + return Host.Type.L2Networking; + } + + @Override + public StartupCommand[] initialize() { + StartupCommand cmd = new StartupCommand(getType()); + cmd.setName(_name); + cmd.setGuid(_guid); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(""); + cmd.setStorageIpAddress(""); + cmd.setVersion(GloboNetworkResource.class.getPackage().getImplementationVersion()); + + return new StartupCommand[] {cmd}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + return new PingCommand(getType(), id); + } + + @Override + public void disconnected() { + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof GloboNetworkResourceCommand) { + GloboNetworkResourceCommand globoNetworkResourceCommand = (GloboNetworkResourceCommand)cmd; + GloboNetworkAPI api = getNewGloboNetworkAPI(); + return globoNetworkResourceCommand.execute(api); + } + + if (cmd instanceof ReadyCommand) { + return new ReadyAnswer((ReadyCommand)cmd); + } else if (cmd instanceof MaintainCommand) { + return new MaintainAnswer((MaintainCommand)cmd); + } else if (cmd instanceof GetVlanInfoFromGloboNetworkCommand) { + return execute((GetVlanInfoFromGloboNetworkCommand)cmd); + } else if (cmd instanceof CreateNewVlanInGloboNetworkCommand) { + return execute((CreateNewVlanInGloboNetworkCommand)cmd); + } else if (cmd instanceof ActivateNetworkCommand) { + return execute((ActivateNetworkCommand)cmd); + } else if (cmd instanceof ListAllEnvironmentsFromGloboNetworkCommand) { + return execute((ListAllEnvironmentsFromGloboNetworkCommand)cmd); + } else if (cmd instanceof RemoveNetworkInGloboNetworkCommand) { + return execute((RemoveNetworkInGloboNetworkCommand)cmd); + } else if (cmd instanceof DeallocateVlanFromGloboNetworkCommand) { + return execute((DeallocateVlanFromGloboNetworkCommand)cmd); + } else if (cmd instanceof RegisterEquipmentAndIpInGloboNetworkCommand) { + return execute((RegisterEquipmentAndIpInGloboNetworkCommand)cmd); + } else if (cmd instanceof UnregisterEquipmentAndIpInGloboNetworkCommand) { + return execute((UnregisterEquipmentAndIpInGloboNetworkCommand)cmd); + } else if (cmd instanceof GetVipInfoFromGloboNetworkCommand) { + return execute((GetVipInfoFromGloboNetworkCommand)cmd); + } else if (cmd instanceof ValidateVipUpdateCommand) { + return execute((ValidateVipUpdateCommand)cmd); + } else if (cmd instanceof RemoveVipFromGloboNetworkCommand) { + return execute((RemoveVipFromGloboNetworkCommand)cmd); + } else if (cmd instanceof AcquireNewIpForLbCommand) { + return execute((AcquireNewIpForLbCommand)cmd); + } else if (cmd instanceof ReleaseIpFromGloboNetworkCommand) { + return execute((ReleaseIpFromGloboNetworkCommand)cmd); + } else if (cmd instanceof ApplyVipInGloboNetworkCommand) { + return execute((ApplyVipInGloboNetworkCommand)cmd); + } else if (cmd instanceof GetNetworkFromGloboNetworkCommand) { + return execute((GetNetworkFromGloboNetworkCommand)cmd); + } else if (cmd instanceof ListGloboNetworkLBCacheGroupsCommand) { + return execute((ListGloboNetworkLBCacheGroupsCommand) cmd); + }else if (cmd instanceof ListPoolOptionsCommand){ + return execute((ListPoolOptionsCommand) cmd); + }else if (cmd instanceof ListPoolLBCommand) { + return execute((ListPoolLBCommand) cmd); + }else if (cmd instanceof GetPoolLBByIdCommand) { + return execute((GetPoolLBByIdCommand) cmd); + }else if (cmd instanceof ListExpectedHealthchecksCommand) { + return execute((ListExpectedHealthchecksCommand) cmd); + }else if (cmd instanceof CreatePoolCommand) { + return execute((CreatePoolCommand) cmd); + }else if (cmd instanceof DeletePoolCommand) { + return execute((DeletePoolCommand) cmd); + }else if (cmd instanceof CheckDSREnabled) { + return execute((CheckDSREnabled) cmd); + } + return Answer.createUnsupportedCommandAnswer(cmd); + } + + private Answer execute(ListExpectedHealthchecksCommand cmd) { + try{ + ExpectHealthcheckAPI api = getNewGloboNetworkAPI().getExpectHealthcheckAPI(); + + List expectHealthchecks = api.listHealthcheck(); + + List result = new ArrayList(); + + for (ExpectHealthcheck expectHealthcheck : expectHealthchecks){ + result.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(expectHealthcheck.getId(), expectHealthcheck.getExpect())); + } + + GloboNetworkExpectHealthcheckResponse response = new GloboNetworkExpectHealthcheckResponse(result); + return response; + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } catch (Exception e) { + s_logger.error("Generic error accessing GloboNetwork while update pool", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(CreatePoolCommand cmd) { + PoolV3 pool = null; + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + + try{ + VipAPIFacade vipAPIFacade = createVipAPIFacade(cmd.getVipId(), gnAPI); + + if(vipAPIFacade.hasVip()) { + VipInfoHelper vipInfo = getVipInfos(gnAPI, cmd.getVipEnvironment(), cmd.getVipIp()); + + pool = createPool( + cmd.getPublicPort(), cmd.getPrivatePort(), vipInfo.getEnvironment(), cmd.getVipName(), + cmd.getBalacingAlgorithm(), cmd.getL4protocol().name(), null, null, null, + DEFAULT_MAX_CONN, cmd.getServiceDownAction(), buildPoolMembers(gnAPI, cmd.getReals()), cmd.getRegion() + ); + + pool = gnAPI.getPoolAPI().save(pool); + + VipEnvironment vipEnv = gnAPI.getVipEnvironmentAPI().search(cmd.getVipEnvironment(), null, null, null); + vipAPIFacade.addPool(vipEnv, cmd.getPublicPort(), cmd.getL4protocol().getNetworkApiOptionValue(), cmd.getL7protocol().getNetworkApiOptionValue(), pool); + return new GloboNetworkPoolResponse(poolV3FromNetworkApi(pool)); + } + return new GloboNetworkPoolResponse(cmd, true, "", new GloboNetworkPoolResponse.Pool()); + } catch (GloboNetworkException e) { + rollbackPoolCreation(pool, gnAPI); + return handleGloboNetworkException(cmd, e); + } catch (Exception e) { + rollbackPoolCreation(pool, gnAPI); + s_logger.error("Generic error accessing GloboNetwork while creating new pool", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private void rollbackPoolCreation(PoolV3 pool, GloboNetworkAPI gnAPI){ + if(pool != null && pool.getId() != null){ + try { + gnAPI.getPoolAPI().deleteV3(pool.getId()); + } catch (GloboNetworkException e) { + s_logger.error("Error rollbacking pool creation", e); + } + } + } + + private Answer execute(DeletePoolCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + VipAPIFacade vipAPIFacade = null; + + try{ + vipAPIFacade = createVipAPIFacade(cmd.getVipId(), gnAPI); + vipAPIFacade.removePool(cmd.getPoolId()); + + PoolAPI poolAPI = gnAPI.getPoolAPI(); + PoolV3 pool = poolAPI.getById(cmd.getPoolId()); + if(pool.getPoolCreated()){ + poolAPI.undeployV3(pool.getId()); + } + + poolAPI.deleteV3(cmd.getPoolId()); + + return new Answer(cmd, true, "Pool successfuly removed"); + } catch (GloboNetworkException e) { + rollbackPoolRemoval(cmd.getPoolId(), cmd.getVipPort(), vipAPIFacade, gnAPI); + return handleGloboNetworkException(cmd, e); + } catch (Exception e) { + rollbackPoolRemoval(cmd.getPoolId(), cmd.getVipPort(), vipAPIFacade, gnAPI); + s_logger.error("Generic error accessing GloboNetwork while creating new pool", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private void rollbackPoolRemoval(Long poolId, Integer vipPort, VipAPIFacade vipAPIFacade, GloboNetworkAPI gnAPI){ + try { + PoolV3 pool = gnAPI.getPoolAPI().getById(poolId); + if(pool != null) { + VipEnvironment vipEnv = gnAPI.getVipEnvironmentAPI().search(vipAPIFacade.getVip().getEnvironmentVipId(), null, null, null); + String l4ProtocolString = HealthCheckHelper.getL4Protocol(pool.getHealthcheck().getHealthcheckType(), vipPort); + String l7ProtocolString = HealthCheckHelper.getL7Protocol(pool.getHealthcheck().getHealthcheckType(), vipPort); + vipAPIFacade.addPool(vipEnv, vipPort, l4ProtocolString, l7ProtocolString, pool); + } + } catch (GloboNetworkException e) { + s_logger.error("Error rollbacking pool removal", e); + } + } + + + private Answer execute(GetPoolLBByIdCommand cmd) { + try { + PoolV3 pool = getNewGloboNetworkAPI().getPoolAPI().getById(cmd.getPoolId()); + + GloboNetworkPoolResponse.Pool poolCS = poolFromNetworkApi(pool, null); + GloboNetworkPoolResponse answer = new GloboNetworkPoolResponse(cmd, true, "", poolCS); + + return answer; + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } catch (Exception e) { + s_logger.error("Generic error accessing GloboNetwork while getPoolById", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(ListPoolLBCommand cmd) { + try { + GloboNetworkAPI globoNetworkAPI = getNewGloboNetworkAPI(); + PoolAPI poolAPI = globoNetworkAPI.getPoolAPI(); + + VipAPIFacade apiFacade = this.createVipAPIFacade(cmd.getVipId(), globoNetworkAPI); + VipV3 vip = apiFacade.getVip(); + + List optionVipV3s = globoNetworkAPI.getOptionVipV3API().listOptions(vip.getEnvironmentVipId()); + + List pools = new ArrayList<>(); + for(VipV3.Port port : vip.getPorts()){ + Integer vipPort = port.getPort(); + VipV3.PortOptions options = port.getOptions(); + String optionL4 = findProtocol(optionVipV3s, options.getL4ProtocolId()); + String optionL7 = findProtocol(optionVipV3s, options.getL7ProtocolId()); + + for(VipV3.Pool p : port.getPools()){ + PoolV3 pool = poolAPI.getById(p.getPoolId()); + GloboNetworkPoolResponse.Pool poolCS = poolFromNetworkApi(pool, vipPort); + poolCS.setL4protocol(optionL4); + poolCS.setL7protocol(optionL7); + pools.add(poolCS); + + } + } + + GloboNetworkPoolResponse answer = new GloboNetworkPoolResponse(cmd, true, "", pools); + return answer; + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } catch (Exception e) { + s_logger.error("Generic error accessing GloboNetwork", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private String findProtocol(List optionVipV3s, Long optionId) { + + for (OptionVipV3 option : optionVipV3s) { + if (option.getId().equals(optionId)) { + return option.getName(); + } + } + + return null; + } + + private static GloboNetworkPoolResponse.Pool poolFromNetworkApi(PoolV3 poolNetworkApi, Integer vipPort) throws GloboNetworkException { + GloboNetworkPoolResponse.Pool pool = new GloboNetworkPoolResponse.Pool(); + + pool.setId(poolNetworkApi.getId()); + pool.setIdentifier(poolNetworkApi.getIdentifier()); + pool.setPort(poolNetworkApi.getDefaultPort()); + pool.setLbMethod(poolNetworkApi.getLbMethod()); + pool.setMaxconn(poolNetworkApi.getMaxconn()); + pool.setVipPort(vipPort); + + + PoolV3.Healthcheck healthcheck = poolNetworkApi.getHealthcheck(); + + if (healthcheck != null) { + pool.setHealthcheck(healthcheck.getHealthcheckRequest()); + pool.setHealthcheckType(healthcheck.getHealthcheckType()); + pool.setExpectedHealthcheck(healthcheck.getExpectedHealthcheck()); + } + + return pool; + } + public static GloboNetworkPoolResponse.Pool poolV3FromNetworkApi(PoolV3 poolNetworkApi) throws GloboNetworkException { + GloboNetworkPoolResponse.Pool pool = new GloboNetworkPoolResponse.Pool(); + + pool.setId(poolNetworkApi.getId()); + pool.setIdentifier(poolNetworkApi.getIdentifier()); + pool.setPort(poolNetworkApi.getDefaultPort()); + pool.setLbMethod(poolNetworkApi.getLbMethod()); + pool.setMaxconn(poolNetworkApi.getMaxconn()); + + PoolV3.Healthcheck healthcheck = poolNetworkApi.getHealthcheck(); + + if ( healthcheck != null ) { + pool.setHealthcheck(healthcheck.getHealthcheckRequest()); + pool.setHealthcheckType(healthcheck.getHealthcheckType()); + pool.setExpectedHealthcheck(healthcheck.getExpectedHealthcheck()); + } + + return pool; + } + + private Answer execute(ListPoolOptionsCommand cmd) { + try { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + Network network = gnAPI.getNetworkJsonAPI().listVipNetworks(cmd.getGloboNetworkLBEnvironmentId(), false).get(0); + Vlan vlan = gnAPI.getVlanAPI().getById(network.getVlanId()); + List poolOptions = gnAPI.getPoolAPI().listPoolOptionsV3(vlan.getEnvironment(), cmd.getType()); + + List options = new ArrayList<>(); + for(PoolOption option : poolOptions){ + options.add(new GloboNetworkPoolOptionResponse.PoolOption(option.getId(), option.getName())); + } + return new GloboNetworkPoolOptionResponse(cmd, options); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } catch (IOException e) { + s_logger.error("Generic error accessing GloboNetwork", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private Answer execute(ListGloboNetworkLBCacheGroupsCommand cmd) { + try { + List optionVips = getNewGloboNetworkAPI().getOptionVipAPI().listCacheGroups(cmd.getLBEnvironmentId()); + List cacheGroups = new ArrayList(); + if (optionVips != null) { + for(OptionVip optionVip : optionVips) { + cacheGroups.add(optionVip.getCacheGroup()); + } + } + // if optionVips is null, then an empty list will be returned + return new GloboNetworkCacheGroupsResponse(cmd, cacheGroups); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + private Answer execute(GetNetworkFromGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + if (cmd.getNetworkId() == null) { + return new Answer(cmd, false, "Invalid network ID"); + } + + Network network = gnAPI.getNetworkAPI().getNetwork(cmd.getNetworkId(), cmd.isv6()); + if (network == null) { + return new Answer(cmd, false, "Network with ID " + cmd.getNetworkId() + " not found in GloboNetwork"); + } + + return this.createNetworkResponse(network, cmd, gnAPI); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + private Answer handleGloboNetworkException(Command cmd, GloboNetworkException e) { + return GloboNetworkResourceCommand.handleGloboNetworkException(cmd, e); + } + + public Answer execute(RemoveVipFromGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + + if (cmd.getVipId() == null) { + return new Answer(cmd, true, "Vip request was previously removed from GloboNetwork"); + } + try { + VipAPIFacade vipAPIFacade = this.createVipAPIFacade(cmd.getVipId(), gnAPI); + if (!vipAPIFacade.hasVip()) { + return new Answer(cmd, true, "Vip request " + cmd.getVipId() + " was previously removed from GloboNetwork"); + } + + vipAPIFacade.undeploy(); + + s_logger.info("Requesting GloboNetwork to delete vip vip_id=" + cmd.getVipId() + " keepIp=" + cmd.isKeepIp()); + vipAPIFacade.delete(cmd.isKeepIp()); + + return new Answer(cmd); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(GetVlanInfoFromGloboNetworkCommand cmd) { + try { + Vlan vlan = getNewGloboNetworkAPI().getVlanAPI().getById(cmd.getVlanId()); + return createResponse(vlan, cmd); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(CreateNewVlanInGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + + Vlan vlan = null; + try { + vlan = gnAPI.getVlanAPI().allocateWithoutNetwork(cmd.getGloboNetworkEnvironmentId(), cmd.getVlanName(), cmd.getVlanDescription()); + + /*Network network = */gnAPI.getNetworkAPI().addNetwork(vlan.getId(), Long.valueOf(NETWORK_TYPE), null, cmd.isIpv6(), cmd.getSubnet()); + + // Bug in GloboNetworkApi: I need to have a second call to get networkid + vlan = gnAPI.getVlanAPI().getById(vlan.getId()); + return createResponse(vlan, cmd); + } catch (GloboNetworkException e) { + if (vlan != null) { + try { + gnAPI.getVlanAPI().deallocate(vlan.getId()); + } catch (GloboNetworkException ex) { + s_logger.error("Error deallocating vlan " + vlan.getId() + "from GloboNetwork."); + } + } + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(ActivateNetworkCommand cmd) { + try { + getNewGloboNetworkAPI().getNetworkJsonAPI().createNetworks(cmd.getNetworkId(), cmd.isv6()); + return new Answer(cmd, true, "Network created"); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(ListAllEnvironmentsFromGloboNetworkCommand cmd) { + try { + List apiEnvironmentList = getNewGloboNetworkAPI().getEnvironmentAPI().listAll(); + + List environmentList = new ArrayList(apiEnvironmentList.size()); + for (Environment apiEnvironment : apiEnvironmentList) { + GloboNetworkAllEnvironmentResponse.Environment environment = new GloboNetworkAllEnvironmentResponse.Environment(); + environment.setId(apiEnvironment.getId()); + environment.setDcDivisionName(apiEnvironment.getDcDivisionName()); + environment.setL3GroupName(apiEnvironment.getL3GroupName()); + environment.setLogicalEnvironmentName(apiEnvironment.getLogicalEnvironmentName()); + environmentList.add(environment); + } + + return new GloboNetworkAllEnvironmentResponse(cmd, environmentList); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(RemoveNetworkInGloboNetworkCommand cmd) { + try { + getNewGloboNetworkAPI().getNetworkJsonAPI().removeNetwork(cmd.getNetworkId(), cmd.isIpv6()); + return new Answer(cmd, true, "Network removed"); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(DeallocateVlanFromGloboNetworkCommand cmd) { + try { + getNewGloboNetworkAPI().getVlanAPI().deallocate(cmd.getVlanId()); + return new Answer(cmd, true, "Vlan deallocated"); + } catch (GloboNetworkException e) { + if (e instanceof GloboNetworkErrorCodeException) { + GloboNetworkErrorCodeException ex = (GloboNetworkErrorCodeException)e; + if (ex.getCode() == GloboNetworkErrorCodeException.VLAN_NOT_REGISTERED) { + return new Answer(cmd, true, "Vlan already has been deallocated"); + } + } + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(RegisterEquipmentAndIpInGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + Equipment equipment = gnAPI.getEquipmentAPI().listByName(cmd.getVmName()); + if (equipment == null) { + s_logger.info("Registering virtualmachine " + cmd.getVmName() + " in GloboNetwork"); + // Equipment (VM) does not exist, create it + equipment = gnAPI.getEquipmentAPI().insert(cmd.getVmName(), EQUIPMENT_TYPE, cmd.getEquipmentModelId(), cmd.getEquipmentGroupId()); + } + + Vlan vlan = gnAPI.getVlanAPI().getById(cmd.getVlanId()); + + // Make sure this vlan has only one IPv4/IPv6 network associated to it + if (vlan.getNetworks().size() == 0) { + return new Answer(cmd, false, "No IPv4/IPv6 networks in this vlan"); + } else if (vlan.getNetworks().size() > 1) { + return new Answer(cmd, false, "Multiple IPv4/IPv6 networks in this vlan"); + } + Network network = vlan.getNetworks().get(0); + + Ip ip = gnAPI.getIpAPI().findByIpAndEnvironment(cmd.getNicIp(), cmd.getEnvironmentId(), network.isv6()); + if (ip == null) { + // Doesn't exist, create it + ip = gnAPI.getIpAPI().saveIp(cmd.getNicIp(), equipment.getId(), cmd.getNicDescription(), network.getId(), network.isv6()); + } else { + ip = gnAPI.getIpAPI().getIp(ip.getId(), false); + if (!ip.getEquipments().contains(cmd.getVmName())) { + gnAPI.getIpAPI().assocIp(ip.getId(), equipment.getId(), network.getId(), network.isv6(), cmd.getNicDescription()); + } + } + + if (ip == null) { + return new Answer(cmd, false, "Could not register NIC in GloboNetwork"); + } + + return new Answer(cmd, true, "NIC " + cmd.getNicIp() + " registered successfully in GloboNetwork"); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(UnregisterEquipmentAndIpInGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + Equipment equipment = gnAPI.getEquipmentAPI().listByName(cmd.getVmName()); + if (equipment == null) { + s_logger.warn("VM was removed from GloboNetwork before being destroyed in Cloudstack. This is not critical, logging inconsistency: VM UUID " + cmd.getVmName()); + return new Answer(cmd); + } + + if (cmd.getEnvironmentId() != null && cmd.getNicIp() != null) { + Ip ip = gnAPI.getIpAPI().findByIpAndEnvironment(cmd.getNicIp(), cmd.getEnvironmentId(), cmd.isv6()); + if (ip == null) { + // Doesn't exist, ignore + s_logger.warn("IP was removed from GloboNetwork before being destroyed in Cloudstack. This is not critical, logging inconsistency: IP " + cmd.getNicIp()); + } else { + gnAPI.getEquipmentAPI().removeIP(equipment.getId(), ip.getId(), cmd.isv6()); + } + } + + // if there are no more IPs in equipment, remove it. + List ipList = gnAPI.getIpAPI().findIpsByEquipment(equipment.getId()); + if (ipList.size() == 0) { + gnAPI.getEquipmentAPI().delete(equipment.getId()); + } + + return new Answer(cmd, true, "NIC " + cmd.getNicIp() + " deregistered successfully in GloboNetwork"); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(ReleaseIpFromGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + Ip ip = gnAPI.getIpAPI().checkVipIp(cmd.getIp(), cmd.getVipEnvironmentId(), cmd.isv6()); + if (ip == null) { + // Doesn't exist, ignore + s_logger.warn("IP was removed from GloboNetwork before being destroyed in Cloudstack. This is not critical."); + } else { + gnAPI.getIpAPI().deleteIp(ip.getId(), cmd.isv6()); + } + return new Answer(cmd, true, "IP deleted successfully from GloboNetwork"); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(GetVipInfoFromGloboNetworkCommand cmd) { + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + VipAPIFacade apiFacade = createVipAPIFacade(cmd.getVipId(), gnAPI); + return createVipResponse(apiFacade.getVip(), cmd, gnAPI); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(ValidateVipUpdateCommand cmd){ + GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + VipAPIFacade apiFacade = createVipAPIFacade(cmd.getVipId(), gnAPI); + VipV3 vip = apiFacade.getVip(); + Ip ip = gnAPI.getIpAPI().getIp(vip.getIpv4Id(), false); + + if (!cmd.getIp().equals(ip.getIpString())) { + return new Answer(cmd, false, "You can create only 1 lb rule per IP."); + } + + if (!vip.getName().equals(cmd.getName())) { + return new Answer(cmd, false, "It is not allowed to change load balancer name in GloboNetwork"); + } + return new Answer(cmd, true, null); + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(AcquireNewIpForLbCommand cmd) { + final GloboNetworkAPI gnAPI = getNewGloboNetworkAPI(); + try { + long globoNetworkLBEnvironmentId = cmd.getGloboNetworkLBEnvironmentId(); + Ip globoIp = gnAPI.getIpAPI().getAvailableIpForVip(globoNetworkLBEnvironmentId, cmd.getDescription(), cmd.isv6()); + if (globoIp == null) { + return new Answer(cmd, false, "No available ip address for load balancer environment network " + globoNetworkLBEnvironmentId); + } + + // get network information + Long networkId = globoIp.getNetworkId(); + Network network = gnAPI.getNetworkAPI().getNetwork(networkId, cmd.isv6()); + if (network == null) { + return new Answer(cmd, false, "Network with id " + networkId + " not found"); + } + + GloboNetworkAndIPResponse answer = (GloboNetworkAndIPResponse)this.createNetworkResponse(network, cmd, gnAPI); + + // ip information + answer.setIp(globoIp.getIpString()); + answer.setIpId(globoIp.getId()); + + return answer; + } catch (GloboNetworkException e) { + return handleGloboNetworkException(cmd, e); + } + } + + public Answer execute(CheckDSREnabled cmd) { + try { + GloboNetworkAPI gnApi = getNewGloboNetworkAPI(); + VipAPIFacade vipAPIFacade = this.createVipAPIFacade(null, gnApi); + if(vipAPIFacade.isDSRVipEnabled(cmd.getVipEnvironmentId())){ + return new CheckDSREnabledResponse(cmd, true, true, ""); + }else{ + return new CheckDSREnabledResponse(cmd, true, false, "This VIP environment does not support DSR"); + } + } catch (GloboNetworkException e) { + return new CheckDSREnabledResponse(cmd, false, false, "Error checking VIP environment configuration"); + } + } + + public Answer execute(ApplyVipInGloboNetworkCommand cmd) { + List vipPoolMapping = new ArrayList<>(); + VipAPIFacade vipAPIFacade = null; + GloboNetworkAPI gnApi = getNewGloboNetworkAPI(); + + try { + s_logger.debug("[ApplyVip_" + cmd.getHost() + "] Vip_id: " + cmd.getVipId() + " ip: " + cmd.getIpv4() + " envId: " + cmd.getVipEnvironmentId()); + Long start = new Date().getTime(); + + vipAPIFacade = this.createVipAPIFacade(cmd.getVipId(), gnApi); + VipInfoHelper vipInfo = getVipInfos(gnApi, cmd.getVipEnvironmentId(), cmd.getIpv4()); + List poolMembers = buildPoolMembers(gnApi,cmd.getRealList()); + List poolIds = vipAPIFacade.getPoolIds(); + + if(poolIds.isEmpty()){ + vipPoolMapping = this.createPools(gnApi, poolMembers, vipInfo.getEnvironment(), cmd); + }else{ + this.updatePools(gnApi, poolIds, poolMembers, cmd.getPortPairs()); + } + + if (!vipAPIFacade.hasVip()) { + vipAPIFacade.save(cmd, cmd.getHost(), vipInfo.vipEnvironment, vipInfo.vipIp, vipPoolMapping); + } else { + vipAPIFacade.update(cmd, vipInfo.vipIp, vipPoolMapping); + } + + vipAPIFacade.deploy(); + + Answer vipResponse = vipAPIFacade.createVipResponse(cmd); + Long time = new Date().getTime() - start; + + s_logger.debug("[ApplyVip END] Vip: " + cmd.getHost() + ", ip: " + cmd.getIpv4() +", Operation time: " + time + " ms"); + return vipResponse; + }catch (GloboNetworkException e) { + rollbackVipCreation(gnApi, cmd, vipAPIFacade, vipPoolMapping); + return handleGloboNetworkException(cmd, e); + } catch (InvalidParameterValueException e){ + s_logger.error("Error", e); + return new Answer(cmd, false, e.getMessage()); + } + } + + private void rollbackVipCreation(GloboNetworkAPI gnApi, ApplyVipInGloboNetworkCommand cmd, VipAPIFacade vipAPIFacade, List poolMappings){ + if(vipAPIFacade != null && !vipAPIFacade.hasVip() && cmd.getVipId() == null && poolMappings != null){ + List ids = new ArrayList<>(); + for(VipPoolMap vipPoolMap : poolMappings){ + ids.add(vipPoolMap.getPoolId()); + } + try { + gnApi.getPoolAPI().deleteV3(ids); + } catch (GloboNetworkException e) { + s_logger.error("It was not possible to cleanup pools after failed vip creation", e); + } + } + } + + public static String buildPoolName(String region, String host, Integer vipPort, Integer realport) { + return "ACS_POOL_" + region + "_" + host + "_" + vipPort + "_" + realport; + } + + protected LbAlgorithm getBalancingAlgorithm(String methodBal) { + LbAlgorithm lbAlgorithm; + if ("roundrobin".equals(methodBal)) { + lbAlgorithm = LbAlgorithm.RoundRobin; + } else if ("leastconn".equals(methodBal)) { + lbAlgorithm = LbAlgorithm.LeastConn; + } else { + throw new InvalidParameterValueException("Invalid balancing method provided."); + } + return lbAlgorithm; + } + + protected List findPoolsByPort(Integer port, List pools) { + List poolsFound = new ArrayList<>(); + if(pools != null) { + for (PoolV3 pool : pools) { + if (port.equals(pool.getDefaultPort())) { + poolsFound.add(pool); + } + } + } + return poolsFound; + } + + protected Answer createVipResponse(VipV3 vip, GetVipInfoFromGloboNetworkCommand cmd, GloboNetworkAPI gnAPI) throws GloboNetworkException { + Set servicePorts = new LinkedHashSet<>(); + OptionVipV3 cacheOption = gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getCacheGroupId()); + String cache = cacheOption != null ? cacheOption.getName() : null; + OptionVipV3 persistenceOption = gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getPersistenceId()); + String persistence = persistenceOption != null ? persistenceOption.getName() : null; + String balancingMethod = null; + String healthcheckType = null; + String healthcheckString = null; + Integer maxConn = null; + List members = new ArrayList<>(); + + for(VipV3.Port port : vip.getPorts()){ + for(VipV3.Pool p : port.getPools()){ + PoolV3 pool = gnAPI.getPoolAPI().getById(p.getPoolId()); + servicePorts.add(port.getPort() + ":" + pool.getDefaultPort()); + members.addAll(pool.getPoolMembers()); + + balancingMethod = pool.getLbMethod(); + healthcheckType = pool.getHealthcheck().getHealthcheckType(); + healthcheckString = pool.getHealthcheck().getHealthcheckRequest(); + maxConn = pool.getMaxconn(); + } + } + + Map reals = new HashMap<>(); + for (PoolV3.PoolMember real : members) { + Real realResponse = reals.get(real.getIpId()); + if (realResponse == null) { + realResponse = new Real(); + realResponse.setIp(real.getIpFormated()); + realResponse.setVmName(real.getEquipmentName()); + realResponse.setPorts(new ArrayList<>(servicePorts)); + reals.put(real.getIpId(), realResponse); + } + } + + Ip ip = gnAPI.getIpAPI().getIp(vip.getIpv4Id(), false); + + return new GloboNetworkVipResponse( + cmd, vip.getId(), vip.getName(), ip.getIpString(), vip.getIpv4Id(), vip.getEnvironmentVipId(), null, + cache, balancingMethod, persistence, healthcheckType, healthcheckString, maxConn, servicePorts, + reals.values(), vip.getCreated() + ); + } + + private Answer createResponse(Vlan vlan, Command cmd) { + + if (vlan.getIpv4Networks().isEmpty() && vlan.getIpv6Networks().isEmpty()) { + // Error code 116 from GloboNetwork: 116 : VlanNaoExisteError, + return new GloboNetworkErrorAnswer(cmd, 116, "No networks in this VLAN"); + } + + String vlanName = vlan.getName(); + String vlanDescription = vlan.getDescription(); + Long vlanId = vlan.getId(); + Long vlanNum = vlan.getVlanNum(); + Network network = vlan.getNetworks().get(0); + + return new GloboNetworkVlanResponse(cmd, vlanId, vlanName, vlanDescription, vlanNum, network.getNetworkAddressAsString(), + network.getMaskAsString(), network.getId(), network.getActive(), network.getBlock(), network.isv6()); + } + + private Answer createNetworkResponse(Network network, Command cmd, GloboNetworkAPI gnAPI) throws GloboNetworkException { + GloboNetworkAndIPResponse answer = new GloboNetworkAndIPResponse(cmd); + answer.setNetworkId(network.getId()); + answer.setVipEnvironmentId(network.getVipEnvironmentId()); + answer.setNetworkAddress(network.getNetworkAddressAsString()); + answer.setNetworkMask(network.getMaskAsString()); + answer.setActive(Boolean.TRUE.equals(network.getActive())); + answer.setNetworkCidr(network.getNetworkAddressAsString() + "/" + network.getBlock()); + answer.setIsv6(network.isv6()); + + // get vlan information + Long vlanId = network.getVlanId(); + Vlan vlan = gnAPI.getVlanAPI().getById(vlanId); + if (vlan == null) { + return new Answer(cmd, false, "Vlan with id " + vlanId + " not found"); + } + answer.setVlanId(vlanId); + answer.setVlanName(vlan.getName()); + answer.setVlanDescription(vlan.getDescription()); + answer.setVlanNum(vlan.getVlanNum().intValue()); + return answer; + } + + public enum PersistenceMethod { + NONE("(nenhum)", "None"), + COOKIE("cookie", "Cookie"), + SOURCE_IP("source-ip", "Source-ip"), + SOURCE_IP_PERSISTENCE_PORTS("source-ip com persist. entre portas", "Source-ip with persistence between ports"), + PRIORITY_FAILOVER("Priority_Failover", "Priority Failover"); + + + private String networkAPICode; + private String description; + private PersistenceMethod(String networkAPICode, String description){ + this.networkAPICode = networkAPICode; + this.description = description; + } + public static String fromPersistencePolicy(LoadBalancingRule.LbStickinessPolicy policy) { + if (policy == null) { + return PersistenceMethod.NONE.networkAPICode; + } + + for (PersistenceMethod persistenceMethod : PersistenceMethod.values()) { + if (persistenceMethod.description.equals(policy.getMethodName())) { + return persistenceMethod.networkAPICode; + } + } + + throw new InvalidParameterValueException("Invalid persistence policy provided. value: " + policy.getMethodName()); + } + + public static PersistenceMethod validateValue(String value) { + + List values = new ArrayList<>(); + for (PersistenceMethod persistenceMethod : PersistenceMethod.values()) { + if (persistenceMethod.description.equals(value)) { + return persistenceMethod; + } + values.add(persistenceMethod.description); + } + values.remove(PersistenceMethod.NONE.description); + + throw new InvalidParameterValueException("Invalid persistence policy provided. value: " + value + ", possible values: " + StringUtils.join(values, ", ")+ "."); + } + } + + protected ArrayList createPools(GloboNetworkAPI gnApi, List poolMembers, Long vipEnvironment, ApplyVipInGloboNetworkCommand cmd) throws GloboNetworkException { + Map vipPoolMaps = new LinkedHashMap<>(); + PoolAPI poolAPI = gnApi.getPoolAPI(); + + for (Pair portPair : cmd.getPortPairs()) { + Integer vipPort = portPair.first(); + Integer realPort = portPair.second(); + + PoolV3 pool = createPool( + vipPort, + realPort, + vipEnvironment, + cmd.getHost(), + cmd.getMethodBal(), + cmd.getHealthcheckType(), + cmd.getHealthcheck(), + cmd.getExpectedHealthcheck(), + cmd.getHealthCheckDestination(), + DEFAULT_MAX_CONN, + cmd.getServiceDownAction(), + poolMembers, + cmd.getRegion() + ); + + pool = poolAPI.save(pool); + vipPoolMaps.put(vipPort + ":" + realPort, new VipPoolMap(pool.getId(), vipPort)); + } + return new ArrayList<>(vipPoolMaps.values()); + } + + protected PoolV3 createPool(Integer vipPort, Integer realPort, Long environment, String lbName, String balancingAlgorithm, String healthcheckType, String healthcheckPath, String expectedHealthcheck, String healthcheckDestination, Integer maxConn, String serviceDownActionStr, List poolMembers, String region){ + PoolV3 poolV3 = new PoolV3(); + poolV3.setMaxconn(maxConn != null ? maxConn : DEFAULT_MAX_CONN); + poolV3.setIdentifier(buildPoolName(region, lbName, vipPort, realPort)); + poolV3.setDefaultPort(realPort); + poolV3.setEnvironment(environment); + + PoolV3.Healthcheck healthcheck = poolV3.getHealthcheck(); + healthcheck.setDestination(healthcheckDestination); + if (forceSupportOldPoolVersion(healthcheckType, realPort)) { + healthcheck.setHealthcheck("TCP", "", ""); + } else { + healthcheck.setHealthcheck(healthcheckType, healthcheckPath, expectedHealthcheck); + } + + LbAlgorithm lbAlgorithm = getBalancingAlgorithm(balancingAlgorithm); + poolV3.setLbMethod(lbAlgorithm.getGloboNetworkBalMethod()); + + PoolV3.ServiceDownAction serviceDownAction = new PoolV3.ServiceDownAction(); + serviceDownAction.setName(serviceDownActionStr); + poolV3.setServiceDownAction(serviceDownAction); + + for (PoolV3.PoolMember poolMember : poolMembers) { + poolMember.setPortReal(realPort); + poolV3.getPoolMembers().add(poolMember); + } + return poolV3; + } + + protected void updatePools(GloboNetworkAPI gnApi, List poolIds, List poolMembers, List> portPairs) throws GloboNetworkException { + PoolAPI poolAPI = gnApi.getPoolAPI(); + Set updatePoolsIdList = new HashSet<>(); + + for (Pair portPair : portPairs) { + Integer realPort = portPair.second(); + + List pools = poolAPI.getByIdsV3(poolIds); + List poolsFound = findPoolsByPort(realPort, pools); + + for (PoolV3 poolV3 : poolsFound) { + if(!updatePoolsIdList.contains(poolV3.getId())) { + List poolMembersFinal = new ArrayList<>(); + + for (PoolV3.PoolMember poolMember : poolMembers) { + PoolV3.PoolMember poolMemberAlreadyExists = findExistsRealInPool(poolMember, poolV3.getPoolMembers()); + + if (poolMemberAlreadyExists == null) { + poolMember.setPortReal(realPort); + poolMember.setLimit(poolV3.getMaxconn()); + poolMembersFinal.add(poolMember); + } else { + poolMemberAlreadyExists.setEquipment(poolMember.getEquipment()); + poolMemberAlreadyExists.setIp(poolMember.getIp()); + poolMembersFinal.add(poolMemberAlreadyExists); + } + } + poolV3.setPoolMembers(poolMembersFinal); + + poolV3 = poolAPI.save(poolV3); + updatePoolsIdList.add(poolV3.getId()); + } + } + } + } + + protected boolean forceSupportOldPoolVersion(String healthcheckType, Integer realPort) { + return (!healthcheckType.equals(HealthCheckHelper.HealthCheckType.HTTPS.name()) + &&(realPort == 443 || realPort == 8443)); + } + + private PoolV3.PoolMember findExistsRealInPool(PoolV3.PoolMember poolMember, List poolMembersFromNetworkApi) { + + for (PoolV3.PoolMember poolMemberCreated : poolMembersFromNetworkApi) { + if (poolMemberCreated.getIpFormated().equals(poolMember.getIpFormated())) { + return poolMemberCreated; + } + } + return null; + } + + protected List buildPoolMembers(GloboNetworkAPI gnAPI, List realList) throws GloboNetworkException { + List poolMembers = new ArrayList(); + + for (Real real : realList) { + if (real.isRevoked()) { + continue; + } + Ip equipmentIp = gnAPI.getIpAPI().findByIpAndEnvironment(real.getIp(), real.getEnvironmentId(), false); + if (equipmentIp == null) { + throw new InvalidParameterValueException("Could not get information by real IP: " + real.getIp()); + } + Equipment equipment = gnAPI.getEquipmentAPI().listByName(real.getVmName()); + + PoolV3.PoolMember poolMember = new PoolV3.PoolMember(); + + poolMember.setPriority(DEFAULT_REALS_PRIORITY); + poolMember.setWeight(DEFAULT_REAL_WEIGHT); + poolMember.setMemberStatus(DEFAULT_REAL_STATUS); + + poolMember.setEquipmentId(equipment.getId()); + poolMember.setEquipmentName(equipment.getName()); + + PoolV3.Ip ipReal = new PoolV3.Ip(); + ipReal.setIpFormated(real.getIp()); + ipReal.setId(equipmentIp.getId()); + + + boolean isIPv6 = IPAddressUtil.isIPv6LiteralAddress(real.getIp()); + if (isIPv6) { + poolMember.setIpv6(ipReal); + } else { + poolMember.setIp(ipReal); + } + + poolMembers.add(poolMember); + } + + return poolMembers; + } + + protected VipInfoHelper getVipInfos(GloboNetworkAPI gnAPI, Long vipEnvironmentId, String ipv4) throws GloboNetworkException { + VipEnvironment environmentVip = gnAPI.getVipEnvironmentAPI().search(vipEnvironmentId, null, null, null); + if (environmentVip == null) { + throw new InvalidParameterValueException("Could not find VIP environment " + vipEnvironmentId); + } + + Ip vipIp = gnAPI.getIpAPI().checkVipIp(ipv4, vipEnvironmentId, false); + if (vipIp == null) { + throw new InvalidParameterValueException("IP " + ipv4 + " doesn't exist in VIP environment " + vipEnvironmentId); + } + + Network network = gnAPI.getNetworkAPI().getNetwork(vipIp.getNetworkId(), false); + if (network == null) { + throw new InvalidParameterValueException("Network " + vipIp.getNetworkId() + " was not found in GloboNetwork"); + } + Vlan vlan = gnAPI.getVlanAPI().getById(network.getVlanId()); + if (vlan == null) { + throw new InvalidParameterValueException("Vlan " + network.getVlanId() + " was not found in GloboNetwork"); + } + + return new VipInfoHelper(vlan.getEnvironment(), environmentVip, vipIp, network); + } + + public static class VipInfoHelper { + + private final Network vipNetwork; + private final Ip vipIp; + private Long environment; + private VipEnvironment vipEnvironment; + + public VipInfoHelper(Long environment, VipEnvironment vipEnv, Ip vipIp, Network vipNetwork) { + this.environment = environment; + this.vipEnvironment = vipEnv; + this.vipIp = vipIp; + this.vipNetwork = vipNetwork; + + } + + public Long getEnvironment() { + return environment; + } + } + + protected VipAPIFacade createVipAPIFacade(Long vipId, GloboNetworkAPI api) throws GloboNetworkException { + return new VipAPIFacade(vipId, api); + } + + + public GloboNetworkAPI getNewGloboNetworkAPI(){ + GloboNetworkAPI api = new GloboNetworkAPI(_url, _username, _password); + api.setConnectTimeout(connectTimeout); + api.setReadTimeout(readTimeout); + api.setNumberOfRetries(numberOfRetries); + + + String ndcContext = CallContext.current().getNdcContext(); + + api.setContext(ndcContext); + + return api; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/VipAPIFacade.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/VipAPIFacade.java new file mode 100644 index 000000000000..b794a2d6c1e7 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/resource/VipAPIFacade.java @@ -0,0 +1,255 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.resource; + +import com.cloud.agent.api.Answer; +import com.cloud.network.lb.LoadBalancingRule; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.VipEnvironment; +import com.globo.globonetwork.client.model.VipPoolMap; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.OptionVipV3; +import com.globo.globonetwork.client.model.Ip; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.manager.HealthCheckHelper; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import org.apache.log4j.Logger; + +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; + +public class VipAPIFacade { + + private VipV3 vip; + private Ip ip; + private GloboNetworkAPI globoNetworkAPI; + + private static final String DEFAULT_CACHE = "(nenhum)"; + private static final Integer DEFAULT_TIMEOUT = 5; + private static final String DEFAULT_TRAFFIC_RETURN = "Normal"; + private static final String DSR_TRAFFIC_RETURN = "DSRL3"; + private static final Logger s_logger = Logger.getLogger(VipAPIFacade.class); + + public VipAPIFacade(Long id, GloboNetworkAPI globoNetworkAPI) throws GloboNetworkException { + this.globoNetworkAPI = globoNetworkAPI; + if(id != null){ + vip = globoNetworkAPI.getVipV3API().getById(id); + } + } + + Boolean hasVip(){ + return vip != null; + } + + public VipAPIFacade save(ApplyVipInGloboNetworkCommand cmd, String host, VipEnvironment vipEnvironment, Ip ip, List vipPoolMapping) throws GloboNetworkException { + this.ip = ip; + VipV3 vip = new VipV3(); + vip.setName(host); + vip.setService(cmd.getServiceName()); + vip.setBusiness(cmd.getBusinessArea()); + vip.setEnvironmentVipId(vipEnvironment.getId()); + vip.setIpv4Id(ip.getId()); + vip.setOptions(buildVipOptions(cmd)); + + OptionVipV3 l7Rule = getProtocolOption(vipEnvironment.getId(), "l7_rule", "default_vip"); + List ports = new ArrayList<>(); + + String l4ProtocolString; + String l7ProtocolString; + + for(VipPoolMap vipPoolMap : vipPoolMapping){ + + String healthcheckType = cmd.getHealthcheckType(); + Integer vipPort = vipPoolMap.getPort(); + + + l4ProtocolString = HealthCheckHelper.getL4Protocol(healthcheckType, vipPort); + l7ProtocolString = HealthCheckHelper.getL7Protocol(healthcheckType, vipPort); + + + VipV3.Port port = createPort(vipEnvironment, vipPoolMap.getPoolId(), vipPort, l4ProtocolString, l7ProtocolString, l7Rule); + ports.add(port); + } + vip.setPorts(ports); + + globoNetworkAPI.getVipV3API().save(vip); + + this.vip = globoNetworkAPI.getVipV3API().getById(vip.getId()); + return this; + } + + public VipV3.Port createPort(VipEnvironment vipEnvironment, Long poolId, Integer vipPort, String l4ProtocolValue, String l7ProtocolValue, OptionVipV3 defaultL7Rule) throws GloboNetworkException { + + OptionVipV3 l4Protocol = getProtocolOption(vipEnvironment.getId(), "l4_protocol", l4ProtocolValue); + OptionVipV3 l7Protocol = getProtocolOption(vipEnvironment.getId(), "l7_protocol", l7ProtocolValue); + + VipV3.Port port = new VipV3.Port(); + port.setPort(vipPort); + port.setOptions(new VipV3.PortOptions(l4Protocol.getId(), l7Protocol.getId())); + + VipV3.Pool pool = new VipV3.Pool(poolId, defaultL7Rule.getId(), null); + port.setPools(Collections.singletonList(pool)); + return port; + } + + public VipAPIFacade update(ApplyVipInGloboNetworkCommand cmd, Ip ip, List vipPoolMapping) throws GloboNetworkException { + this.ip = ip; + VipV3 result; + String lbPersistence = getPersistenceMethod(cmd.getPersistencePolicy()); + OptionVipV3 persistence = getProtocolOption(cmd.getVipEnvironmentId(), "Persistencia", lbPersistence); + + if (vip.getCreated()) { + if(!persistence.getId().equals(vip.getOptions().getPersistenceId())) { + globoNetworkAPI.getVipV3API().updatePersistence(vip.getId(), persistence.getId()); + } + result = vip; + }else{ + vip.setOptions(buildVipOptions(cmd)); + result = globoNetworkAPI.getVipV3API().save(vip); + } + this.vip = result; + return this; + } + + public OptionVipV3 getProtocolOption(Long vipEnvid, String protocol, String protocolString) throws GloboNetworkException { + + List optionsByTypeAndName = globoNetworkAPI.getOptionVipV3API().findOptionsByTypeAndName(vipEnvid, protocol, protocolString); + + if (optionsByTypeAndName.size() == 0) { + throw new GloboNetworkException("Integration problem, could not find option '"+ protocolString + "' for protocol '" + protocol + "' in environment vip '" + vipEnvid + "' in NetworkAPI. Please, contact you system administrator to register that option in NetworkAPI." ); + } + + return optionsByTypeAndName.get(0); + } + + void deploy() throws GloboNetworkException { + if (!vip.getCreated()) { + s_logger.info("Requesting GloboNetwork to create vip " + vip.getId()); + globoNetworkAPI.getVipV3API().deploy(vip.getId()); + } + } + + Answer createVipResponse(ApplyVipInGloboNetworkCommand cmd) { + if (vip == null || vip.getId() == null) { + return new Answer(cmd, false, "Vip request was not created in GloboNetwork"); + } + + return new GloboNetworkVipResponse( + cmd, vip.getId(), vip.getName(), ip.getIpString(), vip.getIpv4Id(), vip.getEnvironmentVipId(), null, cmd.getCache(), + cmd.getMethodBal(), getPersistenceMethod(cmd.getPersistencePolicy()), cmd.getHealthcheckType(), cmd.getHealthcheck(), + 0, new ArrayList(), new ArrayList(), vip.getCreated() + ); + } + + void undeploy() throws GloboNetworkException { + if (vip.getCreated()) { + s_logger.info("Requesting GloboNetwork to undeploy vip from loadbalancer equipment vip_id=" + vip.getId()); + globoNetworkAPI.getVipV3API().undeploy(vip.getId()); + } + } + + void delete(Boolean keepIp) throws GloboNetworkException { + globoNetworkAPI.getVipV3API().delete(vip.getId(), keepIp); + } + + void addPool(VipEnvironment vipEnvironment, Integer vipPort, String l4protocol, String l7protocol, PoolV3 poolV3) throws GloboNetworkException { + OptionVipV3 l7Rule = getProtocolOption(vipEnvironment.getId(), "l7_rule", "default_vip"); + + VipV3.Port port = createPort(vipEnvironment, poolV3.getId(), vipPort, l4protocol, l7protocol, l7Rule); + vip.getPorts().add(port); + globoNetworkAPI.getVipV3API().deployUpdate(vip); + } + + void removePool(Long poolId) throws GloboNetworkException { + Iterator portIterator = vip.getPorts().iterator(); + while(portIterator.hasNext()){ + VipV3.Port port = portIterator.next(); + Iterator poolIterator = port.getPools().iterator(); + + while(poolIterator.hasNext()){ + VipV3.Pool pool = poolIterator.next(); + if(pool.getPoolId().equals(poolId)) { + if(port.getPools().size() == 1){ + portIterator.remove(); + }else{ + poolIterator.remove(); + } + globoNetworkAPI.getVipV3API().deployUpdate(vip); + return; + } + } + } + } + + private VipV3.VipOptions buildVipOptions(ApplyVipInGloboNetworkCommand cmd) throws GloboNetworkException { + String cache = cmd.getCache() == null ? DEFAULT_CACHE : cmd.getCache(); + String lbPersistence = getPersistenceMethod(cmd.getPersistencePolicy()); + Long environment = cmd.getVipEnvironmentId(); + + OptionVipV3 cacheGroup = getProtocolOption(environment, "cache", cache); + OptionVipV3 trafficReturn = getProtocolOption(environment, "Retorno de trafego", cmd.isDsr() ? DSR_TRAFFIC_RETURN : DEFAULT_TRAFFIC_RETURN); + OptionVipV3 timeout = getProtocolOption(environment, "timeout", String.valueOf(DEFAULT_TIMEOUT)); + OptionVipV3 persistence = getProtocolOption(environment, "Persistencia", lbPersistence); + + return new VipV3.VipOptions(cacheGroup.getId(), trafficReturn.getId(), timeout.getId(), persistence.getId()); + } + + List getPoolIds() throws GloboNetworkException { + if(vip != null) { + Long defaultVipL7Rule = getProtocolOption(vip.getEnvironmentVipId(), "l7_rule", "default_vip").getId(); + Set poolIdSet = new HashSet<>(); + for (VipV3.Port port : vip.getPorts()) { + for (VipV3.Pool pool : port.getPools()) { + if (pool.getL7RuleId().equals(defaultVipL7Rule)) { + poolIdSet.add(pool.getPoolId()); + } + } + } + return new ArrayList<>(poolIdSet); + }else{ + return new ArrayList<>(); + } + } + + boolean isDSRVipEnabled(Long environmentId) throws GloboNetworkException { + List trafficReturns = globoNetworkAPI.getOptionVipV3API().findOptionsByTypeAndName(environmentId, "Retorno de trafego", DSR_TRAFFIC_RETURN); + return trafficReturns.size() > 0; + } + + protected static String getPersistenceMethod(LoadBalancingRule.LbStickinessPolicy persistencePolicy) { + return GloboNetworkResource.PersistenceMethod.fromPersistencePolicy(persistencePolicy); + } + + @Override + public String toString() { + return "VipAPIFacade{" + vip + "}"; + } + + public VipV3 getVip() { + return vip; + } + + protected void setVip(VipV3 vip) { + this.vip = vip; + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/CheckDSREnabledResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/CheckDSREnabledResponse.java new file mode 100644 index 000000000000..4ed0905949d2 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/CheckDSREnabledResponse.java @@ -0,0 +1,38 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.cloudstack.commands.CheckDSREnabled; + +public class CheckDSREnabledResponse extends Answer { + + private Boolean dsrEnabled; + + public CheckDSREnabledResponse(CheckDSREnabled command, boolean result, boolean dsrEnabled, String message) { + super(command, result, message); + this.dsrEnabled = dsrEnabled; + } + + public Boolean isDsrEnabled() { + return dsrEnabled; + } + + public void setDsrEnabled(Boolean dsrEnabled) { + this.dsrEnabled = dsrEnabled; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GetGloboResourceConfigurationResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GetGloboResourceConfigurationResponse.java new file mode 100644 index 000000000000..ff4e3dcf0fcd --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GetGloboResourceConfigurationResponse.java @@ -0,0 +1,62 @@ +package com.globo.globonetwork.cloudstack.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; + +@EntityReference(value = GloboResourceConfiguration.class) +public class GetGloboResourceConfigurationResponse extends BaseResponse { + + @SerializedName("resourceuuid") + @Param(description = "the uuid of the resource") + private String uuid; + + @SerializedName("resourcetype") + @Param(description = "the resourcetype of the resource") + private String resourceType; + + @SerializedName("configurationkey") + @Param(description = "the configuration key") + private String configurationKey; + + @SerializedName("configurationvalue") + @Param(description = "the configuration value") + private String configurationValue; + + public GetGloboResourceConfigurationResponse() { + } + + public String getUuid() { + return this.uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getResourceType() { + return this.resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public void setConfigurationKey(String configurationKey){ + this.configurationKey = configurationKey; + } + + public void setConfigurationValue(String configurationValue){ + this.configurationValue = configurationValue; + } + + public String getConfigurationKey() { + return configurationKey; + } + + public String getConfigurationValue() { + return configurationValue; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAllEnvironmentResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAllEnvironmentResponse.java new file mode 100644 index 000000000000..920284c0efe9 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAllEnvironmentResponse.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 com.globo.globonetwork.cloudstack.response; + +import java.util.List; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class GloboNetworkAllEnvironmentResponse extends Answer { + + private List environmentList; + + public GloboNetworkAllEnvironmentResponse(Command command, List environmentList) { + super(command, true, null); + this.environmentList = environmentList; + } + + public List getEnvironmentList() { + return environmentList; + } + + public static class Environment { + private Long id; + private String l3GroupName; + private String logicalEnvironmentName; + private String dcDivisionName; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getL3GroupName() { + return l3GroupName; + } + + public void setL3GroupName(String l3GroupName) { + this.l3GroupName = l3GroupName; + } + + public String getLogicalEnvironmentName() { + return logicalEnvironmentName; + } + + public void setLogicalEnvironmentName(String logicalEnvironmentName) { + this.logicalEnvironmentName = logicalEnvironmentName; + } + + public String getDcDivisionName() { + return dcDivisionName; + } + + public void setDcDivisionName(String dcDivisionName) { + this.dcDivisionName = dcDivisionName; + } + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAndIPResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAndIPResponse.java new file mode 100644 index 000000000000..d6748c724db0 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkAndIPResponse.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 com.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class GloboNetworkAndIPResponse extends Answer { + + // Vlan information + private Long vlanId; + + private String vlanName; + + private String vlanDescription; + + private Integer vlanNum; + + // Network information + private Long networkId; + + private String networkAddress; + + private String networkMask; + + private String networkCidr; + + private boolean isActive; + + private boolean isv6; + + // Vip information (optional) + private Long vipEnvironmentId; + + // Ip information (optional) + private Long ipId; + + private String ip; + + public GloboNetworkAndIPResponse(Command command) { + super(command, true, null); + } + + public GloboNetworkAndIPResponse(Command command, Long vlanId, String vlanName, String vlanDescription, Integer vlanNum, boolean isActive, Long networkId, + String networkAddress, String networkMask, String networkCidr, Long ipId, String ip, Long vipEnvironmentId, boolean isv6) { + this(command); + this.vlanId = vlanId; + this.vlanName = vlanName; + this.vlanDescription = vlanDescription; + this.vlanNum = vlanNum; + this.networkAddress = networkAddress; + this.networkMask = networkMask; + this.networkCidr = networkCidr; + this.networkId = networkId; + this.isActive = isActive; + this.ipId = ipId; + this.ip = ip; + this.vipEnvironmentId = vipEnvironmentId; + this.setIsv6(isv6); + } + + public Long getVlanId() { + return vlanId; + } + + public void setVlanId(Long vlanId) { + this.vlanId = vlanId; + } + + public String getVlanName() { + return vlanName; + } + + public void setVlanName(String vlanName) { + this.vlanName = vlanName; + } + + public String getVlanDescription() { + return vlanDescription; + } + + public void setVlanDescription(String vlanDescription) { + this.vlanDescription = vlanDescription; + } + + public Integer getVlanNum() { + return vlanNum; + } + + public void setVlanNum(Integer vlanNum) { + this.vlanNum = vlanNum; + } + + public boolean isActive() { + return isActive; + } + + public void setActive(boolean isActive) { + this.isActive = isActive; + } + + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public String getNetworkAddress() { + return networkAddress; + } + + public void setNetworkAddress(String networkAddress) { + this.networkAddress = networkAddress; + } + + public Long getIpId() { + return ipId; + } + + public void setIpId(Long ipId) { + this.ipId = ipId; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getNetworkMask() { + return networkMask; + } + + public void setNetworkMask(String networkMask) { + this.networkMask = networkMask; + } + + public String getNetworkCidr() { + return networkCidr; + } + + public void setNetworkCidr(String networkCidr) { + this.networkCidr = networkCidr; + } + + public Long getVipEnvironmentId() { + return vipEnvironmentId; + } + + public void setVipEnvironmentId(Long vipEnvironmentId) { + this.vipEnvironmentId = vipEnvironmentId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkCacheGroupsResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkCacheGroupsResponse.java new file mode 100644 index 000000000000..fc682ee8690e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkCacheGroupsResponse.java @@ -0,0 +1,40 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +import java.util.List; + +public class GloboNetworkCacheGroupsResponse extends Answer { + + private List cacheGroups; + + public GloboNetworkCacheGroupsResponse(Command command, List cacheGroups) { + super(command, true, null); + this.cacheGroups = cacheGroups; + } + + public List getCacheGroups() { + return cacheGroups; + } + + public void setCacheGroups(List cacheGroups) { + this.cacheGroups = cacheGroups; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkEnvironmentExternalResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkEnvironmentExternalResponse.java new file mode 100644 index 000000000000..2b296f23cb1e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkEnvironmentExternalResponse.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.globo.globonetwork.cloudstack.response; + +import org.apache.cloudstack.api.BaseResponse; + +public class GloboNetworkEnvironmentExternalResponse extends BaseResponse { + + private Long environmentId; + + private String environmentFullName; + + public Long getEnvironmentId() { + return environmentId; + } + + public void setEnvironmentId(Long environmentId) { + this.environmentId = environmentId; + } + + public String getEnvironmentFullName() { + return environmentFullName; + } + + public void setEnvironmentFullName(String environmentFullName) { + this.environmentFullName = environmentFullName; + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkExpectHealthcheckResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkExpectHealthcheckResponse.java new file mode 100644 index 000000000000..335b404c66f9 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkExpectHealthcheckResponse.java @@ -0,0 +1,52 @@ +package com.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import java.util.List; + +public class GloboNetworkExpectHealthcheckResponse extends Answer{ + + public GloboNetworkExpectHealthcheckResponse(Command command, boolean success, String details, List expectedHealthchecks) { + super(command, success, details); + this.expectedHealthchecks = expectedHealthchecks; + } + + public GloboNetworkExpectHealthcheckResponse(List expectedHealthchecks) { + this.expectedHealthchecks = expectedHealthchecks; + } + + private List expectedHealthchecks; + + public List getExpectedHealthchecks() { + return expectedHealthchecks; + } + + public static class ExpectedHealthcheck { + private Long id; + private String expected; + + public ExpectedHealthcheck() { + } + + public ExpectedHealthcheck(Long id, String expected) { + this.id = id; + this.expected = expected; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getExpected() { + return expected; + } + + public void setExpected(String expected) { + this.expected = expected; + } + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolOptionResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolOptionResponse.java new file mode 100644 index 000000000000..9ed308059250 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolOptionResponse.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.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.cloudstack.commands.ListPoolOptionsCommand; + +import java.util.List; + +public class GloboNetworkPoolOptionResponse extends Answer { + + private List poolOptions; + + public GloboNetworkPoolOptionResponse(ListPoolOptionsCommand cmd, List poolOptions) { + this.poolOptions = poolOptions; + } + + public List getPoolOptions() { + return poolOptions; + } + + public static class PoolOption { + + private Long id; + + private String name; + + public PoolOption(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolResponse.java new file mode 100644 index 000000000000..3163d46c2b7c --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkPoolResponse.java @@ -0,0 +1,151 @@ +package com.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import java.util.List; + +public class GloboNetworkPoolResponse extends Answer{ + + public GloboNetworkPoolResponse(Command command, boolean success, String details, List pools) { + super(command, success, details); + this.pools = pools; + } + public GloboNetworkPoolResponse(Command command, boolean success, String details, Pool pool) { + super(command, success, details); + this.pool = pool; + } + + public GloboNetworkPoolResponse(List pools) { + this.pools = pools; + } + + public GloboNetworkPoolResponse(Command command,List pools, boolean success, String details) { + super(command, success, details); + this.pools = pools; + } + + public GloboNetworkPoolResponse(Pool pool) { + this.pool = pool; + } + + private List pools; + private Pool pool; + + public List getPools() { + return pools; + } + + public Pool getPool() { + return pool; + } + + public void setPools(List pools) { + this.pools = pools; + } + + public void setPool(Pool pool) { + this.pool = pool; + } + + public static class Pool { + private Long id; + private String identifier; + private String lbMethod; + private Integer port; + private String healthcheckType; + private String expectedHealthcheck; + private String healthcheck; + private Integer maxconn; + private Integer vipPort; + + private String l4protocol; + private String l7protocol; + + public String getL4protocol() { + return l4protocol; + } + + public void setL4protocol(String l4protocol) { + this.l4protocol = l4protocol; + } + + public String getL7protocol() { + return l7protocol; + } + + public void setL7protocol(String l7protocol) { + this.l7protocol = l7protocol; + } + + public String getHealthcheckType() { + return healthcheckType; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public String getExpectedHealthcheck() { + return expectedHealthcheck; + } + + public void setExpectedHealthcheck(String expectedHealthcheck) { + this.expectedHealthcheck = expectedHealthcheck; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getLbMethod() { + return lbMethod; + } + + public void setLbMethod(String lbMethod) { + this.lbMethod = lbMethod; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + + public Integer getMaxconn() { + return maxconn; + } + public void setMaxconn(Integer maxconn) { + this.maxconn = maxconn; + } + + public Integer getVipPort() { + return vipPort; + } + public void setVipPort(Integer vipPort) { + this.vipPort = vipPort; + } + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipExternalResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipExternalResponse.java new file mode 100644 index 000000000000..50322d964727 --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipExternalResponse.java @@ -0,0 +1,208 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.response; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.BaseResponse; + +public class GloboNetworkVipExternalResponse extends BaseResponse { + + private Long id; + + private String name; + + private String ip; + + private List networkids; + + private String cache; + + private String method; + + private String persistence; + + private String healthchecktype; + + private String healthcheck; + + private Integer maxconn; + + private List ports; + + private List reals; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public List getNetworkids() { + return networkids; + } + + public void setNetworkids(List networkids) { + this.networkids = networkids; + } + + public String getCache() { + return cache; + } + + public void setCache(String cache) { + this.cache = cache; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getPersistence() { + return persistence; + } + + public void setPersistence(String persistence) { + this.persistence = persistence; + } + + public String getHealthchecktype() { + return healthchecktype; + } + + public void setHealthchecktype(String healthchecktype) { + this.healthchecktype = healthchecktype; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public Integer getMaxconn() { + return maxconn; + } + + public void setMaxconn(Integer maxconn) { + this.maxconn = maxconn; + } + + public List getPorts() { + return ports; + } + + public void setPorts(List ports) { + this.ports = ports; + } + + public List getReals() { + return reals == null ? new ArrayList() : reals; + } + + public void setReals(List reals) { + this.reals = reals; + } + + public static class Real extends BaseResponse { + private String vmname; + + private String ip; + + private String network; + + private String ports; + + private Boolean state; + + private String nic; + + public String getVmname() { + return vmname; + } + + public void setVmname(String vmname) { + this.vmname = vmname; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getNetwork() { + return network; + } + + public void setNetwork(String network) { + this.network = network; + } + + public String getPorts() { + return ports; + } + + public void setPorts(String ports) { + this.ports = ports; + } + + public Boolean getState() { + return state; + } + + public void setState(Boolean state) { + this.state = state; + } + + public String getNic() { + return nic; + } + + public void setNic(String nic) { + this.nic = nic; + } + } + +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipResponse.java new file mode 100644 index 000000000000..733b79d3315e --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVipResponse.java @@ -0,0 +1,272 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.response; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class GloboNetworkVipResponse extends Answer { + + private Long id; + private String name; + private String ip; + private Long ipId; + private Long lbEnvironmentId; + private String cache; + private String method; + private String persistence; + private String healthcheckType; + private String healthcheck; + private List networkIds; + private Integer maxConn; + private List ports; + private List reals; + private Boolean created; + + public GloboNetworkVipResponse(Command command, Long id, String name, String ip, Long ipId, Long lbEnvironmentId, String network, String cache, String method, + String persistence, String healthcheckType, String healthcheck, Integer maxConn, Collection ports, Collection reals, Boolean created) { + super(command, true, null); + this.id = id; + this.name = name; + this.ip = ip; + this.ipId = ipId; + this.lbEnvironmentId = lbEnvironmentId; + this.cache = cache; + this.method = method; + this.persistence = persistence; + this.healthcheckType = healthcheckType; + this.healthcheck = healthcheck; + this.maxConn = maxConn; + this.ports = new ArrayList(ports); + this.reals = new ArrayList(reals); + this.setCreated(created); + } + public GloboNetworkVipResponse() { + + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIp() { + return ip; + } + + public Long getIpId() { + return this.ipId; + } + + public void setIpId(Long ipId) { + this.ipId = ipId; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getCache() { + return cache; + } + + public void setCache(String cache) { + this.cache = cache; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getPersistence() { + return persistence; + } + + public void setPersistence(String persistence) { + this.persistence = persistence; + } + + public String getHealthcheckType() { + return healthcheckType; + } + + public void setHealthcheckType(String healthcheckType) { + this.healthcheckType = healthcheckType; + } + + public String getHealthcheck() { + return healthcheck; + } + + public void setHealthcheck(String healthcheck) { + this.healthcheck = healthcheck; + } + + public Integer getMaxConn() { + return maxConn; + } + + public void setMaxConn(Integer maxConn) { + this.maxConn = maxConn; + } + + public List getPorts() { + return ports; + } + + public void setPorts(List ports) { + this.ports = ports; + } + + public List getReals() { + return reals; + } + + public void setReals(List reals) { + this.reals = reals; + } + + public List getNetworkIds() { + return networkIds; + } + + public void setNetworkIds(List networkIds) { + this.networkIds = networkIds; + } + + public Boolean getCreated() { + return created; + } + + public void setCreated(Boolean created) { + this.created = created; + } + + public Long getLbEnvironmentId() { + return lbEnvironmentId; + } + + public void setLbEnvironmentId(Long lbEnvironmentId) { + this.lbEnvironmentId = lbEnvironmentId; + } + + public static class Real extends BaseResponse { + private String vmName; + + private String ip; + + private String network; + + private Long environmentId; + + private List ports; + + private Boolean state; + + private String nic; + + private boolean isRevoked; + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getNetwork() { + return network; + } + + public void setNetwork(String network) { + this.network = network; + } + + public List getPorts() { + if (ports == null) { + ports = new ArrayList(); + } + return ports; + } + + public void setPorts(List ports) { + this.ports = ports; + } + + public Boolean getState() { + return state; + } + + public void setState(Boolean state) { + this.state = state; + } + + public String getNic() { + return nic; + } + + public void setNic(String nic) { + this.nic = nic; + } + + public boolean isRevoked() { + return isRevoked; + } + + public void setRevoked(boolean isRevoked) { + this.isRevoked = isRevoked; + } + + public Long getEnvironmentId() { + return environmentId; + } + + public void setEnvironmentId(Long environmentId) { + this.environmentId = environmentId; + } + } +} diff --git a/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVlanResponse.java b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVlanResponse.java new file mode 100644 index 000000000000..4e8d9dc6413b --- /dev/null +++ b/plugins/network-elements/globonetwork/src/com/globo/globonetwork/cloudstack/response/GloboNetworkVlanResponse.java @@ -0,0 +1,139 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.response; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class GloboNetworkVlanResponse extends Answer { + + private Long vlanId; + + private String vlanName; + + private String vlanDescription; + + private Long vlanNum; + + private String mask; + + private String networkAddress; + + private Long networkId; + + private boolean isActive; + + private Integer block; + + private boolean isv6; + + public GloboNetworkVlanResponse(Command command, Long vlanId, String vlanName, String vlanDescription, Long vlanNum, String networkAddress, String mask, + Long networkId, Boolean isActive, Integer block, boolean isv6) { + super(command, true, null); + this.vlanId = vlanId; + this.vlanName = vlanName; + this.vlanDescription = vlanDescription; + this.vlanNum = vlanNum; + this.mask = mask; + this.networkAddress = networkAddress; + this.setNetworkId(networkId); + this.setIsActive(isActive == null ? false : isActive); + this.setBlock(block); + this.isv6 = isv6; + } + + public Long getVlanNum() { + return vlanNum; + } + + public void setVlanNum(Long vlanNum) { + this.vlanNum = vlanNum; + } + + public Long getVlanId() { + return vlanId; + } + + public void setVlanId(Long vlanId) { + this.vlanId = vlanId; + } + + public String getMask() { + return mask; + } + + public void setMask(String mask) { + this.mask = mask; + } + + public String getVlanName() { + return vlanName; + } + + public void setVlanName(String vlanName) { + this.vlanName = vlanName; + } + + public String getVlanDescription() { + return vlanDescription; + } + + public void setVlanDescription(String vlanDescription) { + this.vlanDescription = vlanDescription; + } + + public String getNetworkAddress() { + return networkAddress; + } + + public void setNetworkAddress(String networkAddress) { + this.networkAddress = networkAddress; + } + + public Boolean isActive() { + return isActive; + } + + public void setIsActive(Boolean isActive) { + this.isActive = isActive; + } + + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public boolean isv6() { + return isv6; + } + + public void setIsv6(boolean isv6) { + this.isv6 = isv6; + } + + public Integer getBlock() { + return block; + } + + public void setBlock(Integer block) { + this.block = block; + } + +} diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmdTest.java new file mode 100644 index 000000000000..843166189428 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/CreateGloboNetworkPoolCmdTest.java @@ -0,0 +1,14 @@ +package com.globo.globonetwork.cloudstack.api; + + +import org.junit.Test; + +public class CreateGloboNetworkPoolCmdTest { + + @Test + public void testValidatel4l7Params() { + CreateGloboNetworkPoolCmd cmd = new CreateGloboNetworkPoolCmd(); + + cmd.validateParams(); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmdTest.java new file mode 100644 index 000000000000..1e666ecfe089 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/DeleteNetworkInGloboNetworkCmdTest.java @@ -0,0 +1,89 @@ +package com.globo.globonetwork.cloudstack.api; + + +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkService; +import junit.framework.TestCase; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + + +public class DeleteNetworkInGloboNetworkCmdTest extends TestCase { + + private static final Logger s_logger = Logger.getLogger(DeleteNetworkInGloboNetworkCmdTest.class); + + private DeleteNetworkInGloboNetworkCmd cmd; + + @Before + public void setUp() { + cmd = new DeleteNetworkInGloboNetworkCmd() { + public Long getId() { + return 1l; + } + public boolean isForced() { + return false; + } + }; + } + @After + public void searDown() { + cmd = null; + } + + @Test + public void testExecute() { + GloboNetworkService glbMock = Mockito.mock(GloboNetworkService.class); + Mockito.when(glbMock.destroyGloboNetwork(1l, false)).thenReturn(true); + cmd._glbNetService = glbMock; + + cmd.execute(); + + SuccessResponse response = (SuccessResponse)cmd.getResponseObject(); + assertEquals("deletenetworkresponse", response.getResponseName()); + + Mockito.verify(glbMock, Mockito.times(1)).destroyGloboNetwork(1l, false); + } + + @Test + public void testExecute_fail() { + GloboNetworkService glbMock = Mockito.mock(GloboNetworkService.class); + Mockito.when(glbMock.destroyGloboNetwork(1l, false)).thenReturn(false); + cmd._glbNetService = glbMock; + + try { + cmd.execute(); + } catch (ServerApiException e) { + assertEquals("Failed to delete network", e.getMessage()); + Mockito.verify(glbMock, Mockito.times(1)).destroyGloboNetwork(1l, false); + } catch (Exception e) { + s_logger.error("should be ServerApiException", e); + fail("should be ServerApiException: " + e.getClass() + " " + e.getMessage()); + } + + } + + @Test + public void testExecute_throws() { + GloboNetworkService glbMock = Mockito.mock(GloboNetworkService.class); + Mockito.when(glbMock.destroyGloboNetwork(1l, false)).thenThrow(new CloudRuntimeException("test delete error")); + cmd._glbNetService = glbMock; + + try { + cmd.execute(); + } catch (ServerApiException e) { + assertEquals("test delete error", e.getMessage()); + Mockito.verify(glbMock, Mockito.times(1)).destroyGloboNetwork(1l, false); + } catch (Exception e) { + s_logger.error("should be ServerApiException", e); + fail("should be ServerApiException: " + e.getClass() + " " + e.getMessage()); + } + + } + +} diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmdTest.java new file mode 100644 index 000000000000..d68f61962b26 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/GetGloboNetworkPoolCmdTest.java @@ -0,0 +1,52 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import junit.framework.TestCase; +import org.apache.cloudstack.api.response.PoolResponse; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +public class GetGloboNetworkPoolCmdTest extends TestCase { + + public void testExecute() throws Exception { + GetGloboNetworkPoolCmd cmd = new GetGloboNetworkPoolCmd(); + + GloboNetworkPoolResponse.Pool pool1 = new GloboNetworkPoolResponse.Pool(); + pool1.setId(123l); + pool1.setIdentifier("my_pool"); + pool1.setLbMethod("leastcon"); + pool1.setPort(80); + pool1.setMaxconn(9); + pool1.setHealthcheck("/heal.html"); + pool1.setHealthcheckType("HTTP"); + pool1.setExpectedHealthcheck("OK"); + + + GloboNetworkManager mock = mock(GloboNetworkManager.class); + when(mock.getPoolById(123l, 10l)).thenReturn(pool1); + cmd._globoNetworkService = mock; + + cmd.setPoolId(123l); + cmd.setZoneId(10l); + cmd.execute(); + + PoolResponse poolR = (PoolResponse) cmd.getResponseObject(); + + assertEquals((Long) 123l, poolR.getId()); + assertEquals("my_pool", poolR.getName()); + assertEquals("leastcon", poolR.getLbMethod()); + assertEquals((Integer) 80, poolR.getPort()); + + assertEquals((Integer)9, poolR.getMaxconn()); + assertEquals("/heal.html", poolR.getHealthcheck()); + assertEquals("HTTP", poolR.getHealthcheckType()); + assertEquals("OK", poolR.getExpectedHealthcheck()); + + + verify(mock, times(1)).getPoolById(123l, 10l); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmdTest.java new file mode 100644 index 000000000000..de38d6083378 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkExpectedHealthchecksCmdTest.java @@ -0,0 +1,62 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import java.util.ArrayList; +import java.util.List; +import org.apache.cloudstack.api.response.ExpectedHealthcheckResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +/** + * Created by lucas.castro on 3/7/16. + */ +public class ListGloboNetworkExpectedHealthchecksCmdTest { + + @Test + public void testExecute() throws Exception { + ListGloboNetworkExpectedHealthchecksCmd cmd = new ListGloboNetworkExpectedHealthchecksCmd(); + + List list = new ArrayList<>(); + list.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(3l, "OK")); + list.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(1l, "AC")); + list.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(5l, "WORKING")); + list.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(4l, "ok1")); + list.add(new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(2l, "ac")); + + GloboNetworkManager mock = mock(GloboNetworkManager.class); + when(mock.listAllExpectedHealthchecks()).thenReturn(list); + + cmd._globoNetworkService = mock; + + cmd.execute(); + + ListResponse responseObject = (ListResponse< ExpectedHealthcheckResponse >)cmd.getResponseObject(); + assertNotNull(responseObject); + + assertEquals((Integer)5, responseObject.getCount()); + + List result = responseObject.getResponses(); + + ExpectedHealthcheckResponse expectedHealthcheck = result.get(0); + assertEquals((Long)1l, expectedHealthcheck.getId()); + assertEquals("AC", expectedHealthcheck.getExpected()); + + expectedHealthcheck = result.get(1); + assertEquals((Long)2l, expectedHealthcheck.getId()); + assertEquals("ac", expectedHealthcheck.getExpected()); + + + expectedHealthcheck = result.get(result.size() -1); + assertEquals((Long)5l, expectedHealthcheck.getId()); + assertEquals("WORKING", expectedHealthcheck.getExpected()); + } + + +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmdTest.java new file mode 100644 index 000000000000..dfe626f58a21 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/ListGloboNetworkPoolsCmdTest.java @@ -0,0 +1,63 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.List; +import junit.framework.TestCase; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PoolResponse; + +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + + +public class ListGloboNetworkPoolsCmdTest extends TestCase { + + public void testExecute() throws Exception { + ListGloboNetworkPoolsCmd cmd = new ListGloboNetworkPoolsCmd(); + + List lbResponses = new ArrayList(); + GloboNetworkPoolResponse.Pool pool1 = new GloboNetworkPoolResponse.Pool(); + pool1.setId(123l); + pool1.setIdentifier("my_pool"); + pool1.setLbMethod("leastcon"); + pool1.setPort(80); + lbResponses.add(pool1); + + + GloboNetworkPoolResponse.Pool pool2 = new GloboNetworkPoolResponse.Pool(); + pool2.setId(124l); + pool2.setIdentifier("my_pool_2"); + pool2.setLbMethod("round"); + pool2.setPort(8090); + lbResponses.add(pool2); + + GloboNetworkManager mock = mock(GloboNetworkManager.class); + when(mock.listAllPoolByVipId(123l, 10l)).thenReturn(lbResponses); + cmd._globoNetworkService = mock; + + + cmd.setLbId(123l); + cmd.setZoneId(10l); + cmd.execute(); + + ListResponse list = (ListResponse) cmd.getResponseObject(); + assertEquals((Integer) 2, list.getCount()); + + List pools = list.getResponses(); + + PoolResponse pool11 = pools.get(0); + assertEquals((Long)123l, pool11.getId()); + assertEquals("my_pool", pool11.getName()); + assertEquals("leastcon", pool11.getLbMethod()); + assertEquals((Integer) 80, pool11.getPort()); + + PoolResponse pool21 = pools.get(1); + assertEquals((Long)124l, pool21.getId()); + assertEquals("my_pool_2", pool21.getName()); + assertEquals("round", pool21.getLbMethod()); + assertEquals((Integer) 8090, pool21.getPort()); + + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmdTest.java new file mode 100644 index 000000000000..aff9cf1c3302 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/UpdateGloboNetworkPoolCmdTest.java @@ -0,0 +1,121 @@ +package com.globo.globonetwork.cloudstack.api; + +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.List; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PoolResponse; +import org.junit.Test; + + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + + +public class UpdateGloboNetworkPoolCmdTest { + + @Test + public void testExecute() throws Exception { + UpdateGloboNetworkPoolCmd cmd = new UpdateGloboNetworkPoolCmd(); + cmd._lbService = mock(LoadBalancingRulesService.class); + doNothing().when(cmd._lbService).throwExceptionIfIsChildLoadBalancer(123L, "teste"); + + + List lbResponses = new ArrayList(); + GloboNetworkPoolResponse.Pool pool1 = new GloboNetworkPoolResponse.Pool(); + pool1.setId(12l); + pool1.setHealthcheck("/heal.html"); + pool1.setHealthcheckType("HTTP"); + pool1.setExpectedHealthcheck("OK"); + lbResponses.add(pool1); + + GloboNetworkPoolResponse.Pool pool2 = new GloboNetworkPoolResponse.Pool(); + pool2.setId(13l); + pool2.setHealthcheck("/heal.html"); + pool2.setHealthcheckType("HTTP"); + pool2.setExpectedHealthcheck("OK"); + lbResponses.add(pool2); + + + List ids = new ArrayList(); + ids.add(12l); + ids.add(13l); + + GloboNetworkManager mock = mock(GloboNetworkManager.class); + when(mock.updatePools(ids, 123l, 10l, "HTTP", "/heal.html", "OK", 10, null, null, false)).thenReturn(lbResponses); + cmd._globoNetworkService = mock; + + + cmd.setPoolIds(ids); + cmd.setLbId(123l); + cmd.setZoneId(10l); + cmd.setHealthcheckType("HTTP"); + cmd.setHealthcheck("/heal.html"); + cmd.setExpectedHealthcheck("OK"); + cmd.setMaxConn(10); + cmd.execute(); + + ListResponse list = (ListResponse) cmd.getResponseObject(); + assertEquals((Integer) 2, list.getCount()); + + List pools = list.getResponses(); + + PoolResponse pool11 = pools.get(0); + assertEquals((Long) 12l, pool11.getId()); + assertEquals("HTTP", pool11.getHealthcheckType()); + assertEquals("/heal.html", pool11.getHealthcheck()); + assertEquals("OK", pool11.getExpectedHealthcheck()); + + PoolResponse pool21 = pools.get(1); + assertEquals((Long) 13l, pool21.getId()); + assertEquals("HTTP", pool21.getHealthcheckType()); + assertEquals("/heal.html", pool21.getHealthcheck()); + assertEquals("OK", pool21.getExpectedHealthcheck()); + + + } + + @Test + public void testValidateParamsl4l7Null() { + UpdateGloboNetworkPoolCmd cmd = new UpdateGloboNetworkPoolCmd(); + cmd.validateParams(); + } + + @Test + public void testValidateParamsl4l7Valid() { + UpdateGloboNetworkPoolCmd cmd = new UpdateGloboNetworkPoolCmd(); + cmd.setL4protocol("TCP"); + cmd.setL7protocol("HTTP"); + cmd.setRedeploy(true); + cmd.validateParams(); + + cmd = new UpdateGloboNetworkPoolCmd(); + cmd.setL4protocol("UDP"); + cmd.setL7protocol("Outros"); + cmd.setRedeploy(true); + cmd.validateParams(); + } + + @Test(expected = CloudRuntimeException.class) + public void testValidateParamsl4l7WithRedeployFalse() { + UpdateGloboNetworkPoolCmd cmd = new UpdateGloboNetworkPoolCmd(); + cmd.setL4protocol("TCP"); + cmd.setL7protocol("HTTPS"); + cmd.setRedeploy(false); + cmd.validateParams(); + } + + @Test(expected = CloudRuntimeException.class) + public void testValidateParamsl4l7Whenl4l7dontmatch() { + UpdateGloboNetworkPoolCmd cmd = new UpdateGloboNetworkPoolCmd(); + cmd.setL4protocol("UDP"); + cmd.setL7protocol("HTTPS"); + cmd.setRedeploy(true); + cmd.validateParams(); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmdTest.java new file mode 100644 index 000000000000..c69232157ae0 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/CreateGloboLoadBalancerCmdTest.java @@ -0,0 +1,215 @@ +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.LBHealthCheckPolicyVO; +import com.cloud.network.dao.LBStickinessPolicyVO; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.HashMap; +import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; +import org.junit.Test; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Created by lucas.castro on 11/3/16. + */ +public class CreateGloboLoadBalancerCmdTest { + + CreateGloboLoadBalancerCmd cmd = new CreateGloboLoadBalancerCmd(); + + + + @Test + public void testCreateHealthcheck() throws ResourceUnavailableException { + //when + cmd.setPingPath("/index.html"); + cmd.setEntityId(123l); + cmd.setEntityUuid("54321"); + + CreateLBHealthCheckPolicyCmd cmd1 = new CreateLBHealthCheckPolicyCmd(); + cmd1.setPingPath("/index.html"); + cmd1.setLbRuleId(123l); + + //mock create + LoadBalancingRulesService lbService = mock(LoadBalancingRulesService.class); + LBHealthCheckPolicyVO healthcheckPolicity = new LBHealthCheckPolicyVO(123l, "/index.html",null, 0, 0, 0, 0); + healthcheckPolicity.setUuid("66"); + + when(lbService.createLBHealthCheckPolicy(cmd1)).thenReturn(healthcheckPolicity); + //mock execute + when(lbService.applyLBHealthCheckPolicy(cmd1)).thenReturn(true); + cmd._lbService = lbService; + + //do + cmd.createHealthcheck(); + + //check + verify(lbService, times(1)).createLBHealthCheckPolicy(cmd1); + verify(lbService, times(1)).applyLBHealthCheckPolicy(cmd1); + } + + @Test(expected = RuntimeException.class) + public void testCreateHealthcheckWhenCreateFail() throws ResourceUnavailableException { + //when + cmd.setPingPath("/index.html"); + cmd.setEntityId(123l); + cmd.setEntityUuid("54321"); + + CreateLBHealthCheckPolicyCmd cmd1 = new CreateLBHealthCheckPolicyCmd(); + cmd1.setPingPath("/index.html"); + cmd1.setLbRuleId(123l); + + //mock create + LoadBalancingRulesService lbService = mock(LoadBalancingRulesService.class); + when(lbService.createLBHealthCheckPolicy(cmd1)).thenThrow(new RuntimeException()); + + cmd._lbService = lbService; + + try { + //do + cmd.createHealthcheck(); + + }finally { + //check + verify(lbService, times(1)).createLBHealthCheckPolicy(cmd1); + } + } + + + @Test(expected = CloudRuntimeException.class) + public void testCreateHealthcheckWhenApplyFail() throws ResourceUnavailableException { + //when + cmd.setPingPath("/index.html"); + cmd.setEntityId(123l); + cmd.setEntityUuid("54321"); + + CreateLBHealthCheckPolicyCmd cmd1 = new CreateLBHealthCheckPolicyCmd(); + cmd1.setPingPath("/index.html"); + cmd1.setLbRuleId(123l); + + //mock create + LoadBalancingRulesService lbService = mock(LoadBalancingRulesService.class); + LBHealthCheckPolicyVO healthcheckPolicity = new LBHealthCheckPolicyVO(123l, "/index.html",null, 0, 0, 0, 0); + healthcheckPolicity.setUuid("66"); + + + when(lbService.createLBHealthCheckPolicy(cmd1)).thenReturn(healthcheckPolicity); + //mock execute + + when(lbService.applyLBHealthCheckPolicy(cmd1)).thenThrow(new CloudRuntimeException("error 1")); + cmd._lbService = lbService; + + try { + //do + cmd.createHealthcheck(); + + }finally { + //check + verify(lbService, times(1)).createLBHealthCheckPolicy(cmd1); + verify(lbService, times(1)).applyLBHealthCheckPolicy(cmd1); + } + } + + + + @Test + public void testCreateStickiness() throws ResourceUnavailableException, NetworkRuleConflictException { + //when + cmd.setStickinessMethodName("Cookie"); + cmd.setEntityId(123l); + cmd.setEntityUuid("54321"); + + CreateLBStickinessPolicyCmd cmd1 = new CreateLBStickinessPolicyCmd(); + cmd1.setStickinessMethodName("Cookie"); + cmd1.setLbStickinessPolicyName("Cookie"); + cmd1.setLbRuleId(123l); + + //mock create + LoadBalancingRulesService lbService = mock(LoadBalancingRulesService.class); + LBStickinessPolicyVO stickiness = new LBStickinessPolicyVO(123l, "Cookie", "Cookie", new HashMap(),""); + stickiness.setUuid("66"); + + when(lbService.createLBStickinessPolicy(cmd1)).thenReturn(stickiness); + //mock execute + when(lbService.applyLBStickinessPolicy(cmd1)).thenReturn(true); + cmd._lbService = lbService; + + //do + cmd.createStickiness(); + + //check + verify(lbService, times(1)).createLBStickinessPolicy(cmd1); + verify(lbService, times(1)).applyLBStickinessPolicy(cmd1); + } + + + @Test(expected = CloudRuntimeException.class) + public void testCreateStickinessWhenCreateFail() throws ResourceUnavailableException, NetworkRuleConflictException { + //when + cmd.setStickinessMethodName("Cookie"); + cmd.setEntityId(123l); + cmd.setEntityUuid("54321"); + + CreateLBStickinessPolicyCmd cmd1 = new CreateLBStickinessPolicyCmd(); + cmd1.setStickinessMethodName("Cookie"); + cmd1.setLbStickinessPolicyName("Cookie"); + cmd1.setLbRuleId(123l); + + //mock create + LoadBalancingRulesService lbService = mock(LoadBalancingRulesService.class); + LBStickinessPolicyVO stickiness = new LBStickinessPolicyVO(123l, "Cookie", "Cookie", new HashMap(),""); + stickiness.setUuid("66"); + + when(lbService.createLBStickinessPolicy(cmd1)).thenThrow(new CloudRuntimeException("error stickiness")); + cmd._lbService = lbService; + + try { + //do + cmd.createStickiness(); + } finally { + //check + verify(lbService, times(1)).createLBStickinessPolicy(cmd1); + } + } + + @Test + public void testIsToCreateHealthcheck() { + boolean tocreate = cmd.isToCreateHealthcheck(); + assertFalse(tocreate); + + + cmd.setPingPath(""); + tocreate = cmd.isToCreateHealthcheck(); + assertFalse(tocreate); + + + cmd.setPingPath("/index.html"); + tocreate = cmd.isToCreateHealthcheck(); + assertTrue(tocreate); + } + + @Test + public void testIsToCreateStickiness() { + boolean tocreate = cmd.isToCreateStickiness(); + assertFalse(tocreate); + + + cmd.setStickinessMethodName(""); + tocreate = cmd.isToCreateStickiness(); + assertFalse(tocreate); + + + cmd.setStickinessMethodName("Cookie"); + tocreate = cmd.isToCreateStickiness(); + assertTrue(tocreate); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmdTest.java new file mode 100644 index 000000000000..441b11312469 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/LinkGloboLoadBalancerCmdTest.java @@ -0,0 +1,69 @@ +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.globo.globonetwork.cloudstack.manager.GloboLoadBalancerService; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; +import org.apache.cloudstack.api.ResponseGenerator; + +import org.apache.cloudstack.api.response.LoadBalancerResponse; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LinkGloboLoadBalancerCmdTest { + + @Test + public void execute() throws Exception { + //mocks + LinkGloboLoadBalancerCmd cmd = new LinkGloboLoadBalancerCmd(); + cmd.setChildlbid(123L); + cmd.setParentlbid(456L); + + cmd.globoLBService = mock(GloboLoadBalancerService.class); + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setName("test.lb.com"); + lb.setUuid("123123"); + when(cmd.globoLBService.linkLoadBalancer(123L, 456L)).thenReturn(lb); + + + cmd._responseGenerator = mock(ResponseGenerator.class); + LoadBalancerResponse lbResponse = new LoadBalancerResponse(); + lbResponse.setName("test.lb.com"); + when(cmd._responseGenerator.createLoadBalancerResponse(lb)).thenReturn(lbResponse); + + cmd.globoNetworkSvc = mock(GloboNetworkManager.class); + GloboResourceConfigurationVO globoResourceConfi = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, "123123", GloboResourceKey.linkedLoadBalancer, "456456"); + when(cmd.globoNetworkSvc.getGloboResourceConfiguration("123123", GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer)).thenReturn(globoResourceConfi); + + cmd._lbService = mock(LoadBalancingRulesService.class); + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setUuid("456456"); + targetLb.setName("target.lb.com"); + + when(cmd._lbService.findByUuid("456456")).thenReturn(targetLb); + + //execute + cmd.execute(); + + + //check + Object obj = cmd.getResponseObject(); + assertTrue(obj instanceof LoadBalancerResponse); + + LoadBalancerResponse lbResponseReturned = (LoadBalancerResponse) obj; + assertEquals("test.lb.com", lbResponseReturned.getName()); + + LoadBalancerResponse.LinkedLoadBalancer linkedLoadBalancer = lbResponseReturned.getLinkedparent(); + assertEquals("456456", linkedLoadBalancer.getUuid()); + assertEquals("target.lb.com", linkedLoadBalancer.getName()); + + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmdTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmdTest.java new file mode 100644 index 000000000000..dbd2632d55d3 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/api/loadbalancer/ListGloboLinkableLoadBalancersCmdTest.java @@ -0,0 +1,46 @@ +package com.globo.globonetwork.cloudstack.api.loadbalancer; + +import com.cloud.network.dao.LoadBalancerVO; +import com.globo.globonetwork.cloudstack.api.response.LinkableLoadBalancerResponse; +import com.globo.globonetwork.cloudstack.manager.GloboNetworkManager; + +import junit.framework.TestCase; +import org.apache.cloudstack.api.response.ListResponse; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ListGloboLinkableLoadBalancersCmdTest extends TestCase { + + public void testExecute() throws Exception { + + ListGloboLinkableLoadBalancersCmd cmd = new ListGloboLinkableLoadBalancersCmd(); + cmd.setLbid(123L); + cmd.setProjectId(33L); + cmd.globoNetworkSvc = mock(GloboNetworkManager.class); + List list = new ArrayList<>(); + LoadBalancerVO l = new LoadBalancerVO(null, "lb123", "descrip", 1L, 0, 0, "leastcon", 3L, 1L, 1L, "TCP"); + l.setUuid("id_123"); + list.add(l); + + when(cmd.globoNetworkSvc.listLinkableLoadBalancers(123L, 33L)).thenReturn(list); + + cmd.execute(); + + Object result = cmd.getResponseObject(); + + assertTrue(result instanceof ListResponse); + ListResponse listResponse = (ListResponse)result; + assertEquals("listlinkableloadbalancerresponse", listResponse.getResponseName()); + + assertEquals(1, listResponse.getResponses().size()); + + LinkableLoadBalancerResponse llb = (LinkableLoadBalancerResponse)listResponse.getResponses().get(0); + assertEquals("lb123", llb.getName()); + assertEquals("id_123", llb.getUuid()); + + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommandTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommandTest.java new file mode 100644 index 000000000000..58a9de8373c8 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/commands/UpdatePoolCommandTest.java @@ -0,0 +1,79 @@ +package com.globo.globonetwork.cloudstack.commands; + + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +import static com.globo.globonetwork.cloudstack.resource.GloboNetworkResourceTest.mockPool; +import static com.globo.globonetwork.cloudstack.resource.GloboNetworkResourceTest.mockPoolSave; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class UpdatePoolCommandTest { + + private UpdatePoolCommand cmd; + private GloboNetworkAPI gnAPI; + + @Before + public void setUp(){ + gnAPI = mock(GloboNetworkAPI.class); + when(gnAPI.getPoolAPI()).thenReturn(mock(PoolAPI.class)); + } + + @Test + public void testExecuteUpdatePool() throws GloboNetworkException { + cmd = new UpdatePoolCommand(Arrays.asList(12L, 13L), "HTTP", "GET /heal HTTP/1.0\\r\\nHost: vip.domain.com\\r\\n\\r\\n", "OK", 5, "vip.domain.com"); + + PoolV3 pool1 = mockPool(12L, "ACS_POOL_vip.domain.com_8080", 8080, "least", "http", "/heal.html", "OK", "*:*", 10); + + + List poolMembers = pool1.getPoolMembers(); + PoolV3.PoolMember poolMember = new PoolV3.PoolMember(); + poolMember.setLimit(10); + poolMembers.add(poolMember); + + + PoolV3 pool2 = mockPool(13L, "ACS_POOL_vip.domain.com_8443", 8443, "least", "http", "/heal.html", "OK", "*:*", 10); + List poolsResponse = new ArrayList<>(); + poolsResponse.add(pool1); + poolsResponse.add(pool2); + + when(gnAPI.getPoolAPI().getByIdsV3(Arrays.asList(12L, 13L))).thenReturn(poolsResponse); + + mockPoolSave(12L, 12L, true, 80, 8080, "10.0.0.1", "HTTP", "GET /heal HTTP/1.0\\r\\nHost: vip.domain.com\\r\\n\\r\\n", "OK", 5, "none" , gnAPI); + mockPoolSave(13L, 13L, true, 443, 8443, "10.0.0.1", "HTTP", "GET /heal HTTP/1.0\\r\\nHost: vip.domain.com\\r\\n\\r\\n", "OK", 5, "none", gnAPI ); + + Answer answer = cmd.execute(gnAPI); + + List pools = ((GloboNetworkPoolResponse)answer).getPools(); + + assertEquals(2, pools.size()); + + GloboNetworkPoolResponse.Pool pool = pools.get(0); + assertEquals((Long) 12L, pool.getId()); + assertEquals((Integer)5, pool.getMaxconn()); + assertEquals("HTTP", pool.getHealthcheckType()); + assertEquals("GET /heal HTTP/1.0\\r\\nHost: vip.domain.com\\r\\n\\r\\n", pool.getHealthcheck()); + assertEquals("OK", pool.getExpectedHealthcheck()); + + assertEquals((Integer)5, poolMember.getLimit()); + + + pool = pools.get(1); + assertEquals((Long) 13L, pool.getId()); + assertEquals((Integer)5, pool.getMaxconn()); + assertEquals("HTTP", pool.getHealthcheckType()); + assertEquals("GET /heal HTTP/1.0\\r\\nHost: vip.domain.com\\r\\n\\r\\n", pool.getHealthcheck()); + assertEquals("OK", pool.getExpectedHealthcheck()); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManagerTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManagerTest.java new file mode 100644 index 000000000000..e21024abe72a --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboLoadBalancerManagerTest.java @@ -0,0 +1,223 @@ +package com.globo.globonetwork.cloudstack.manager; + +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.LoadBalancerVMMapVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.globoconfig.GloboResourceConfiguration; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GloboLoadBalancerManagerTest { + + GloboLoadBalancerManager service; + @Before + public void setup() { + service = new GloboLoadBalancerManager(); + } + + + @Test(expected = CloudRuntimeException.class) + public void testCheckIfSourceLBHasVmsItHasOne() { + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setUuid("123123"); + lb.setName("test.lb.com"); + lb.setId(123L); + + service._lb2VmMapDao = mock(LoadBalancerVMMapDao.class); + List listVms = new ArrayList<>(); + LoadBalancerVMMapVO vm = new LoadBalancerVMMapVO(); + listVms.add(vm); + when(service._lb2VmMapDao.listByLoadBalancerId(123L)).thenReturn(listVms); + + + service.checkIfSourceLBHasVms(lb); + } + + @Test + public void testCheckIfSourceLBHasVms() { + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setUuid("123123"); + lb.setName("test.lb.com"); + lb.setId(123L); + + service._lb2VmMapDao = mock(LoadBalancerVMMapDao.class); + List listVms = new ArrayList<>(); + when(service._lb2VmMapDao.listByLoadBalancerId(123L)).thenReturn(listVms); + + service.checkIfSourceLBHasVms(lb); + + verify(service._lb2VmMapDao, times(1)).listByLoadBalancerId(123L); + } + + @Test() + public void testCheckIfLBAlreadyIsLinkedItIsNotLinked() throws Exception { + + service.lbService = mock(LoadBalancingRulesService.class); + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setUuid("123123"); + lb.setName("test.lb.com"); + when(service.lbService.findById(123L)).thenReturn(lb); + + service.globoNetworkSvc = mock(GloboNetworkManager.class); + when(service.globoNetworkSvc.getGloboResourceConfiguration("123123", + GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer)).thenReturn(null); + + LoadBalancer lbReturned = service.checkIfLBAlreadyIsLinked(123L); + + assertEquals("test.lb.com", lbReturned.getName()); + assertEquals("123123", lbReturned.getUuid()); + } + + @Test(expected = CloudRuntimeException.class) + public void testCheckIfLBAlreadyIsLinkedItIsLinked() throws Exception { + + service.lbService = mock(LoadBalancingRulesService.class); + LoadBalancerVO lb = new LoadBalancerVO(); + lb.setUuid("123123"); + lb.setName("test.lb.com"); + when(service.lbService.findById(123L)).thenReturn(lb); + + service.globoNetworkSvc = mock(GloboNetworkManager.class); + GloboResourceConfigurationVO config = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, "123123", + GloboResourceKey.linkedLoadBalancer, "789789"); + + when(service.globoNetworkSvc.getGloboResourceConfiguration("123123", + GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer)).thenReturn(config); + + LoadBalancerVO targetExisted = new LoadBalancerVO(); + targetExisted.setUuid("789789"); + targetExisted.setName("targetExisted.lb.com"); + when(service.lbService.findByUuid("789789")).thenReturn(targetExisted); + + + service.checkIfLBAlreadyIsLinked(123L); + } + + @Test + public void testRegister() { + LoadBalancerVO sourceLb = new LoadBalancerVO(); + sourceLb.setUuid("123123"); + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setUuid("456456"); + + service.resourceConfigDao = mock(GloboResourceConfigurationDao.class); + GloboResourceConfigurationVO config = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, sourceLb.getUuid(), GloboResourceKey.linkedLoadBalancer, targetLb.getUuid()); + + GloboResourceConfigurationVO configResult = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, sourceLb.getUuid(), GloboResourceKey.linkedLoadBalancer, targetLb.getUuid()); + configResult.setId(1L); + when(service.resourceConfigDao.persist(config)).thenReturn(configResult); + + GloboResourceConfiguration configPersisted = service.registerLink(sourceLb, targetLb); + assertEquals("456456", configPersisted.getValue()); + assertEquals("123123", configPersisted.getResourceUuid()); + assertEquals(1L, configPersisted.getId().longValue()); + + verify(service.resourceConfigDao, times(1)).persist(any(GloboResourceConfigurationVO.class)); + + } + + @Test + public void testGetNetworksToAddIntoSourceAllLbsWithOneNetwork() { + LoadBalancerVO sourceLb = new LoadBalancerVO(); + sourceLb.setNetworkId(12L); + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setNetworkId(12L); + + LoadBalancingRule lbRule = new LoadBalancingRule(sourceLb, null, null, null, null); + lbRule.setAdditionalNetworks(new ArrayList()); + + LoadBalancingRule targetRule = new LoadBalancingRule(targetLb, null, null, null ,null); + targetRule.setAdditionalNetworks(new ArrayList()); + + List diffNetworks = this.service.getNetworksToAddIntoSource(lbRule, targetRule); + + assertTrue(diffNetworks.isEmpty()); + } + + @Test + public void testGetNetworksToAddIntoSourceLbWith2NetworksAndTargetWithOne() { + LoadBalancerVO sourceLb = new LoadBalancerVO(); + sourceLb.setNetworkId(12L); + + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setNetworkId(12L); + + LoadBalancingRule lbRule = new LoadBalancingRule(sourceLb, null, null, null, null); + List additionalNetworks = new ArrayList<>(Arrays.asList(5L)); + lbRule.setAdditionalNetworks(additionalNetworks); + + LoadBalancingRule targetRule = new LoadBalancingRule(targetLb, null, null, null ,null); + targetRule.setAdditionalNetworks(new ArrayList()); + List diffNetworks = this.service.getNetworksToAddIntoSource(lbRule, targetRule); + + //it is not necessery to add networks into sourceLb + assertTrue(diffNetworks.isEmpty()); + } + + @Test + public void testGetNetworksToAddIntoSourceLbWith2NetworksAndTargetWith2() { + LoadBalancerVO sourceLb = new LoadBalancerVO(); + sourceLb.setNetworkId(12L); + + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setNetworkId(12L); + + LoadBalancingRule lbRule = new LoadBalancingRule(sourceLb, null, null, null, null); + List additionalNetworks = new ArrayList<>(Arrays.asList(5L,6L)); + lbRule.setAdditionalNetworks(additionalNetworks); + + LoadBalancingRule targetRule = new LoadBalancingRule(targetLb, null, null, null ,null); + targetRule.setAdditionalNetworks(new ArrayList(Arrays.asList(8L, 6L))); + List diffNetworks = this.service.getNetworksToAddIntoSource(lbRule, targetRule); + + //only need to add network 8L + assertFalse(diffNetworks.isEmpty()); + assertEquals(1, diffNetworks.size()); + assertTrue(diffNetworks.contains(8L)); + } + + @Test + public void testGetNetworksToAddIntoSourceLbFirstNetworkIsDifferent() { + LoadBalancerVO sourceLb = new LoadBalancerVO(); + sourceLb.setNetworkId(15L); + + LoadBalancerVO targetLb = new LoadBalancerVO(); + targetLb.setNetworkId(12L); + + LoadBalancingRule lbRule = new LoadBalancingRule(sourceLb, null, null, null, null); + List additionalNetworks = new ArrayList<>(Arrays.asList(5L,6L)); + lbRule.setAdditionalNetworks(additionalNetworks); + + LoadBalancingRule targetRule = new LoadBalancingRule(targetLb, null, null, null ,null); + targetRule.setAdditionalNetworks(new ArrayList(Arrays.asList(8L, 6L))); + List diffNetworks = this.service.getNetworksToAddIntoSource(lbRule, targetRule); + + //only need to add network 8L + assertFalse(diffNetworks.isEmpty()); + assertEquals(2, diffNetworks.size()); + assertTrue(diffNetworks.contains(8L)); + assertTrue(diffNetworks.contains(12L)); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboNetworkManagerTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboNetworkManagerTest.java new file mode 100644 index 000000000000..3f6d8b27efe8 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/GloboNetworkManagerTest.java @@ -0,0 +1,1291 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.manager; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyMapOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerOptionsDao; +import com.cloud.network.dao.LoadBalancerOptionsVO; +import com.cloud.network.dao.LoadBalancerPortMapDao; +import com.cloud.network.dao.LoadBalancerPortMapVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.utils.net.Ip; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.globo.globodns.cloudstack.element.GloboDnsTO; +import com.globo.globonetwork.cloudstack.GloboNetworkIpDetailVO; +import com.globo.globonetwork.cloudstack.api.CreateGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.api.DeleteGloboNetworkPoolCmd; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.CreatePoolCommand; +import com.globo.globonetwork.cloudstack.commands.DeletePoolCommand; +import com.globo.globonetwork.cloudstack.commands.GetPoolLBByIdCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolLBCommand; +import com.globo.globonetwork.cloudstack.commands.UpdatePoolCommand; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.cloud.network.Network; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.utils.exception.CloudRuntimeException; +import com.globo.globonetwork.client.model.Vlan; +import com.globo.globonetwork.cloudstack.GloboNetworkLoadBalancerEnvironment; +import com.globo.globonetwork.cloudstack.GloboNetworkNetworkVO; +import com.globo.globonetwork.cloudstack.commands.ActivateNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GetVlanInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.GloboNetworkErrorAnswer; +import com.globo.globonetwork.cloudstack.commands.ListPoolOptionsCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveNetworkInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.exception.CloudstackGloboNetworkException; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.CloudException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network.Provider; +import com.cloud.network.NetworkModel; +import com.cloud.network.lb.LoadBalancingRulesManager; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.UserVO; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.vm.dao.VMInstanceDao; +import com.globo.globodns.cloudstack.element.GloboDnsElementService; +import com.globo.globonetwork.cloudstack.GloboNetworkEnvironmentVO; +import com.globo.globonetwork.cloudstack.commands.CreateNewVlanInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.DeallocateVlanFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkEnvironmentDao; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkIpDetailDao; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkLoadBalancerEnvironmentDAO; +import com.globo.globonetwork.cloudstack.dao.GloboNetworkNetworkDao; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVlanResponse; + +public class GloboNetworkManagerTest { + + private static final long zoneId = 5L; + private static final long networkOfferingId = 10L; + private static final long globoNetworkEnvironmentId = 120L; + private static final long physicalNetworkId = 200L; + private static final long globoNetworkHostId = 7L; + private static final long domainId = 10L; + private AccountVO acct = null; + private UserVO user = null; + + private GloboNetworkManager _globoNetworkService; + + @Mock + DataCenterDao _dcDao; + + @Mock + PhysicalNetworkDao _physicalNetworkDao; + + @Mock + GloboNetworkEnvironmentDao _globoNetworkEnvironmentDao; + + @Mock + GloboNetworkLoadBalancerEnvironmentDAO _globoNetworkLBEnvironmentDao; + + @Mock + GloboNetworkNetworkDao _globoNetworkNetworkDao; + + @Mock + VMInstanceDao _vmDao; + + @Mock + NetworkModel _networkManager; + + @Mock + HostDao _hostDao; + + @Mock + ConfigurationDao _configDao; + + @Mock + AgentManager _agentMgr; + + @Mock + ResourceManager _resourceMgr; + + @Mock + AccountManager _acctMgr; + + @Mock + GloboNetworkIpDetailDao _globoNetworkIpDetailDao; + + @Mock + IPAddressDao _ipAddrDao; + + @Mock + LoadBalancerOptionsDao _lbOptionsDao; + + @Mock + GloboResourceConfigurationDao _globoResourceConfigurationDao; + + @Mock + GloboDnsElementService _globoDnsService; + + @Before + public void testSetUp() { + MockitoAnnotations.initMocks(this); + + _globoNetworkService = spy(new GloboNetworkManager()); + acct = new AccountVO(200L); + acct.setType(Account.ACCOUNT_TYPE_NORMAL); + acct.setAccountName("user"); + acct.setDomainId(domainId); + + user = new UserVO(); + user.setUsername("user"); + user.setAccountId(acct.getAccountId()); + + _globoNetworkService._dcDao = _dcDao; + _globoNetworkService._physicalNetworkDao = _physicalNetworkDao; + _globoNetworkService._globoNetworkEnvironmentDao = _globoNetworkEnvironmentDao; + _globoNetworkService._globoNetworkLBEnvironmentDao = _globoNetworkLBEnvironmentDao; + _globoNetworkService._globoNetworkNetworkDao = _globoNetworkNetworkDao; + _globoNetworkService._vmDao = _vmDao; + _globoNetworkService._networkManager = _networkManager; + _globoNetworkService._hostDao = _hostDao; + _globoNetworkService._configDao = _configDao; + _globoNetworkService._agentMgr = _agentMgr; + _globoNetworkService._resourceMgr = _resourceMgr; + _globoNetworkService._accountMgr = _acctMgr; + _globoNetworkService._ipAddrDao = _ipAddrDao; + _globoNetworkService._globoNetworkIpDetailDao = _globoNetworkIpDetailDao; + _globoNetworkService._lbOptionsDao = _lbOptionsDao; + _globoNetworkService._globoResourceConfigurationDao = _globoResourceConfigurationDao; + _globoNetworkService._globoDnsService = _globoDnsService; + + CallContext.register(user, acct); + when(_acctMgr.getSystemAccount()).thenReturn(this.acct); + when(_acctMgr.getSystemUser()).thenReturn(this.user); + } + + @After + public void testTearDown() { + CallContext.unregister(); + acct = null; + } + + @Test + public void revertGloboNetworkCreationWhenFailureNetworkCreation() throws CloudException { + + DataCenterVO dc = new DataCenterVO(0L, null, null, null, null, null, null, null, null, null, null, null, null); + when(_dcDao.findById(anyLong())).thenReturn(dc); + + List pNtwList = new ArrayList<>(); + pNtwList.add(new PhysicalNetworkVO(physicalNetworkId, zoneId, null, null, null, null, null)); + when(_physicalNetworkDao.listByZone(zoneId)).thenReturn(pNtwList); + String networkName = "MockTestNetwork"; + when(_globoNetworkEnvironmentDao.findByPhysicalNetworkIdAndEnvironmentId(physicalNetworkId, globoNetworkEnvironmentId)).thenReturn( + new GloboNetworkEnvironmentVO(physicalNetworkId, networkName, globoNetworkEnvironmentId)); + + HostVO napiHost = new HostVO(globoNetworkHostId, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, zoneId, null, 0L, 0L, + null, null, null, 0L, null); + when(_hostDao.findByTypeNameAndZoneId(zoneId, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(napiHost); + + Answer answer = new GloboNetworkVlanResponse(new CreateNewVlanInGloboNetworkCommand(), null, null, null, null, null, null, null, false, null, false); + when(_agentMgr.easySend(eq(globoNetworkHostId), any(CreateNewVlanInGloboNetworkCommand.class))).thenReturn(answer); + + when(_physicalNetworkDao.findById(physicalNetworkId)).thenReturn(null); + + try { + _globoNetworkService.createNetwork(networkName, networkName, zoneId, networkOfferingId, globoNetworkEnvironmentId, null, ACLType.Domain, null, null, null, null, true, + null, false, null); + // This command must throw InvalidParameterValueException, otherwise fails + Assert.fail(); + } catch (ResourceAllocationException e) { + verify(_agentMgr, atLeastOnce()).easySend(eq(globoNetworkHostId), any(DeallocateVlanFromGloboNetworkCommand.class)); + } + } + + @Test + public void checkPermissionsBeforeCreatingVlanOnGloboNetwork() throws CloudException { + try { + when(_acctMgr.finalizeOwner(eq(acct), eq(acct.getAccountName()), eq(domainId), anyLong())).thenThrow(new PermissionDeniedException("")); + + acct.setDomainId(domainId + 1); + _globoNetworkService.createNetwork("net-name", "display-name", zoneId, networkOfferingId, globoNetworkEnvironmentId, null, ACLType.Domain, acct.getAccountName(), null, + domainId, null, true, null, false, null); + fail(); + } catch (PermissionDeniedException e) { + verify(_agentMgr, never()).easySend(any(Long.class), any(Command.class)); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void addGloboNetworkHostInvalidParameters() throws CloudException { + CallContext.register(user, acct); + _globoNetworkService.addGloboNetworkHost(physicalNetworkId, null, null, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void addGloboNetworkHostEmptyParameters() throws CloudException { + CallContext.register(user, acct); + _globoNetworkService.addGloboNetworkHost(physicalNetworkId, "", "", ""); + } + + @Test + public void addGloboNetworkHost() throws CloudException { + + String username = "testUser"; + String password = "testPwd"; + String url = "testUrl"; + + PhysicalNetworkVO pNtwk = new PhysicalNetworkVO(physicalNetworkId, zoneId, null, null, null, null, null); + when(_physicalNetworkDao.findById(physicalNetworkId)).thenReturn(pNtwk); + + HostVO globoNetworkHost = new HostVO(1L, "GloboNetwork", null, "Up", "L2Networking", "", null, null, "", null, null, null, null, null, null, null, null, zoneId, null, 0L, + 0L, null, null, null, 0L, null); + + when(_resourceMgr.addHost(eq(zoneId), any(ServerResource.class), eq(Host.Type.L2Networking), anyMapOf(String.class, String.class))).thenReturn(globoNetworkHost); + + TransactionLegacy tx = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + try { + CallContext.register(user, acct); + + Host host = _globoNetworkService.addGloboNetworkHost(physicalNetworkId, username, password, url); + assertNotNull(host); + assertEquals(host.getDataCenterId(), zoneId); + assertEquals(host.getName(), "GloboNetwork"); + } finally { + tx.rollback(); + } + } + + @Test + public void testLbPortMapValidationWithNoAdditionalPortMap() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + try { + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + }catch(Exception e){ + fail(); + } + } + + @Test + public void testLbPortMapValidationWithOneAdditionalPortMap() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("443:8443")); + + try { + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + }catch(Exception e){ + fail(); + } + } + + @Test + public void testLbPortMapValidationWithMoreThanOneAdditionalPortMap() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Arrays.asList("443:8443", "22:22")); + + try { + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + }catch(Exception e){ + fail(); + } + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithAdditionalPortMapEqualsMainPortMap() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("80:8080")); + + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + fail(); + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithDuplicatedAdditionalPortMap() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Arrays.asList("443:8443", "443:8443")); + + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + fail(); + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithNoPrivatePort() { + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("80:")); + + new GloboNetworkManager().validatePortMaps(loadBalancingRule); + fail(); + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithInvalidPortMapFormat() { + GloboNetworkManager globoNetworkManager = new GloboNetworkManager(); + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("----")); + + globoNetworkManager.validatePortMaps(loadBalancingRule); + fail(); + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithEmptyPortMapValue() { + GloboNetworkManager globoNetworkManager = new GloboNetworkManager(); + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("")); + + globoNetworkManager.validatePortMaps(loadBalancingRule); + fail(); + } + + @Test(expected=InvalidParameterValueException.class) + public void testLbPortMapValidationWithNotNumericValue() { + GloboNetworkManager globoNetworkManager = new GloboNetworkManager(); + LoadBalancingRule loadBalancingRule = createLoadBalancerRule(); + loadBalancingRule.setAdditionalPortMap(Collections.singletonList("abc:abc")); + + globoNetworkManager.validatePortMaps(loadBalancingRule); + fail(); + } + + @Test + public void testListPoolOptions(){ + NetworkVO network = new NetworkVO(); + GloboNetworkPoolOptionResponse.PoolOption option = new GloboNetworkPoolOptionResponse.PoolOption(1L, "reset"); + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + mockAgentManagerSend(network, ListPoolOptionsCommand.class, new GloboNetworkPoolOptionResponse(null, Collections.singletonList(option))); + + List poolOptions = _globoNetworkService.listPoolOptions(45L, network.getId(), "ServiceDownAction"); + assertEquals(1, poolOptions.size()); + assertEquals(new Long(1L), poolOptions.get(0).getId()); + assertEquals("reset", poolOptions.get(0).getName()); + } + + @Test + public void testListPoolOptionsGivenEmptyList(){ + NetworkVO network = new NetworkVO(); + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + mockAgentManagerSend(network, ListPoolOptionsCommand.class, new GloboNetworkPoolOptionResponse(null, new ArrayList())); + List poolOptions = _globoNetworkService.listPoolOptions(45L, network.getId(), "ServiceDownAction"); + assertTrue(poolOptions.isEmpty()); + } + + @Test + public void testListPoolOptionsGivenEmptyNetworkId(){ + try{ + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + _globoNetworkService.listPoolOptions(45L, null, "ServiceDownAction"); + }catch(InvalidParameterValueException e){ + assertEquals("Invalid Network ID", e.getMessage()); + } + } + + @Test + public void testListPoolOptionsGivenInvalidNetworkId(){ + try{ + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + when(_globoNetworkService._networkManager.getNetwork(1L)).thenReturn(null); + _globoNetworkService.listPoolOptions(45L, 1L, "ServiceDownAction"); + }catch(InvalidParameterValueException e){ + assertEquals("Cannot find network with ID : 1", e.getMessage()); + } + } + + @Test + public void testListPoolOptionsGivenEmptyEnvironmentId(){ + try{ + _globoNetworkService.listPoolOptions(null, 1L, "ServiceDownAction"); + }catch(InvalidParameterValueException e){ + assertEquals("Invalid LB Environment ID", e.getMessage()); + } + } + + @Test + public void testListPoolOptionsGivenInvalidEnvironmentId(){ + try{ + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(null); + _globoNetworkService.listPoolOptions(45L, 1L, "ServiceDownAction"); + }catch(InvalidParameterValueException e){ + assertEquals("Could not find mapping to LB environment 45" , e.getMessage()); + } + } + + @Test + public void testListPoolOptionsGivenGenericError(){ + try{ + NetworkVO network = new NetworkVO(); + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + mockAgentManagerSend(network, ListPoolOptionsCommand.class, new Answer(new ListPoolOptionsCommand(1L, "ServiceDownAction"), false, "Error")); + _globoNetworkService.listPoolOptions(45L, network.getId(), "ServiceDownAction"); + }catch(CloudRuntimeException e){ + assertEquals("Error" , e.getMessage()); + } + } + + @Test + public void testListPoolOptionsGivenNetworkApiError(){ + try{ + NetworkVO network = new NetworkVO(); + when(_globoNetworkService._globoNetworkLBEnvironmentDao.findById(45L)).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + mockAgentManagerSend(network, ListPoolOptionsCommand.class, new GloboNetworkErrorAnswer(new ListPoolOptionsCommand(1L, "ServiceDownAction"), 404, "Error")); + _globoNetworkService.listPoolOptions(45L, network.getId(), "ServiceDownAction"); + }catch(CloudstackGloboNetworkException e){ + assertEquals("Error" , e.getNapiDescription()); + assertEquals(404 , e.getNapiCode()); + } + } + + @Test + public void testImplementNetwork() throws javax.naming.ConfigurationException { + Network network = new NetworkVO(); + GloboNetworkVlanResponse vlanResponse = new GloboNetworkVlanResponse(new GetVlanInfoFromGloboNetworkCommand(), 1L, "vlan", "vlandesc", 1L, "172.20.1.0", "255.255.255.0", 1L, false, 24, false); + GloboNetworkNetworkVO globoNetwork = new GloboNetworkNetworkVO(1L, 1L, 1L); + + when(_globoNetworkService._globoNetworkNetworkDao.findByNetworkId(network.getId())).thenReturn(globoNetwork); + doReturn(vlanResponse).when(_globoNetworkService).getVlanFromGloboNetwork(network, 1L); + mockAgentManagerSend(network, ActivateNetworkCommand.class, new Answer(null, true, "")); + + _globoNetworkService.implementNetwork(network); + verify(_globoNetworkService._agentMgr, atLeastOnce()).easySend(any(Long.class), any(ActivateNetworkCommand.class)); + } + + @Test(expected = CloudRuntimeException.class) + public void testImplementNetworkGivenInvalidVlan() throws javax.naming.ConfigurationException { + Network network = new NetworkVO(); + when(_globoNetworkService._globoNetworkNetworkDao.findByNetworkId(network.getId())).thenReturn(null); + _globoNetworkService.implementNetwork(network); + } + + @Test + public void testImplementNetworkGivenVlanAlreadyActivated() throws javax.naming.ConfigurationException { + Network network = new NetworkVO(); + GloboNetworkVlanResponse vlanResponse = new GloboNetworkVlanResponse(new GetVlanInfoFromGloboNetworkCommand(), 1L, "vlan", "vlandesc", 1L, "172.20.1.0", "255.255.255.0", 1L, true, 24, false); + GloboNetworkNetworkVO globoNetwork = new GloboNetworkNetworkVO(1L, 1L, 1L); + + when(_globoNetworkService._globoNetworkNetworkDao.findByNetworkId(network.getId())).thenReturn(globoNetwork); + doReturn(vlanResponse).when(_globoNetworkService).getVlanFromGloboNetwork(network, 1L); + mockAgentManagerSend(network, ActivateNetworkCommand.class, new Answer(null, true, "")); + + _globoNetworkService.implementNetwork(network); + verify(_globoNetworkService._agentMgr, never()).easySend(any(Long.class), any(ActivateNetworkCommand.class)); + } + + @Test + public void testRemoveNetwork(){ + Network network = new NetworkVO(); + Vlan vlan = new Vlan(); + GloboNetworkVlanResponse vlanResponse = new GloboNetworkVlanResponse(new GetVlanInfoFromGloboNetworkCommand(), 1L, "vlan", "vlandesc", 1L, "172.20.1.0", "255.255.255.0", 1L, true, 24, false); + + doReturn(vlan).when(_globoNetworkService).getVlanInfoFromGloboNetwork(network); + doReturn(vlanResponse).when(_globoNetworkService).getVlanFromGloboNetwork(network, vlan.getId()); + mockAgentManagerSend(network, RemoveNetworkInGloboNetworkCommand.class, new Answer(null, true, "")); + + _globoNetworkService.removeNetworkFromGloboNetwork(network); + + verify(_globoNetworkService._agentMgr, atLeastOnce()).easySend(any(Long.class), any(RemoveNetworkInGloboNetworkCommand.class)); + } + + private void mockAgentManagerSend(Network network, Class clazz, Answer response) { + when(_globoNetworkService._networkManager.getNetwork(network.getId())).thenReturn(network); + when(_globoNetworkService._hostDao.findByTypeNameAndZoneId(eq(network.getDataCenterId()), eq(Provider.GloboNetwork.getName()), eq(Host.Type.L2Networking))).thenReturn(new HostVO("guid")); + when(_globoNetworkService._agentMgr.easySend(anyLong(), any(clazz))).thenReturn(response); + } + + @Test + public void testListAllPoolByVipId() { + GloboNetworkManager manager = new GloboNetworkManager(); + + HostDao mock = mock(HostDao.class); + HostVO host = createMockHost(); + when(mock.findByTypeNameAndZoneId(10L, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(host); + manager._hostDao = mock; + + + LoadBalancingRulesService lbServiceMock = mock(LoadBalancingRulesService.class); + LoadBalancerVO lb = new LoadBalancerVO(null,null,null, 0L,0,0,null, 10, 0L, 0L,""); + + when(lbServiceMock.findById(123L)).thenReturn(lb); + manager._lbService = lbServiceMock; + + mockGetNetworkApiVipIp(lb, manager, 10001L); + + AgentManager mockAgent = mock(AgentManager.class); + + + List lbResponses = mockPools(); + GloboNetworkPoolResponse poolResponseAnswer = new GloboNetworkPoolResponse(lbResponses); + + + when(mockAgent.easySend(eq(host.getId()), any(ListPoolLBCommand.class))).thenReturn(poolResponseAnswer); + manager._agentMgr = mockAgent; + + List poolResponses = manager.listAllPoolByVipId(123L, 10L); + + assertEquals(2, poolResponses.size()); + GloboNetworkPoolResponse.Pool pool = poolResponses.get(0); + assertEquals((Long) 123L, pool.getId()); + assertEquals("my_pool", pool.getIdentifier()); + assertEquals("leastcon", pool.getLbMethod()); + assertEquals((Integer)80, pool.getPort()); + + pool = poolResponses.get(1); + assertEquals((Long) 123L, pool.getId()); + assertEquals("my_pool_2", pool.getIdentifier()); + assertEquals("round", pool.getLbMethod()); + assertEquals((Integer)8090, pool.getPort()); + } + + @Test + public void testListAllowedLbSuffixes() { + registerConfigKey("globonetwork.lb.allowed.suffixes", " hmg.test.com,test.com,stagging.test.com, "); + + List list = _globoNetworkService.listAllowedLbSuffixes(); + + assertEquals(3, list.size()); + assertEquals(list.get(0), ".stagging.test.com"); + assertEquals(list.get(1), ".hmg.test.com"); + assertEquals(list.get(2), ".test.com"); + } + + @Test + public void testGetLbDomain() { + registerConfigKey("globonetwork.lb.allowed.suffixes", " test.com,hmg.test.com,stagging.test.com, "); + String lbDomain = _globoNetworkService.getLbDomain("xpto.hmg.test.com"); + assertEquals(lbDomain, "hmg.test.com"); + String anotherLbDomain = _globoNetworkService.getLbDomain("xpto2.stagging.test.com"); + assertEquals(anotherLbDomain, "stagging.test.com"); + } + + @Test + public void testRemoveLbDNSRecord() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", true); + registerConfigKey("globonetwork.lb.allowed.suffixes", "hmg.test.com,test.com,stagging.test.com"); + + _globoNetworkService.manageLoadBalancerDomainNameRegistry(new NetworkVO(), rule); + + verify(_globoDnsService).removeDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyString(), anyLong()); + } + + @Test + public void testRegisterDomainName() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + registerConfigKey("globonetwork.lb.allowed.suffixes", "hmg.test.com,test.com,stagging.test.com"); + when(_globoDnsService.validateDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyLong(), eq(false))).thenReturn(true); + + _globoNetworkService.manageLoadBalancerDomainNameRegistry(new NetworkVO(), rule); + + verify(_globoDnsService).createDnsRecordForLoadBalancer(any(GloboDnsTO.class), eq(false)); + } + + @Test + public void testRegisterDomainNameGivenDomainRecordAlreadyRegistered() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + registerConfigKey("globonetwork.lb.allowed.suffixes", "hmg.test.com,test.com,stagging.test.com"); + when(_globoDnsService.validateDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyLong(), eq(false))).thenReturn(false); + + _globoNetworkService.manageLoadBalancerDomainNameRegistry(new NetworkVO(), rule); + + verify(_globoDnsService, times(1)).validateDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyLong(), eq(false)); + verify(_globoDnsService, times(0)).createDnsRecordForLoadBalancer(any(GloboDnsTO.class), eq(false)); + } + + @Test(expected = CloudRuntimeException.class) + public void testRegisterDomainNameGivenDomainRecordNotValid() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + registerConfigKey("globonetwork.lb.allowed.suffixes", "hmg.test.com,test.com,stagging.test.com"); + when(_globoDnsService.validateDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyLong(), eq(false))).thenThrow(new InvalidParameterValueException("invalid")); + + _globoNetworkService.manageLoadBalancerDomainNameRegistry(new NetworkVO(), rule); + + verify(_globoDnsService, times(1)).validateDnsRecordForLoadBalancer(anyString(), anyString(), anyString(), anyLong(), eq(false)); + verify(_globoDnsService, times(0)).createDnsRecordForLoadBalancer(any(GloboDnsTO.class), eq(false)); + } + + @Test(expected = CloudRuntimeException.class) + public void testRegisterDomainNameGivenNotAllowedDomainSuffix() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.other.com", false); + registerConfigKey("globonetwork.lb.allowed.suffixes", "hmg.test.com,test.com,stagging.test.com"); + + _globoNetworkService.manageLoadBalancerDomainNameRegistry(new NetworkVO(), rule); + verify(_globoDnsService, times(0)).createDnsRecordForLoadBalancer(any(GloboDnsTO.class), eq(false)); + } + + @Test + public void testPoolById() { + GloboNetworkManager manager = new GloboNetworkManager(); + + HostDao mock = mock(HostDao.class); + HostVO host = createMockHost(); + when(mock.findByTypeNameAndZoneId(10L, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(host); + manager._hostDao = mock; + + + LoadBalancingRulesService lbServiceMock = mock(LoadBalancingRulesService.class); + LoadBalancerVO lb = new LoadBalancerVO(null,null,null, 0L,0,0,null, 10, 0L, 0L,""); + + when(lbServiceMock.findById(123L)).thenReturn(lb); + manager._lbService = lbServiceMock; + + mockGetNetworkApiVipIp(lb, manager, 10001L); + + AgentManager mockAgent = mock(AgentManager.class); + + + GloboNetworkPoolResponse.Pool pool1 = mockPool("pool1", "round", 8080, 123L, "TCP"); + GloboNetworkPoolResponse poolResponseAnswer = new GloboNetworkPoolResponse(pool1); + + + when(mockAgent.easySend(eq(host.getId()), any(GetPoolLBByIdCommand.class))).thenReturn(poolResponseAnswer); + manager._agentMgr = mockAgent; + + GloboNetworkPoolResponse.Pool pool = manager.getPoolById(123L, 10L); + + assertEquals((Long) 123L, pool.getId()); + assertEquals("pool1", pool.getIdentifier()); + assertEquals("round", pool.getLbMethod()); + assertEquals((Integer)8080, pool.getPort()); + assertEquals((Long) 123L, pool.getId()); + assertEquals("TCP", pool.getHealthcheckType()); + + verify(mockAgent, times(1)).easySend(eq(host.getId()), any(GetPoolLBByIdCommand.class)); + } + + private void mockGetNetworkApiVipIp(LoadBalancer lb, GloboNetworkManager manager, Long networkVipId) { + LoadBalancingRulesManager lbMgrMock = mock(LoadBalancingRulesManager.class); + Ip ip = new Ip("75.75.75.75"); + when(lbMgrMock.getSourceIp(lb)).thenReturn(ip); + manager._lbMgr = lbMgrMock; + + IPAddressDao ipAddrMock = mock(IPAddressDao.class); + IPAddressVO ipV0 = new IPAddressVO(ip, 0L, 0L, 0L, true); + when(ipAddrMock.findByIpAndNetworkId(lb.getNetworkId(), ip.addr())).thenReturn(ipV0); + manager._ipAddrDao = ipAddrMock; + + GloboNetworkIpDetailDao _globoNetworkIpDetailMock = mock(GloboNetworkIpDetailDao.class); + GloboNetworkIpDetailVO detail = new GloboNetworkIpDetailVO(); + detail.setGloboNetworkVipId(networkVipId); + when(_globoNetworkIpDetailMock.findByIp(anyLong())).thenReturn(detail); + manager._globoNetworkIpDetailDao = _globoNetworkIpDetailMock; + } + + @Test + public void testCreatePool() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(null,null,null, 0L, 80, 8080,null, 10, 0L, 0L,""); + + CreateGloboNetworkPoolCmd cmd = new CreateGloboNetworkPoolCmd(); + cmd.setLbId(1L); + cmd.setPublicPort(443); + cmd.setPrivatePort(8443); + cmd.setZoneId(1L); + + GloboNetworkManager manager = spy(new GloboNetworkManager()); + doReturn(new GloboNetworkIpDetailVO(1L, 1L)).when(manager).getNetworkApiVipIp(loadBalancer); + doReturn(new GloboNetworkPoolResponse(null, true, "", new GloboNetworkPoolResponse.Pool())).when(manager).callCommand(any(CreatePoolCommand.class), eq(cmd.getZoneId())); + + manager._lbService = mock(LoadBalancingRulesService.class); + when(manager._lbService.findById(cmd.getLbId())).thenReturn(loadBalancer); + + manager._lbPortMapDao = mock(LoadBalancerPortMapDao.class); + when(manager._lbPortMapDao.listByLoadBalancerId(1L)).thenReturn(new ArrayList()); + + manager._lbMgr = mock(LoadBalancingRulesManager.class); + when(manager._lbMgr.getSourceIp(loadBalancer)).thenReturn(new Ip("192.168.10.5")); + + manager._globoNetworkLBEnvironmentDao = mock(GloboNetworkLoadBalancerEnvironmentDAO.class); + when(manager._globoNetworkLBEnvironmentDao.findById(anyLong())).thenReturn(new GloboNetworkLoadBalancerEnvironment()); + + manager._globoResourceConfigurationDao = mock(GloboResourceConfigurationDao.class); + manager._lbOptionsDao = mock(LoadBalancerOptionsDao.class); + + GloboNetworkPoolResponse.Pool pool = manager.createPool(cmd); + + assertNotNull(pool); + verify(manager).callCommand(any(CreatePoolCommand.class), eq(cmd.getZoneId())); + verify(manager._lbPortMapDao).persist(any(LoadBalancerPortMapVO.class)); + } + + @Test + public void testCreatePoolGivenInvalidPortMapping() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(null,null,null, 0L, 80, 8080,null, 10, 0L, 0L,""); + + CreateGloboNetworkPoolCmd cmd = new CreateGloboNetworkPoolCmd(); + cmd.setLbId(1L); + cmd.setPublicPort(80); + cmd.setPrivatePort(8080); + cmd.setZoneId(1L); + + GloboNetworkManager manager = spy(new GloboNetworkManager()); + doReturn(new GloboNetworkIpDetailVO(1L, 1L)).when(manager).getNetworkApiVipIp(loadBalancer); + doReturn(new GloboNetworkPoolResponse(null, true, "", new GloboNetworkPoolResponse.Pool())).when(manager).callCommand(any(CreatePoolCommand.class), eq(cmd.getZoneId())); + + manager._lbService = mock(LoadBalancingRulesService.class); + when(manager._lbService.findById(cmd.getLbId())).thenReturn(loadBalancer); + + manager._lbPortMapDao = mock(LoadBalancerPortMapDao.class); + when(manager._lbPortMapDao.listByLoadBalancerId(1L)).thenReturn(new ArrayList()); + + try{ + manager.createPool(cmd); + }catch(InvalidParameterValueException e){ + assertEquals("This public/private port pair already exists.", e.getMessage()); + } + } + + @Test + public void testCreatePoolGivenInvalidPortMappingForDSRLoadBalancer() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(null,null,null, 0L, 80, 80,null, 10, 0L, 0L,""); + + CreateGloboNetworkPoolCmd cmd = new CreateGloboNetworkPoolCmd(); + cmd.setLbId(1L); + cmd.setPublicPort(443); + cmd.setPrivatePort(8443); + cmd.setZoneId(1L); + + GloboNetworkManager manager = spy(new GloboNetworkManager()); + doReturn(new GloboNetworkIpDetailVO(1L, 1L)).when(manager).getNetworkApiVipIp(loadBalancer); + doReturn(new GloboNetworkPoolResponse(null, true, "", new GloboNetworkPoolResponse.Pool())).when(manager).callCommand(any(CreatePoolCommand.class), eq(cmd.getZoneId())); + + manager._lbService = mock(LoadBalancingRulesService.class); + when(manager._lbService.findById(cmd.getLbId())).thenReturn(loadBalancer); + + manager._lbPortMapDao = mock(LoadBalancerPortMapDao.class); + when(manager._lbPortMapDao.listByLoadBalancerId(1L)).thenReturn(new ArrayList()); + + manager._globoResourceConfigurationDao = mock(GloboResourceConfigurationDao.class); + when(manager._globoResourceConfigurationDao.getFirst(GloboResourceType.LOAD_BALANCER, loadBalancer.getUuid(), GloboResourceKey.dsr)).thenReturn( + new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, loadBalancer.getUuid(), GloboResourceKey.dsr, "true") + ); + + try{ + manager.createPool(cmd); + }catch(InvalidParameterValueException e){ + assertEquals("In DSR load balancer the public port must always be the same as private port.", e.getMessage()); + } + } + + @Test + public void testDeletePool(){ + LoadBalancerVO loadBalancer = new LoadBalancerVO(null,null,null, 0L, 80, 80,null, 10, 0L, 0L,""); + GloboNetworkPoolResponse.Pool pool = new GloboNetworkPoolResponse.Pool(); + pool.setId(1L); + pool.setVipPort(443); + pool.setPort(8443); + + DeleteGloboNetworkPoolCmd cmd = new DeleteGloboNetworkPoolCmd(); + cmd.setLbId(1L); + cmd.setPoolId(1L); + cmd.setZoneId(1L); + + GloboNetworkManager manager = spy(new GloboNetworkManager()); + manager._lbMgr = mock(LoadBalancingRulesManager.class); + when(manager._lbMgr.getSourceIp(loadBalancer)).thenReturn(new Ip("192.168.10.5")); + manager._lbService = mock(LoadBalancingRulesService.class); + when(manager._lbService.findById(cmd.getLbId())).thenReturn(loadBalancer); + doReturn(new GloboNetworkIpDetailVO(1L, 1L)).when(manager).getNetworkApiVipIp(loadBalancer); + doReturn(pool).when(manager).findPoolById(anyLong(), anyLong(), anyLong()); + doReturn(new Answer(null, true, "")).when(manager).callCommand(any(DeletePoolCommand.class), eq(cmd.getZoneId())); + doNothing().when(manager).removePortMapping(eq(loadBalancer), any(GloboNetworkPoolResponse.Pool.class)); + + manager.deletePool(cmd); + + verify(manager).findPoolById(anyLong(), anyLong(), anyLong()); + verify(manager).removePortMapping(eq(loadBalancer), eq(pool)); + verify(manager).callCommand(any(DeletePoolCommand.class), eq(cmd.getZoneId())); + } + + @Test + public void testUpdatePools() { + GloboNetworkManager manager = new GloboNetworkManager(); + + HostDao mock = mock(HostDao.class); + HostVO host = createMockHost(); + when(mock.findByTypeNameAndZoneId(10L, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(host); + manager._hostDao = mock; + + LoadBalancingRulesService lbServiceMock = mock(LoadBalancingRulesService.class); + LoadBalancerVO lb = new LoadBalancerVO(null,null,null, 0L,0,0,null, 10, 0L, 0L,""); + + when(lbServiceMock.findById(123L)).thenReturn(lb); + manager._lbService = lbServiceMock; + + mockGetNetworkApiVipIp(lb, manager, 10001L); + + AgentManager mockAgent = mock(AgentManager.class); + + GloboNetworkPoolResponse.Pool pool1 = mockPool("pool1", "round", 8080, 12L, "HTTP"); + GloboNetworkPoolResponse.Pool pool2 = mockPool("pool2", "round", 8080, 13L, "HTTP"); + + GloboNetworkPoolResponse poolResponseAnswer = new GloboNetworkPoolResponse(Arrays.asList(pool1, pool2)); + when(mockAgent.easySend(eq(host.getId()), any(UpdatePoolCommand.class))).thenReturn(poolResponseAnswer); + manager._agentMgr = mockAgent; + + List pools = manager.updatePools(Arrays.asList(12L, 13L), 123L, 10L, "HTTP", "", "", 10, null, null, false); + + GloboNetworkPoolResponse.Pool pool = pools.get(0); + + assertEquals((Long) 12L, pool.getId()); + assertEquals("HTTP", pool.getHealthcheckType()); + + GloboNetworkPoolResponse.Pool pool22 = pools.get(1); + assertEquals((Long) 13L, pool22.getId()); + assertEquals("HTTP", pool22.getHealthcheckType()); + + verify(mockAgent, times(1)).easySend(eq(host.getId()), any(UpdatePoolCommand.class)); + } + + @Test + public void testListAllExpectedHealthchecks() { + Long zoneId = 12L; + GloboNetworkManager manager = new GloboNetworkManager(); + + //mock sendEasy command + HostDao mock = mock(HostDao.class); + HostVO host = createMockHost(); + when(mock.findByTypeNameAndZoneId(zoneId, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(host); + manager._hostDao = mock; + + DataCenterDao mock2 = mock(DataCenterDao.class); + List list = new ArrayList<>(); + list.add(new DataCenterVO(zoneId, null, null, null, null, null, null, null, null, null, null, null, null)); + when(mock2.listEnabledZones()).thenReturn(list); + manager._dcDao = mock2; + + //mock result + GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck expect1 = new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(1L, "OK"); + GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck expect2 = new GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck(2L, "WORKING"); + + AgentManager mockAgent = mock(AgentManager.class); + GloboNetworkExpectHealthcheckResponse expectHealthcheckResponse = new GloboNetworkExpectHealthcheckResponse(Arrays.asList(expect1, expect2)); + + when(mockAgent.easySend(eq(host.getId()), any(UpdatePoolCommand.class))).thenReturn(expectHealthcheckResponse); + manager._agentMgr = mockAgent; + + //execute + List expectedHealthcheckList = manager.listAllExpectedHealthchecks(); + + assertNotNull(expectedHealthcheckList); + assertEquals(2, expectedHealthcheckList.size()); + + GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck expectedHealthcheck = expectedHealthcheckList.get(0); + assertEquals((Long) 1L, expectedHealthcheck.getId()); + assertEquals("OK", expectedHealthcheck.getExpected()); + + expectedHealthcheck = expectedHealthcheckList.get(1); + assertEquals((Long) 2L, expectedHealthcheck.getId()); + assertEquals("WORKING", expectedHealthcheck.getExpected()); + } + + @Test + public void testValidateStickinessPolicyGivenValidInput(){ + List policies = Collections.singletonList(new LoadBalancingRule.LbStickinessPolicy(null, null, false)); + LoadBalancingRule rule = new LoadBalancingRule(null, null, policies, null, null); + new GloboNetworkManager().validateSticknessPolicy(rule); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateStickinessPolicyGivenNullPolicies(){ + LoadBalancingRule rule = new LoadBalancingRule(null, null, null, null, null); + new GloboNetworkManager().validateSticknessPolicy(rule); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateStickinessPolicyGivenMoreThanOnePolicy(){ + List policies = new ArrayList<>(); + policies.add(new LoadBalancingRule.LbStickinessPolicy(null, null, false)); + policies.add(new LoadBalancingRule.LbStickinessPolicy(null, null, false)); + LoadBalancingRule rule = new LoadBalancingRule(null, null, policies, null, null); + new GloboNetworkManager().validateSticknessPolicy(rule); + } + + @Test + public void testValidateHealthCheckPoliciesGivenNoPolicyProvided(){ + LoadBalancingRule rule = new LoadBalancingRule(null, null, null, null, null); + new GloboNetworkManager().validateHealthCheckPolicies(rule); + } + + @Test + public void testValidateHealthCheckPoliciesGivenOnePolicy(){ + List policies = Collections.singletonList(new LoadBalancingRule.LbHealthCheckPolicy(null, null, 10, 10, 10 , 10)); + LoadBalancingRule rule = new LoadBalancingRule(null, null, null, policies, null); + new GloboNetworkManager().validateHealthCheckPolicies(rule); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateHealthCheckPoliciesGivenMoreThanOneNotRevokedPolicies(){ + List policies = new ArrayList<>(); + policies.add(new LoadBalancingRule.LbHealthCheckPolicy(null, null, 10, 10, 10 , 10)); + policies.add(new LoadBalancingRule.LbHealthCheckPolicy(null, null, 10, 10, 10 , 10)); + LoadBalancingRule rule = new LoadBalancingRule(null, null, null, policies, null); + new GloboNetworkManager().validateHealthCheckPolicies(rule); + } + + @Test + public void testValidateHealthCheckPoliciesGivenTwoPoliciesAndOneRevokedPolicy(){ + List policies = new ArrayList<>(); + policies.add(new LoadBalancingRule.LbHealthCheckPolicy(null, null, 10, 10, 10 , 10)); + policies.add(new LoadBalancingRule.LbHealthCheckPolicy(null, null, 10, 10, 10 , 10, true)); + LoadBalancingRule rule = new LoadBalancingRule(null, null, null, policies, null); + new GloboNetworkManager().validateHealthCheckPolicies(rule); + } + + @Test + public void testGetServicePortsGivenOnlyOnePortPairProvided(){ + LoadBalancerVO lb = new LoadBalancerVO(null, null, null, 1L, 80, 8080, null, 1L, 1L, 1L, null); + LoadBalancingRule rule = new LoadBalancingRule(lb, null, null, null, null); + List ports = new GloboNetworkManager().getServicePorts(rule); + + assertEquals(1, ports.size()); + assertEquals("80:8080", ports.get(0)); + } + + @Test + public void testGetServicePortsGivenTwoPortPairProvided(){ + LoadBalancerVO lb = new LoadBalancerVO(null, null, null, 1L, 80, 8080, null, 1L, 1L, 1L, null); + LoadBalancingRule rule = new LoadBalancingRule(lb, null, null, null, null); + rule.setAdditionalPortMap(Collections.singletonList("443:8443")); + List ports = new GloboNetworkManager().getServicePorts(rule); + + assertEquals(2, ports.size()); + assertEquals("80:8080", ports.get(0)); + assertEquals("443:8443", ports.get(1)); + } + + @Test + public void testGetReals(){ + VMInstanceVO vm = new VMInstanceVO(1, 1, "vm-01", "vm-01", VirtualMachine.Type.Instance, 1L, Hypervisor.HypervisorType.Simulator, 1, 1, 1, 1, false, true, 1L); + when(_vmDao.findById(1L)).thenReturn(vm); + GloboNetworkNetworkVO network = new GloboNetworkNetworkVO(1L, 2L, 3L); + when(_globoNetworkNetworkDao.findByNetworkId(1L)).thenReturn(network); + + LoadBalancingRule.LbDestination destination = new LoadBalancingRule.LbDestination(80, 80, "10.170.100.1", 1L, 1L, false); + LoadBalancingRule rule = new LoadBalancingRule(null, Collections.singletonList(destination), null, null, null); + List reals = _globoNetworkService.getReals(rule.getDestinations(), Collections.singletonList("80:8080")); + + GloboNetworkVipResponse.Real real = reals.get(0); + assertEquals(1, reals.size()); + assertEquals("10.170.100.1", real.getIp()); + assertNotNull(real.getVmName()); + assertEquals(Collections.singletonList("80:8080"), real.getPorts()); + assertFalse(real.isRevoked()); + assertEquals((Long) 3L, real.getEnvironmentId()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetRealsGivenVmNotFound(){ + when(_vmDao.findById(1L)).thenReturn(null); + + LoadBalancingRule.LbDestination destination = new LoadBalancingRule.LbDestination(80, 80, "10.170.100.1", 1L, 1L, false); + LoadBalancingRule rule = new LoadBalancingRule(null, Collections.singletonList(destination), null, null, null); + _globoNetworkService.getReals(rule.getDestinations(), Collections.singletonList("80:8080")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetRealsGivenNetworkNotFound(){ + VMInstanceVO vm = new VMInstanceVO(1, 1, "vm-01", "vm-01", VirtualMachine.Type.Instance, 1L, Hypervisor.HypervisorType.Simulator, + 1, 1, 1, 1,false, true, 1L); + when(_vmDao.findById(1L)).thenReturn(vm); + when(_globoNetworkNetworkDao.findByNetworkId(1L)).thenReturn(null); + + LoadBalancingRule.LbDestination destination = new LoadBalancingRule.LbDestination(80, 80, "10.170.100.1", 1L, 1L, false); + LoadBalancingRule rule = new LoadBalancingRule(null, Collections.singletonList(destination), null, null, null); + _globoNetworkService.getReals(rule.getDestinations(), Collections.singletonList("80:8080")); + } + + @Test + public void testGetRealsGivenNoDestinationsSet(){ + LoadBalancingRule rule = new LoadBalancingRule(null, new ArrayList(), null, null, null); + List reals = _globoNetworkService.getReals(rule.getDestinations(), Collections.singletonList("80:8080")); + assertEquals(0, reals.size()); + } + + @Test + public void testAddVip() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + + IPAddressVO ip = new IPAddressVO(new Ip(1L), 1L, 1L, 1L, false); + when(_ipAddrDao.findByIpAndNetworkId(anyLong(), anyString())).thenReturn(ip); + + GloboNetworkIpDetailVO ipDetail = new GloboNetworkIpDetailVO() ; + ipDetail.setGloboNetworkEnvironmentRefId(1L); + when(_globoNetworkIpDetailDao.findByIp(anyLong())).thenReturn(ipDetail); + + GloboNetworkLoadBalancerEnvironment lbEnv = new GloboNetworkLoadBalancerEnvironment(); + when(_globoNetworkLBEnvironmentDao.findById(anyLong())).thenReturn(lbEnv); + when(_acctMgr.getAccount(anyLong())).thenReturn(acct); + when(_lbOptionsDao.listByLoadBalancerId(anyLong())).thenReturn(new ArrayList()); + HostVO napiHost = new HostVO(globoNetworkHostId, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, zoneId, null, 0L, 0L, null, null, null, 0L, null); + when(_hostDao.findByTypeNameAndZoneId(0L, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(napiHost); + GloboNetworkVipResponse response = new GloboNetworkVipResponse(); + response.setId(1L); + when(_agentMgr.easySend(eq(globoNetworkHostId), any(ApplyVipInGloboNetworkCommand.class))).thenReturn(response); + + assertTrue(_globoNetworkService.applyLbRuleInGloboNetwork(new NetworkVO(), rule)); + verify(_agentMgr).easySend(eq(globoNetworkHostId), any(ApplyVipInGloboNetworkCommand.class)); + verify(_globoNetworkIpDetailDao).persist(any(GloboNetworkIpDetailVO.class)); + } + + @Test(expected = CloudRuntimeException.class) + public void testAddVipGivenLBEnvNotFound() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + + IPAddressVO ip = new IPAddressVO(new Ip(1L), 1L, 1L, 1L, false); + when(_ipAddrDao.findByIpAndNetworkId(anyLong(), anyString())).thenReturn(ip); + + GloboNetworkIpDetailVO ipDetail = new GloboNetworkIpDetailVO() ; + ipDetail.setGloboNetworkEnvironmentRefId(1L); + when(_globoNetworkIpDetailDao.findByIp(anyLong())).thenReturn(ipDetail); + + when(_globoNetworkLBEnvironmentDao.findById(anyLong())).thenReturn(null); + + assertTrue(_globoNetworkService.applyLbRuleInGloboNetwork(new NetworkVO(), rule)); + } + + @Test(expected = CloudRuntimeException.class) + public void testAddVipGivenIPDetailNotFound() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + + IPAddressVO ip = new IPAddressVO(new Ip(1L), 1L, 1L, 1L, false); + when(_ipAddrDao.findByIpAndNetworkId(anyLong(), anyString())).thenReturn(ip); + when(_globoNetworkIpDetailDao.findByIp(anyLong())).thenReturn(null); + + assertTrue(_globoNetworkService.applyLbRuleInGloboNetwork(new NetworkVO(), rule)); + } + + @Test(expected = CloudRuntimeException.class) + public void testAddVipGivenIPAddressNotFound() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", false); + when(_ipAddrDao.findByIpAndNetworkId(anyLong(), anyString())).thenReturn(null); + assertTrue(_globoNetworkService.applyLbRuleInGloboNetwork(new NetworkVO(), rule)); + } + + + @Test(expected = CloudRuntimeException.class) + public void testListLinkableLoadBalancersWhenLBDoesNotExist() { + _globoNetworkService._loadBalancerDao = mock(LoadBalancerDao.class); + when(_globoNetworkService._loadBalancerDao.findById(123L)).thenReturn(null); + + List loadBalancerVOS = _globoNetworkService.listLinkableLoadBalancers(123L, 45L); + } + + @Test(expected = CloudRuntimeException.class) + public void testListLinkableLoadBalancersWhenLBAlreadyLinked() { + _globoNetworkService._loadBalancerDao = mock(LoadBalancerDao.class); + LoadBalancerVO lbVo = new LoadBalancerVO(); + lbVo.setUuid("123123"); + lbVo.setName("test.lb.com"); + when(_globoNetworkService._loadBalancerDao.findById(123L)).thenReturn(lbVo); + + + _globoNetworkService._globoResourceConfigurationDao = mock(GloboResourceConfigurationDao.class); + List globoConfigurations = new ArrayList<>(); + globoConfigurations.add(new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, "123123", GloboResourceKey.linkedLoadBalancer, "333")); + when(_globoNetworkService._globoResourceConfigurationDao.getConfiguration(GloboResourceType.LOAD_BALANCER,"123123", GloboResourceKey.linkedLoadBalancer)).thenReturn(globoConfigurations); + + when(_globoNetworkService._loadBalancerDao.findByUuid("333")).thenReturn(lbVo); + + _globoNetworkService.listLinkableLoadBalancers(123L, 45L); + } + + + @Test + public void testListLinkableLoadBalancersEmpty() { + _globoNetworkService._loadBalancerDao = mock(LoadBalancerDao.class); + LoadBalancerVO lbVo = new LoadBalancerVO(null, "test.lb.com", "lb test", 90L, 80, 8080, "leastcon", 511L, 66L, 1L, "HTTP"); + lbVo.setUuid("123123"); + + + when(_globoNetworkService._loadBalancerDao.findById(123L)).thenReturn(lbVo); + + _globoNetworkService._globoNetworkNetworkDao = mock(GloboNetworkNetworkDao.class); + GloboNetworkNetworkVO globonetworkRef = new GloboNetworkNetworkVO(111L, 511L, 120); + when(_globoNetworkService._globoNetworkNetworkDao.findByNetworkId(511L)).thenReturn(globonetworkRef); + + List linkablesLBs = new ArrayList<>(); + + when(_globoNetworkService._loadBalancerDao.listLinkables("123123", 120L, 66L)).thenReturn(linkablesLBs); + + + List loadBalancerVOS = _globoNetworkService.listLinkableLoadBalancers(123L, 45L); + assertNotNull(loadBalancerVOS); + assertTrue(loadBalancerVOS.isEmpty()); + assertEquals(0, loadBalancerVOS.size()); + + } + + + @Test + public void testListLinkableLoadBalancersNotEmpty() { + _globoNetworkService._loadBalancerDao = mock(LoadBalancerDao.class); + LoadBalancerVO lbVo = new LoadBalancerVO(null, "test.lb.com", "lb test", 90L, 80, 8080, "leastcon", 511L, 66L, 1L, "HTTP"); + lbVo.setUuid("123123"); + + + when(_globoNetworkService._loadBalancerDao.findById(123L)).thenReturn(lbVo); + + _globoNetworkService._globoNetworkNetworkDao = mock(GloboNetworkNetworkDao.class); + GloboNetworkNetworkVO globonetworkRef = new GloboNetworkNetworkVO(111L, 511L, 120); + when(_globoNetworkService._globoNetworkNetworkDao.findByNetworkId(511L)).thenReturn(globonetworkRef); + + List linkablesLBs = new ArrayList<>(); + LoadBalancerVO linkablesLB = new LoadBalancerVO(null, "linkable.lb.com", "lb linkable", 90L, 80, 8080, "leastcon", 511L, 66L, 1L, "HTTP"); + linkablesLBs.add(linkablesLB); + when(_globoNetworkService._loadBalancerDao.listLinkables("123123", 120L, 66L)).thenReturn(linkablesLBs); + + + List loadBalancerVOS = _globoNetworkService.listLinkableLoadBalancers(123L, 45L); + assertNotNull(loadBalancerVOS); + assertFalse(loadBalancerVOS.isEmpty()); + assertEquals(1, loadBalancerVOS.size()); + + } + + @Test + public void testRemoveVip() throws ResourceUnavailableException { + LoadBalancingRule rule = createMockLbRule("dummy.test.com", true); + + IPAddressVO ip = new IPAddressVO(new Ip(1L), 1L, 1L, 1L, false); + when(_ipAddrDao.findByIpAndNetworkId(anyLong(), anyString())).thenReturn(ip); + + GloboNetworkIpDetailVO ipDetail = new GloboNetworkIpDetailVO() ; + ipDetail.setGloboNetworkEnvironmentRefId(1L); + when(_globoNetworkIpDetailDao.findByIp(anyLong())).thenReturn(ipDetail); + + GloboNetworkLoadBalancerEnvironment lbEnv = new GloboNetworkLoadBalancerEnvironment(); + when(_globoNetworkLBEnvironmentDao.findById(anyLong())).thenReturn(lbEnv); + when(_acctMgr.getAccount(anyLong())).thenReturn(acct); + when(_lbOptionsDao.listByLoadBalancerId(anyLong())).thenReturn(new ArrayList()); + HostVO napiHost = new HostVO(globoNetworkHostId, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, zoneId, null, 0L, 0L, null, null, null, 0L, null); + when(_hostDao.findByTypeNameAndZoneId(0L, Provider.GloboNetwork.getName(), Host.Type.L2Networking)).thenReturn(napiHost); + GloboNetworkVipResponse response = new GloboNetworkVipResponse(); + response.setId(1L); + + when(_agentMgr.easySend(eq(globoNetworkHostId), any(RemoveNetworkInGloboNetworkCommand.class))).thenReturn(response); + + assertTrue(_globoNetworkService.applyLbRuleInGloboNetwork(new NetworkVO(), rule)); + verify(_agentMgr).easySend(eq(globoNetworkHostId), any(ApplyVipInGloboNetworkCommand.class)); + verify(_globoResourceConfigurationDao).removeConfigurations(any(String.class), any(GloboResourceType.class)); + verify(_globoNetworkIpDetailDao).persist(any(GloboNetworkIpDetailVO.class)); + } + + private LoadBalancingRule createMockLbRule(String name, boolean revoked) { + LoadBalancerVO lb = new LoadBalancerVO(null, null, null, 1L, 80, 8080, null, 1L, 1L, 1L, null); + lb.setState(FirewallRule.State.Add); + if(revoked) { + lb.setState(FirewallRule.State.Revoke); + } + lb.setName(name); + Ip sourceIp = new Ip(1L); + List policies = Collections.singletonList(new LoadBalancingRule.LbStickinessPolicy(null, null, false)); + return new LoadBalancingRule(lb, null, policies, null, sourceIp); + } + + private List mockPools() { + ArrayList pools = new ArrayList<>(); + pools.add(mockPool("my_pool", "leastcon", 80, 123L, "HTTP")); + pools.add(mockPool("my_pool_2", "round", 8090, 123L, "TCP")); + return pools; + } + + private GloboNetworkPoolResponse.Pool mockPool(String name, String lbMethod, Integer port, Long id, String healthcheckType) { + GloboNetworkPoolResponse.Pool pool2 = new GloboNetworkPoolResponse.Pool(); + pool2.setId(id); + pool2.setIdentifier(name); + pool2.setLbMethod(lbMethod); + pool2.setPort(port); + pool2.setHealthcheckType(healthcheckType); + return pool2; + } + + private HostVO createMockHost() { + return new HostVO( + 10L, "Host-1", Host.Type.Routing, null, "10.0.0.0", null, null, null, null, null, null, null, null, + Status.Up, null, null, null, 10L, 10L, 30L, 10233, null, null, null, 0, null + ); + } + + private LoadBalancingRule createLoadBalancerRule() { + return new LoadBalancingRule(new LoadBalancerVO("id", "lb", "lb", 1, 80, 8080, "algorithm", 1, 1, 1, "HTTP"), null, null, null, null); + } + + private void registerConfigKey(String key, String valueMock) { + ConfigDepotImpl mock = mock(ConfigDepotImpl.class); + ConfigurationDao mockDAo = mock(ConfigurationDao.class); + when(mock.global()).thenReturn(mockDAo); + when(mockDAo.findById(key)).thenReturn(new ConfigurationVO("Network", "String", null, key, valueMock, null)); + ConfigKey.init(mock); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/HealthCheckHelperTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/HealthCheckHelperTest.java new file mode 100644 index 000000000000..e4234e0be6e7 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/HealthCheckHelperTest.java @@ -0,0 +1,92 @@ +package com.globo.globonetwork.cloudstack.manager; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class HealthCheckHelperTest { + + @Test + public void testBuild() { + HealthCheckHelper healthcheck = HealthCheckHelper.build("lb.test.com", "HTTP", "/health.html", "200 OK"); + assertEquals("HTTP", healthcheck.getHealthCheckType()); + assertEquals("200 OK", healthcheck.getExpectedHealthCheck()); + assertEquals("GET /health.html HTTP/1.0\\r\\nHost: lb.test.com\\r\\n\\r\\n", healthcheck.getHealthCheck()); + + healthcheck = HealthCheckHelper.build("lb.test.com", "TCP", null, null); + assertEquals("TCP", healthcheck.getHealthCheckType()); + assertEquals(null, healthcheck.getHealthCheck()); + assertEquals(null, healthcheck.getExpectedHealthCheck()); + assertNull(healthcheck.getExpectedHealthCheck()); + + } + + @Test(expected=IllegalArgumentException.class) + public void testBuild_validate() { + HealthCheckHelper healthcheck = HealthCheckHelper.build("lb.test.com", "TCP", null, "WORKING"); + assertEquals("TCP", healthcheck.getHealthCheckType()); + assertEquals(null, healthcheck.getHealthCheck()); + assertEquals(null, healthcheck.getExpectedHealthCheck()); + assertNull(healthcheck.getExpectedHealthCheck()); + } + + @Test + public void testBuild_http_old_version() { + HealthCheckHelper healthcheck = HealthCheckHelper.build("lb.test.com", null, "/health.html", null); + assertEquals("HTTP", healthcheck.getHealthCheckType()); + assertEquals("WORKING", healthcheck.getExpectedHealthCheck()); + assertEquals("GET /health.html HTTP/1.0\\r\\nHost: lb.test.com\\r\\n\\r\\n", healthcheck.getHealthCheck()); + } + + + @Test + public void testBuild_tcp_old_version() { + HealthCheckHelper healthcheck = HealthCheckHelper.build("lb.test.com", null, null, null); + assertEquals("TCP", healthcheck.getHealthCheckType()); + assertEquals(null, healthcheck.getHealthCheck()); + assertEquals(null, healthcheck.getExpectedHealthCheck()); + assertNull(healthcheck.getExpectedHealthCheck()); + } + + + @Test + public void testBuildHealthCheckExpectedStringEmpty(){ + HealthCheckHelper healthcheck = HealthCheckHelper.build("lb.test.com", "HTTP", "/healthcheck.html", ""); + assertEquals("WORKING", healthcheck.getExpectedHealthCheck()); + assertEquals("HTTP", healthcheck.getHealthCheckType()); + assertEquals("GET /healthcheck.html HTTP/1.0\\r\\nHost: lb.test.com\\r\\n\\r\\n", healthcheck.getHealthCheck()); + } + + @Test + public void testBuildHealthCheckStringGivenPathAndHostNull(){ + HealthCheckHelper healthcheck = HealthCheckHelper.build(null, "HTTP", null, "200 OK"); + assertEquals("", healthcheck.buildHealthCheckString(null, null)); + } + + @Test + public void testBuildHealthCheckStringGivenPathNullAndHostFilled(){ + HealthCheckHelper healthCheck = HealthCheckHelper.build("host", "HTTP", null, "200 OK"); + assertEquals("", healthCheck.buildHealthCheckString(null, "host")); + + assertEquals("200 OK", healthCheck.getExpectedHealthCheck()); + assertEquals("HTTP", healthCheck.getHealthCheckType()); + } + + @Test + public void testBuildHealthCheckStringGiveFullHTTPPath(){ + HealthCheckHelper healthCheck = HealthCheckHelper.build("lb.test.com", "HTTP", "/healthcheck.html", "200 OK"); + assertEquals("GET /healthcheck.html", healthCheck.buildHealthCheckString("GET /healthcheck.html", "lb.test.com")); + + assertEquals("200 OK", healthCheck.getExpectedHealthCheck()); + assertEquals("HTTP", healthCheck.getHealthCheckType()); + assertEquals("GET /healthcheck.html HTTP/1.0\\r\\nHost: lb.test.com\\r\\n\\r\\n", healthCheck.getHealthCheck()); + } + + @Test + public void testBuildHealthCheckStringGivenURIandHost(){ + HealthCheckHelper healthCheck = HealthCheckHelper.build("lb.test.com", "HTTP", "/healthcheck.html", "200 OK"); + assertEquals("GET /healtcheck.html HTTP/1.0\\r\\nHost: lb.test.com\\r\\n\\r\\n", healthCheck.buildHealthCheckString("/healtcheck.html", "lb.test.com")); + } + +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/ProtocolTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/ProtocolTest.java new file mode 100644 index 000000000000..37e71aab0f16 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/manager/ProtocolTest.java @@ -0,0 +1,51 @@ +package com.globo.globonetwork.cloudstack.manager; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public class ProtocolTest { + @Test + public void testValidProtocols() throws Exception { + + + Protocol.L4 l4 = Protocol.L4.TCP; + Protocol.L7 l7 = Protocol.L7.HTTP; + + boolean valid = Protocol.validProtocols(l4, l7); + assertTrue(valid); + + l4 = Protocol.L4.TCP; + l7 = Protocol.L7.HTTPS; + + valid = Protocol.validProtocols(l4, l7); + + assertTrue(valid); + + l4 = Protocol.L4.TCP; + l7 = Protocol.L7.OTHERS; + + valid = Protocol.validProtocols(l4, l7); + + assertTrue(valid); + + l4 = Protocol.L4.UDP; + l7 = Protocol.L7.OTHERS; + + valid = Protocol.validProtocols(l4, l7); + + assertTrue(valid); + + } + + @Test + public void testValidProtocolsFalse() { + Protocol.L4 l4 = Protocol.L4.UDP; + Protocol.L7 l7 = Protocol.L7.HTTP; + + boolean valid = Protocol.validProtocols(l4, l7); + assertFalse(valid); + } + +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/GloboNetworkResourceTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/GloboNetworkResourceTest.java new file mode 100644 index 000000000000..4f6fbcc787c5 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/GloboNetworkResourceTest.java @@ -0,0 +1,1154 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globo.globonetwork.cloudstack.resource; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; + +import com.cloud.exception.InvalidParameterValueException; +import com.globo.globonetwork.client.api.ExpectHealthcheckAPI; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.NetworkAPI; +import com.globo.globonetwork.client.api.NetworkJsonAPI; +import com.globo.globonetwork.client.api.OptionVipV3API; +import com.globo.globonetwork.client.api.PoolAPI; +import com.globo.globonetwork.client.model.IPv4Network; +import com.globo.globonetwork.client.model.Network; +import com.globo.globonetwork.client.model.OptionVipV3; +import com.globo.globonetwork.client.model.Pool; +import com.globo.globonetwork.client.model.PoolOption; +import com.globo.globonetwork.client.model.VipPoolMap; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.Vlan; + +import com.globo.globonetwork.client.model.healthcheck.ExpectHealthcheck; +import com.globo.globonetwork.client.model.pool.PoolV3; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.CreatePoolCommand; +import com.globo.globonetwork.cloudstack.commands.DeletePoolCommand; +import com.globo.globonetwork.cloudstack.commands.GetVipInfoFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.commands.ListExpectedHealthchecksCommand; +import com.globo.globonetwork.cloudstack.commands.ListPoolLBCommand; +import com.globo.globonetwork.cloudstack.commands.RemoveVipFromGloboNetworkCommand; +import com.globo.globonetwork.cloudstack.manager.HealthCheckHelper; +import com.globo.globonetwork.cloudstack.manager.Protocol; +import com.globo.globonetwork.cloudstack.response.GloboNetworkExpectHealthcheckResponse; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.naming.ConfigurationException; + +import com.globo.globonetwork.cloudstack.commands.ListPoolOptionsCommand; +import com.globo.globonetwork.cloudstack.response.GloboNetworkPoolOptionResponse; +import org.junit.Before; +import org.junit.Test; + +import com.cloud.agent.api.Answer; +import com.globo.globonetwork.client.api.EquipmentAPI; +import com.globo.globonetwork.client.api.IpAPI; +import com.globo.globonetwork.client.api.VipEnvironmentAPI; +import com.globo.globonetwork.client.api.VlanAPI; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.Equipment; +import com.globo.globonetwork.client.model.Ip; +import com.globo.globonetwork.client.model.Ipv4; +import com.globo.globonetwork.client.model.VipEnvironment; +import com.globo.globonetwork.cloudstack.response.GloboNetworkVipResponse; + +public class GloboNetworkResourceTest { + + GloboNetworkResource _resource; + GloboNetworkAPI gnAPI; + Ipv4 vipIp; + + @Before + public void setUp() throws ConfigurationException { + _resource = spy(new GloboNetworkResource()); + gnAPI = mock(GloboNetworkAPI.class); + doReturn(gnAPI).when(_resource).getNewGloboNetworkAPI(); + when(gnAPI.getEquipmentAPI()).thenReturn(mock(EquipmentAPI.class)); + when(gnAPI.getIpAPI()).thenReturn(mock(IpAPI.class)); + when(gnAPI.getVipEnvironmentAPI()).thenReturn(mock(VipEnvironmentAPI.class)); + when(gnAPI.getVlanAPI()).thenReturn(mock(VlanAPI.class)); + when(gnAPI.getNetworkAPI()).thenReturn(mock(NetworkAPI.class)); + when(gnAPI.getNetworkJsonAPI()).thenReturn(mock(NetworkJsonAPI.class)); + when(gnAPI.getPoolAPI()).thenReturn(mock(PoolAPI.class)); + when(gnAPI.getExpectHealthcheckAPI()).thenReturn(mock(ExpectHealthcheckAPI.class)); + when(gnAPI.getOptionVipV3API()).thenReturn(mock(OptionVipV3API.class)); + } + + static long s_ipSequence = 100; + + @Test + public void testRemoveNullVIP(){ + RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + Answer answer = _resource.execute(cmd); + assertTrue(answer.getResult()); + assertEquals("Vip request was previously removed from GloboNetwork", answer.getDetails()); + } + + @Test + public void testRemoveAlreadyRemovedVIP() throws GloboNetworkException { + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(false); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + + RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + cmd.setVipId(1L); + Answer answer = _resource.execute(cmd); + assertTrue(answer.getResult()); + assertEquals("Vip request 1 was previously removed from GloboNetwork", answer.getDetails()); + } + + @Test + public void testRemoveVIP() throws GloboNetworkException { + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + + RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + cmd.setVipId(1L); + cmd.setKeepIp(true); + + Answer answer = _resource.execute(cmd); + + assertTrue(answer.getResult()); + verify(facadeMock).undeploy(); + verify(facadeMock).delete(true); + } + + @Test + public void testRemoveVIPAndDeleteIP() throws GloboNetworkException { + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + + RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + cmd.setVipId(1L); + cmd.setKeepIp(false); + + Answer answer = _resource.execute(cmd); + + assertTrue(answer.getResult()); + verify(facadeMock).undeploy(); + verify(facadeMock).delete(false); + } + + @Test + public void testRemoveVipWithNetworkApiError() throws GloboNetworkException { + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doThrow(new GloboNetworkException("API error")).when(facadeMock).undeploy(); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + + RemoveVipFromGloboNetworkCommand cmd = new RemoveVipFromGloboNetworkCommand(); + cmd.setVipId(1L); + + Answer answer = _resource.execute(cmd); + assertFalse(answer.getResult()); + } + + @Test + public void testGetBalancingAlgorithmGivenRoundRobin(){ + assertEquals(GloboNetworkResource.LbAlgorithm.RoundRobin, _resource.getBalancingAlgorithm("roundrobin")); + } + + @Test + public void testGetBalancingAlgorithmGivenLeastConn(){ + assertEquals(GloboNetworkResource.LbAlgorithm.LeastConn, _resource.getBalancingAlgorithm("leastconn")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetBalancingAlgorithmGivenInvalid(){ + _resource.getBalancingAlgorithm("random"); + } + + @Test + public void testFindPoolByPort(){ + List pools = new ArrayList<>(); + PoolV3 pool = new PoolV3(); + pool.setDefaultPort(80); + pools.add(pool); + + assertFalse(_resource.findPoolsByPort(80, pools).isEmpty()); + assertTrue(_resource.findPoolsByPort(81, pools).isEmpty()); + } + + @Test + public void testFindPoolByPorGivenNullVip(){ + assertTrue(_resource.findPoolsByPort(80, null).isEmpty()); + } + + public static PoolV3 mockPoolSave(Long poolId, Long idReturned, Boolean hasPoolMember, Integer vipPort, Integer port, String ip, String healthCheckType, String healthCheck, String expectedHealthCheck, int maxConn, String serviceDAction, GloboNetworkAPI gnAPI) throws GloboNetworkException { + PoolV3 expectedPool = new PoolV3(); + expectedPool.setId(poolId); + expectedPool.setIdentifier(GloboNetworkResource.buildPoolName("region", "vip.domain.com",vipPort, port)); + expectedPool.setLbMethod("round-robin"); + expectedPool.setMaxconn(maxConn); + expectedPool.setDefaultPort(port); + expectedPool.setEnvironment(120L); + + PoolV3.Healthcheck healthcheck = expectedPool.getHealthcheck(); + healthcheck.setHealthcheck(healthCheckType, healthCheck, expectedHealthCheck); + healthcheck.setDestination("*:*"); + + PoolV3.ServiceDownAction serviceDownAction = new PoolV3.ServiceDownAction(); + serviceDownAction.setName(serviceDAction); + expectedPool.setServiceDownAction(serviceDownAction); + + if (hasPoolMember) { + PoolV3.PoolMember poolMember = new PoolV3.PoolMember(); + poolMember.setPortReal(port); + poolMember.setWeight(0); + poolMember.setPriority(0); + poolMember.setMemberStatus(7); + poolMember.setEquipmentId(1L); + poolMember.setEquipmentName("vm-01"); + + PoolV3.Ip ipp = new PoolV3.Ip(); + ipp.setIpFormated(ip); + ipp.setId(1L); + poolMember.setIp(ipp); + expectedPool.getPoolMembers().add(poolMember); + } + + PoolV3 newPool = new PoolV3(); + newPool.setId(idReturned); + when(gnAPI.getPoolAPI().save(expectedPool)).thenReturn(newPool); + + return expectedPool; + } + + @Test + public void testGetVipInfosGivenInvalidVlan() throws GloboNetworkException { + Ipv4 ip = new Ipv4(); + ip.setNetworkId(1L); + when(gnAPI.getVipEnvironmentAPI().search(123L, null, null, null)).thenReturn(new VipEnvironment()); + when(gnAPI.getIpAPI().checkVipIp("10.0.0.1", 123L, false)).thenReturn(ip); + when(gnAPI.getNetworkAPI().getNetwork(1L, false)).thenReturn(new IPv4Network()); + when(gnAPI.getVlanAPI().getById(999L)).thenReturn(new Vlan()); + try{ + _resource.getVipInfos(gnAPI, 123L, "10.0.0.1"); + }catch(InvalidParameterValueException e){ + assertEquals("Vlan " + null + " was not found in GloboNetwork", e.getMessage()); + } + } + + @Test + public void testGetVipInfosGivenInvalidNetwork() throws GloboNetworkException { + Ipv4 ip = new Ipv4(); + ip.setNetworkId(1L); + when(gnAPI.getVipEnvironmentAPI().search(123L, null, null, null)).thenReturn(new VipEnvironment()); + when(gnAPI.getIpAPI().checkVipIp("10.0.0.1", 123L, false)).thenReturn(ip); + when(gnAPI.getNetworkAPI().getNetwork(1L, false)).thenReturn(new IPv4Network()); + when(gnAPI.getVlanAPI().getById(anyLong())).thenReturn(new Vlan()); + try{ + _resource.getVipInfos(gnAPI, 123L, "10.0.0.1"); + }catch(InvalidParameterValueException e){ + assertEquals("Network " + null + " was not found in GloboNetwork", e.getMessage()); + } + } + + @Test + public void testCreateNewVIPWithZeroReals() throws Exception { + List ports = Collections.singletonList("80:8080"); + ApplyVipInGloboNetworkCommand cmd = createTestApplyVipCommand("vip.test.com", "10.1.1.1"); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.getPoolIds()).thenReturn(new ArrayList()); + when(facadeMock.hasVip()).thenReturn(false); + when(facadeMock.createVipResponse(cmd)).thenReturn(new GloboNetworkVipResponse()); + doReturn(facadeMock).when(_resource).createVipAPIFacade(cmd.getVipId(), gnAPI); + + mockGetVipMetadata(cmd); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(new PoolV3()); + + Answer answer = _resource.execute(cmd); + + assertTrue(answer.getResult()); + assertTrue(answer instanceof GloboNetworkVipResponse); + verify(gnAPI.getPoolAPI(), times(1)).save(any(PoolV3.class)); + verify(facadeMock).save(any(ApplyVipInGloboNetworkCommand.class), any(String.class), any(VipEnvironment.class), any(Ip.class), any(List.class)); + } + + @Test + public void testCreateNewVIPWithMultiplePortsAndNoReal() throws Exception { + List ports = Arrays.asList("80:8080", "443:8443"); + ApplyVipInGloboNetworkCommand cmd = createTestApplyVipCommand("vip.test.com", "10.1.1.1"); + cmd.setPorts(ports); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.getPoolIds()).thenReturn(new ArrayList()); + when(facadeMock.hasVip()).thenReturn(false); + when(facadeMock.createVipResponse(cmd)).thenReturn(new GloboNetworkVipResponse()); + doReturn(facadeMock).when(_resource).createVipAPIFacade(cmd.getVipId(), gnAPI); + + mockGetVipMetadata(cmd); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(new PoolV3()); + + Answer answer = _resource.execute(cmd); + + assertTrue(answer.getResult()); + assertTrue(answer instanceof GloboNetworkVipResponse); + verify(gnAPI.getPoolAPI(), times(2)).save(any(PoolV3.class)); + verify(facadeMock).save(any(ApplyVipInGloboNetworkCommand.class), any(String.class), any(VipEnvironment.class), any(Ip.class), any(List.class)); + } + + @Test + public void testUpdateVip() throws Exception { + ApplyVipInGloboNetworkCommand cmd = createTestApplyVipCommand("vip.test.com", "10.1.1.1"); + cmd.setVipId(1L); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.getPoolIds()).thenReturn(new ArrayList()); + when(facadeMock.hasVip()).thenReturn(true); + when(facadeMock.createVipResponse(cmd)).thenReturn(new GloboNetworkVipResponse()); + doReturn(facadeMock).when(_resource).createVipAPIFacade(cmd.getVipId(), gnAPI); + + mockGetVipMetadata(cmd); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(new PoolV3()); + + Answer answer = _resource.execute(cmd); + + assertTrue(answer.getResult()); + assertTrue(answer instanceof GloboNetworkVipResponse); + verify(gnAPI.getPoolAPI(), times(1)).save(any(PoolV3.class)); + verify(facadeMock).update(any(ApplyVipInGloboNetworkCommand.class), any(Ip.class), any(List.class)); + } + + @Test + public void testCreateVipGivenFailedOperation() throws Exception { + ApplyVipInGloboNetworkCommand cmd = createTestApplyVipCommand("vip.test.com", "10.1.1.1"); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.getPoolIds()).thenReturn(new ArrayList()); + when(facadeMock.hasVip()).thenReturn(false); + when(facadeMock.save(eq(cmd), any(String.class), any(VipEnvironment.class), any(Ip.class), any(List.class))).thenThrow(new GloboNetworkException("API Error")); + doReturn(facadeMock).when(_resource).createVipAPIFacade(cmd.getVipId(), gnAPI); + + mockGetVipMetadata(cmd); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(new PoolV3()); + Answer answer = _resource.execute(cmd); + + assertFalse(answer.getResult()); + assertFalse(answer instanceof GloboNetworkVipResponse); + verify(facadeMock, times(1)).save(any(ApplyVipInGloboNetworkCommand.class), any(String.class), any(VipEnvironment.class), any(Ip.class), any(List.class)); + verify(gnAPI.getPoolAPI(), times(1)).deleteV3(any(List.class)); + } + + @Test + public void testUpdateVipGivenFailedOperation() throws Exception { + ApplyVipInGloboNetworkCommand cmd = createTestApplyVipCommand("vip.test.com", "10.1.1.1"); + cmd.setVipId(1L); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.getPoolIds()).thenReturn(new ArrayList()); + when(facadeMock.hasVip()).thenReturn(true); + when(facadeMock.update(eq(cmd), any(Ip.class), any(List.class))).thenThrow(new GloboNetworkException("API Error")); + doReturn(facadeMock).when(_resource).createVipAPIFacade(cmd.getVipId(), gnAPI); + + mockGetVipMetadata(cmd); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(new PoolV3()); + Answer answer = _resource.execute(cmd); + + assertFalse(answer.getResult()); + assertFalse(answer instanceof GloboNetworkVipResponse); + verify(facadeMock, times(1)).update(any(ApplyVipInGloboNetworkCommand.class), any(Ip.class), any(List.class)); + verify(gnAPI.getPoolAPI(), times(0)).deleteV3(any(List.class)); + } + + private ApplyVipInGloboNetworkCommand createTestApplyVipCommand(String name, String ip) { + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setVipId(null); + cmd.setHost(name); + cmd.setIpv4(ip); + cmd.setVipEnvironmentId(120L); + cmd.setPorts(Collections.singletonList("80:8080")); + cmd.setRealList(new ArrayList()); + cmd.setMethodBal("roundrobin"); + + HealthCheckHelper build = HealthCheckHelper.build("vip.domain.com", "TCP", "", null); + cmd.setHealthcheckType(build.getHealthCheckType()); + cmd.setExpectedHealthcheck(build.getExpectedHealthCheck()); + cmd.setHealthcheck(build.getHealthCheck()); + return cmd; + } + + + private void mockGetVipMetadata(ApplyVipInGloboNetworkCommand cmd) throws GloboNetworkException { + when(gnAPI.getVipEnvironmentAPI().search(anyLong(), isNull(String.class), isNull(String.class), isNull(String.class))).thenReturn(new VipEnvironment()); + when(gnAPI.getIpAPI().checkVipIp(cmd.getIpv4(), 120L, false)).thenReturn(new Ipv4()); + when(gnAPI.getNetworkAPI().getNetwork(anyLong(), eq(false))).thenReturn(new IPv4Network()); + when(gnAPI.getVlanAPI().getById(anyLong())).thenReturn(new Vlan()); + } + + @Test + public void testCreateVipResponseGivenVipWithOnePortMapping() throws GloboNetworkException { + VipV3 vip = createVipV3(1L, "vip.teste.com", Collections.singletonList("80:8080")); + + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getCacheGroupId())).thenReturn(new OptionVipV3(1L, "cache", "(nenhum)")); + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getPersistenceId())).thenReturn(new OptionVipV3(1L, "Persistence", "cookie")); + when(gnAPI.getIpAPI().getIp(vip.getIpv4Id(), false)).thenReturn(createIpv4(10, 1, 1, 1)); + when(gnAPI.getPoolAPI().getById(0L)).thenReturn(mockPool(0L, "ACS_POOL_vip.teste.com_8080", 8080, "least", "HTTP", "/health.html", "OK", "*:*", 10)); + + GloboNetworkVipResponse answer = (GloboNetworkVipResponse) _resource.createVipResponse(vip, new GetVipInfoFromGloboNetworkCommand(1L, false), gnAPI); + + assertTrue(answer.getResult()); + assertEquals(0, answer.getReals().size()); + assertEquals(vip.getId(), answer.getId()); + assertEquals(vip.getName(), answer.getName()); + assertEquals("10.1.1.1", answer.getIp()); + assertEquals(vip.getEnvironmentVipId(), answer.getLbEnvironmentId()); + assertEquals("(nenhum)", answer.getCache()); + assertEquals("cookie", answer.getPersistence()); + assertEquals("least", answer.getMethod()); + assertEquals("HTTP", answer.getHealthcheckType()); + assertEquals("/health.html", answer.getHealthcheck()); + assertEquals(new Integer(10), answer.getMaxConn()); + } + + @Test + public void testCreateVipResponseGivenVipWithMoreThanOnePortMapping() throws GloboNetworkException { + VipV3 vip = createVipV3(1L, "vip.teste.com", Arrays.asList("80:8080", "443:8443")); + + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getCacheGroupId())).thenReturn(new OptionVipV3(1L, "cache", "(nenhum)")); + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getPersistenceId())).thenReturn(new OptionVipV3(1L, "Persistence", "cookie")); + when(gnAPI.getIpAPI().getIp(vip.getIpv4Id(), false)).thenReturn(createIpv4(10, 1, 1, 1)); + when(gnAPI.getPoolAPI().getById(0L)).thenReturn(mockPool(0L, "ACS_POOL_vip.teste.com_8080", 8080, "least", "HTTP", "/health.html", "OK", "*:*", 10)); + when(gnAPI.getPoolAPI().getById(1L)).thenReturn(mockPool(1L, "ACS_POOL_vip.teste.com_8443", 8443, "least", "HTTP", "/health.html", "OK", "*:*", 10)); + + GloboNetworkVipResponse answer = (GloboNetworkVipResponse) _resource.createVipResponse(vip, new GetVipInfoFromGloboNetworkCommand(1L, false), gnAPI); + + assertTrue(answer.getResult()); + assertEquals(2, answer.getPorts().size()); + assertEquals("80:8080", answer.getPorts().get(0)); + assertEquals("443:8443", answer.getPorts().get(1)); + } + + @Test + public void testCreateVipResponseGivenVipWithMoreThanOneReal() throws GloboNetworkException { + VipV3 vip = createVipV3(1L, "vip.teste.com", Arrays.asList("80:8080", "443:8443")); + PoolV3.PoolMember member1 = new PoolV3.PoolMember(); + member1.setId(1L); + member1.setEquipmentName("member1"); + PoolV3.Ip ip1 = new PoolV3.Ip(); + ip1.setId(1L); + member1.setIp(ip1); + + PoolV3.PoolMember member2 = new PoolV3.PoolMember(); + member2.setId(2L); + member2.setEquipmentName("member1"); + PoolV3.Ip ip2 = new PoolV3.Ip(); + ip2.setId(2L); + member2.setIp(ip2); + + PoolV3 pool1 = mockPool(0L, "ACS_POOL_vip.teste.com_8080", 8080, "least", "HTTP", "/health.html", "OK", "*:*", 10); + pool1.setPoolMembers(Arrays.asList(member1, member2)); + PoolV3 pool2 = mockPool(1L, "ACS_POOL_vip.teste.com_8443", 8443, "least", "HTTP", "/health.html", "OK", "*:*", 10); + pool2.setPoolMembers(Arrays.asList(member1, member2)); + + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getCacheGroupId())).thenReturn(new OptionVipV3(1L, "cache", "(nenhum)")); + when(gnAPI.getOptionVipV3API().findOptionsById(vip.getEnvironmentVipId(), vip.getOptions().getPersistenceId())).thenReturn(new OptionVipV3(1L, "Persistence", "cookie")); + when(gnAPI.getIpAPI().getIp(vip.getIpv4Id(), false)).thenReturn(createIpv4(10, 1, 1, 1)); + when(gnAPI.getPoolAPI().getById(0L)).thenReturn(pool1); + when(gnAPI.getPoolAPI().getById(1L)).thenReturn(pool2); + + GloboNetworkVipResponse answer = (GloboNetworkVipResponse) _resource.createVipResponse(vip, new GetVipInfoFromGloboNetworkCommand(1L, false), gnAPI); + + assertTrue(answer.getResult()); + assertEquals(2, answer.getPorts().size()); + assertEquals("80:8080", answer.getPorts().get(0)); + assertEquals("443:8443", answer.getPorts().get(1)); + assertEquals(2, answer.getReals().size()); + } + + @Test + public void testListPoolOptions() throws IOException, GloboNetworkException { + List options = Collections.singletonList(new PoolOption(1L, "reset")); + IPv4Network network = new IPv4Network(); + network.setVlanId(1L); + Vlan vlan = new Vlan(); + vlan.setEnvironment(1L); + when(gnAPI.getNetworkJsonAPI().listVipNetworks(45L, false)).thenReturn(Arrays.asList(network)); + when(gnAPI.getVlanAPI().getById(1L)).thenReturn(vlan); + when(gnAPI.getPoolAPI().listPoolOptionsV3(1L, "ServiceDownAction")).thenReturn(options); + GloboNetworkPoolOptionResponse answer = (GloboNetworkPoolOptionResponse) _resource.executeRequest(new ListPoolOptionsCommand(45L, "ServiceDownAction")); + assertFalse(answer.getPoolOptions().isEmpty()); + assertEquals(new Long(1L), answer.getPoolOptions().get(0).getId()); + } + + @Test + public void testListPoolOptionsGivenNoPoolsReturned() throws IOException, GloboNetworkException { + IPv4Network network = new IPv4Network(); + network.setVlanId(1L); + Vlan vlan = new Vlan(); + vlan.setEnvironment(1L); + when(gnAPI.getNetworkJsonAPI().listVipNetworks(45L, false)).thenReturn(Arrays.asList(network)); + when(gnAPI.getVlanAPI().getById(1L)).thenReturn(vlan); + when(gnAPI.getPoolAPI().listPoolOptionsV3(1L, "ServiceDownAction")).thenReturn(new ArrayList()); + GloboNetworkPoolOptionResponse answer = (GloboNetworkPoolOptionResponse) _resource.executeRequest(new ListPoolOptionsCommand(45L, "ServiceDownAction")); + assertTrue(answer.getPoolOptions().isEmpty()); + } + + @Test + public void testListPoolOptionsGivenGloboNetworkException() throws IOException, GloboNetworkException { + IPv4Network network = new IPv4Network(); + network.setVlanId(1L); + Vlan vlan = new Vlan(); + vlan.setEnvironment(1L); + when(gnAPI.getNetworkJsonAPI().listVipNetworks(45L, false)).thenReturn(Arrays.asList(network)); + when(gnAPI.getVlanAPI().getById(1L)).thenReturn(vlan); + when(gnAPI.getPoolAPI().listPoolOptionsV3(1L, "ServiceDownAction")).thenThrow(new GloboNetworkException("Netapi failed")); + Answer answer = _resource.executeRequest(new ListPoolOptionsCommand(45L, "ServiceDownAction")); + assertFalse(answer.getResult()); + assertEquals("Netapi failed", answer.getDetails()); + } + + @Test + public void testListPoolOptionsGivenIOException() throws IOException, GloboNetworkException { + IPv4Network network = new IPv4Network(); + network.setVlanId(1L); + Vlan vlan = new Vlan(); + vlan.setEnvironment(1L); + when(gnAPI.getNetworkJsonAPI().listVipNetworks(45L, false)).thenReturn(Arrays.asList(network)); + when(gnAPI.getVlanAPI().getById(1L)).thenReturn(vlan); + when(gnAPI.getPoolAPI().listPoolOptionsV3(1L, "ServiceDownAction")).thenThrow(new IOException()); + Answer answer = _resource.executeRequest(new ListPoolOptionsCommand(45L, "ServiceDownAction")); + assertFalse(answer.getResult()); + } + + @Test + public void testListPoolExecute() throws GloboNetworkException { + List poolsNetworkApi = new ArrayList<>(); + PoolV3 pool1 = new PoolV3(); + pool1.setId(33L); + pool1.setIdentifier("my_pool"); + pool1.setLbMethod("leastcon"); + pool1.setDefaultPort(80); + poolsNetworkApi.add(pool1); + + + PoolV3 pool2 = new PoolV3(); + pool2.setId(22L); + pool2.setIdentifier("my_pool_2"); + pool2.setLbMethod("round"); + pool2.setDefaultPort(8091); + poolsNetworkApi.add(pool1); + + when(gnAPI.getPoolAPI().getById(33L)).thenReturn(pool1); + when(gnAPI.getPoolAPI().getById(22L)).thenReturn(pool2); + + + List options = new ArrayList<>(); + OptionVipV3 opitionTCP = new OptionVipV3(123L, "l4_protocol", "TCP"); + options.add(opitionTCP); + OptionVipV3 opitionHTTP = new OptionVipV3(321L, "l7_protocol", "HTTP"); + options.add(opitionHTTP); + when(gnAPI.getOptionVipV3API().listOptions(333L)).thenReturn(options); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + doReturn(facadeMock).when(_resource).createVipAPIFacade(123L, gnAPI); + when(facadeMock.getVip()).thenReturn(createMockVipV3(pool1.getId(), pool2.getId())); + + createMockVipV3(pool1.getId(), pool2.getId()); + + ListPoolLBCommand cmd = new ListPoolLBCommand(123L); + Answer answer = _resource.executeRequest(cmd); + + List pools = ((GloboNetworkPoolResponse)answer).getPools(); + + assertEquals(2, pools.size()); + + GloboNetworkPoolResponse.Pool pool = pools.get(0); + assertEquals((Long) 33L, pool.getId()); + assertEquals("my_pool", pool.getIdentifier()); + assertEquals("leastcon", pool.getLbMethod()); + assertEquals((Integer) 80, pool.getPort()); + } + + private VipV3 createMockVipV3(Long poolId1, Long poolId2) { + VipV3 vip = new VipV3(); + VipV3.Port port1 = new VipV3.Port(); + port1.setPort(80); + VipV3.Pool p1 = new VipV3.Pool(); + p1.setPoolId(poolId1); + port1.setPools(Collections.singletonList(p1)); + VipV3.PortOptions options = new VipV3.PortOptions(); + options.setL4ProtocolId(123L); + options.setL7ProtocolId(321L); + port1.setOptions(options); + + VipV3.Port port2 = new VipV3.Port(); + port1.setPort(80); + VipV3.Pool p2 = new VipV3.Pool(); + p2.setPoolId(poolId2); + port2.setPools(Collections.singletonList(p2)); + VipV3.PortOptions options2 = new VipV3.PortOptions(); + options2.setL4ProtocolId(123L); + options2.setL7ProtocolId(321L); + port2.setOptions(options); + + vip.setPorts(Arrays.asList(port1, port2)); + + vip.setEnvironmentVipId(333L); + return vip; + } + + @Test + public void testCreatePool() throws GloboNetworkException { + CreatePoolCommand command = createMockCreatePoolCommand(); + PoolV3 pool = new PoolV3(); + pool.setDefaultPort(80); + mockGetVipInfos(); + command.setL4protocol(Protocol.L4.TCP); + command.setL7protocol(Protocol.L7.OTHERS); + + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(pool); + + GloboNetworkPoolResponse answer = (GloboNetworkPoolResponse) _resource.executeRequest(command); + + assertTrue(answer.getResult()); + assertNotNull(answer.getPool()); + verify(gnAPI.getPoolAPI()).save(any(PoolV3.class)); + verify(facadeMock).addPool(any(VipEnvironment.class), eq(80), eq("TCP"), eq((String)"Outros"), any(PoolV3.class)); + } + + @Test + public void testCreatePoolGivenPoolCreationError() throws GloboNetworkException { + CreatePoolCommand command = createMockCreatePoolCommand(); + mockGetVipInfos(); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenThrow(new GloboNetworkException("")); + + Answer answer = _resource.executeRequest(command); + + assertFalse(answer.getResult()); + verify(gnAPI.getPoolAPI()).save(any(PoolV3.class)); + verify(gnAPI.getPoolAPI(), times(0)).deleteV3(any(Long.class)); + verify(facadeMock, times(0)).addPool(any(VipEnvironment.class), eq(80), eq("TCP"), eq((String)null), any(PoolV3.class)); + } + + @Test + public void testCreatePoolGivenVipDeployError() throws GloboNetworkException { + CreatePoolCommand command = createMockCreatePoolCommand(); + PoolV3 pool = new PoolV3(); + pool.setId(1L); + pool.setDefaultPort(80); + mockGetVipInfos(); + command.setL4protocol(Protocol.L4.TCP); + command.setL7protocol(Protocol.L7.OTHERS); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(true); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + doThrow(new GloboNetworkException("")).when(facadeMock).addPool(any(VipEnvironment.class), eq(80), eq("TCP"), eq((String)"Outros"), any(PoolV3.class)); + when(gnAPI.getPoolAPI().save(any(PoolV3.class))).thenReturn(pool); + + Answer answer = _resource.executeRequest(command); + + assertFalse(answer.getResult()); + verify(gnAPI.getPoolAPI()).save(any(PoolV3.class)); + verify(facadeMock).addPool(any(VipEnvironment.class), eq(80), eq("TCP"), eq((String)"Outros"), any(PoolV3.class)); + verify(gnAPI.getPoolAPI()).deleteV3(any(Long.class)); + } + + @Test + public void testCreatePoolGivenVipNotCreated() throws GloboNetworkException { + CreatePoolCommand command = createMockCreatePoolCommand(); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + when(facadeMock.hasVip()).thenReturn(false); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + + GloboNetworkPoolResponse answer = (GloboNetworkPoolResponse) _resource.executeRequest(command); + + assertTrue(answer.getResult()); + verify(facadeMock, times(0)).addPool(any(VipEnvironment.class), eq(80), eq("TCP"), eq((String)null), any(PoolV3.class)); + } + + @Test + public void testDeletePool() throws GloboNetworkException { + PoolV3 pool = new PoolV3(); + pool.setId(1L); + pool.setPoolCreated(true); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + when(gnAPI.getPoolAPI().getById(1L)).thenReturn(pool); + + Answer answer = _resource.executeRequest(new DeletePoolCommand(1L, 1L, 80)); + + assertTrue(answer.getResult()); + verify(facadeMock).removePool(1L); + verify(gnAPI.getPoolAPI()).getById(1L); + verify(gnAPI.getPoolAPI()).undeployV3(1L); + verify(gnAPI.getPoolAPI()).deleteV3(1L); + } + + @Test + public void testDeletePoolGivenNotDeployedPool() throws GloboNetworkException { + PoolV3 pool = new PoolV3(); + pool.setId(1L); + pool.setPoolCreated(false); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + when(gnAPI.getPoolAPI().getById(1L)).thenReturn(pool); + + Answer answer = _resource.executeRequest(new DeletePoolCommand(1L, 1L, 80)); + + assertTrue(answer.getResult()); + verify(facadeMock).removePool(1L); + verify(gnAPI.getPoolAPI()).getById(1L); + verify(gnAPI.getPoolAPI(), times(0)).undeployV3(1L); + verify(gnAPI.getPoolAPI()).deleteV3(1L); + } + + @Test + public void testDeletePoolGivenError() throws GloboNetworkException { + PoolAPI poolAPI = gnAPI.getPoolAPI(); + PoolV3 pool = new PoolV3(); + pool.setId(1L); + pool.setPoolCreated(true); + PoolV3.Healthcheck healthcheck = new PoolV3.Healthcheck(); + healthcheck.setHealthcheck("TCP", null, null); + pool.setHealthcheck(healthcheck); + + VipV3 vip = new VipV3(); + vip.setEnvironmentVipId(99L); + + VipAPIFacade facadeMock = mock(VipAPIFacade.class); + doReturn(facadeMock).when(_resource).createVipAPIFacade(1L, gnAPI); + when(facadeMock.getVip()).thenReturn(vip); + when(poolAPI.getById(1L)).thenReturn(pool); + doThrow(new RuntimeException("")).when(poolAPI).deleteV3(1L); + when(gnAPI.getVipEnvironmentAPI().search(99L, null, null, null)).thenReturn(new VipEnvironment()); + + Answer answer = _resource.executeRequest(new DeletePoolCommand(1L, 1L, 80)); + + assertFalse(answer.getResult()); + verify(facadeMock).removePool(1L); + verify(poolAPI, times(2)).getById(1L); + verify(poolAPI).undeployV3(1L); + verify(poolAPI).deleteV3(1L); + // verify rollback + verify(gnAPI.getVipEnvironmentAPI()).search(99L, null, null, null); + verify(facadeMock).addPool(any(VipEnvironment.class), eq(80), eq(pool.getHealthcheck().getHealthcheckType()), eq((String)"Outros"), eq(pool)); + } + + private void mockGetVipInfos() throws GloboNetworkException { + when(gnAPI.getVipEnvironmentAPI().search(999L, null, null, null)).thenReturn(new VipEnvironment()); + Ipv4 ip = new Ipv4(); + ip.setNetworkId(1L); + when(gnAPI.getIpAPI().checkVipIp("192.168.10.4", 999L, false)).thenReturn(ip); + IPv4Network network = new IPv4Network(); + network.setVlanId(999L); + when(gnAPI.getNetworkAPI().getNetwork(1L, false)).thenReturn(network); + when(gnAPI.getVlanAPI().getById(999L)).thenReturn(new Vlan()); + } + + private CreatePoolCommand createMockCreatePoolCommand() { + CreatePoolCommand command = new CreatePoolCommand(); + command.setL4protocol(Protocol.L4.TCP); + command.setVipId(1L); + command.setVipName("dummy.lb.com"); + command.setVipIp("192.168.10.4"); + command.setVipEnvironment(999L); + command.setPublicPort(80); + command.setPrivatePort(80); + command.setBalacingAlgorithm("leastconn"); + command.setReals(new ArrayList()); + command.setRegion("region"); + return command; + } + + public static PoolV3 mockPool(Long poolId, String identifier, int port, String lbmethod, String healthheckType, String healthcheck, String expectedHealthcheck, String destination, Integer maxconn) { + PoolV3.ServiceDownAction action = new PoolV3.ServiceDownAction(); + action.setId(3L); + action.setName("none"); + + PoolV3 pool = new PoolV3(); + pool.setId(poolId); + pool.setIdentifier(identifier); + pool.setDefaultPort(port); + pool.setLbMethod(lbmethod); + pool.setMaxconn(maxconn); + pool.setEnvironment(120L); + pool.setServiceDownAction(action); + + PoolV3.Healthcheck healthchecker = pool.getHealthcheck(); + healthchecker.setHealthcheck(healthheckType, healthcheck, expectedHealthcheck); + healthchecker.setDestination(destination); + + return pool; + } + + @Test + public void testBuildPoolMembers() throws GloboNetworkException { + List realList = new ArrayList<>(); + GloboNetworkVipResponse.Real real = new GloboNetworkVipResponse.Real(); + real.setIp("10.0.0.1"); + real.setEnvironmentId(1212L); + real.setVmName("vmname-1"); + realList.add(real); + + GloboNetworkVipResponse.Real real2 = new GloboNetworkVipResponse.Real(); + real2.setEnvironmentId(1212L); + real2.setIp("10.0.0.2"); + real2.setVmName("vmname-2"); + realList.add(real2); + + GloboNetworkVipResponse.Real realRevoked = new GloboNetworkVipResponse.Real(); + realRevoked.setRevoked(true); + realList.add(realRevoked); + + //real 1 + Ipv4 ipv4 = new Ipv4(); + ipv4.setId(1111L); + when(gnAPI.getIpAPI().findByIpAndEnvironment("10.0.0.1", 1212L, false)).thenReturn(ipv4); + + Equipment equipment = new Equipment(); + equipment.setId(111L); + equipment.setName("equip-1"); + when(gnAPI.getEquipmentAPI().listByName("vmname-1")).thenReturn(equipment); + + //real 2 + ipv4 = new Ipv4(); + ipv4.setId(2222L); + when(gnAPI.getIpAPI().findByIpAndEnvironment("10.0.0.2", 1212L, false)).thenReturn(ipv4); + + equipment = new Equipment(); + equipment.setId(222L); + equipment.setName("equip-2"); + when(gnAPI.getEquipmentAPI().listByName("vmname-2")).thenReturn(equipment); + + //execute + List poolMembers = _resource.buildPoolMembers(gnAPI, realList); + + //assert + assertEquals(2, poolMembers.size()); + + PoolV3.PoolMember poolMember = poolMembers.get(0); + assertEquals("10.0.0.1", poolMember.getIp().getIpFormated()); + assertEquals((Long) 1111L, poolMember.getIp().getId()); + assertEquals((Long) 111L, poolMember.getEquipmentId()); + assertEquals("equip-1", poolMember.getEquipmentName()); + + PoolV3.PoolMember poolMember2 = poolMembers.get(1); + assertEquals("10.0.0.2", poolMember2.getIp().getIpFormated()); + assertEquals((Long) 2222L, poolMember2.getIp().getId()); + assertEquals((Long) 222L, poolMember2.getEquipmentId()); + assertEquals("equip-2", poolMember2.getEquipmentName()); + } + + @Test + public void testSavePoolsEmptyPool() throws GloboNetworkException { + //mock input + List ports = Arrays.asList("80:8080", "443:8443"); + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setHost("vip.domain.com"); + cmd.setPorts(ports); + cmd.setHealthcheckType("HTTP"); + cmd.setHealthcheck("/index.html"); + cmd.setExpectedHealthcheck("WORKING"); + cmd.setHealthCheckDestination(null); + cmd.setMethodBal("roundrobin"); + cmd.setRegion("region"); + + GloboNetworkResource.VipInfoHelper vipInfos = new GloboNetworkResource.VipInfoHelper(125L, null, null, null); + + //mock save + List poolMembers = new ArrayList<>(); + + PoolV3 pool = new PoolV3(); + pool.setIdentifier("ACS_POOL_region_vip.domain.com_80_8080"); + pool.setLbMethod("round-robin"); + pool.setMaxconn(0); + pool.setDefaultPort(8080); + pool.setEnvironment(125L); + + PoolV3.Healthcheck healthcheck = pool.getHealthcheck(); + healthcheck.setHealthcheck("HTTP", "/index.html", "WORKING"); + healthcheck.setDestination(null); + + when(gnAPI.getPoolAPI().save(pool)).thenReturn(new PoolV3(123L)); + + PoolV3 pool2 = new PoolV3(); + pool2.setIdentifier("ACS_POOL_region_vip.domain.com_443_8443"); + pool2.setLbMethod("round-robin"); + pool2.setMaxconn(0); + pool2.setDefaultPort(8443); + pool2.setEnvironment(125L); + + PoolV3.Healthcheck healthcheck2 = pool2.getHealthcheck(); + healthcheck2.setHealthcheck("TCP", "", ""); + healthcheck2.setDestination(null); + + when(gnAPI.getPoolAPI().save(pool2)).thenReturn(new PoolV3(321L)); + + //execute + List vipPoolMaps = _resource.createPools(gnAPI, poolMembers, vipInfos.getEnvironment(), cmd); + + assertEquals(2, vipPoolMaps.size()); + + VipPoolMap vipPoolMap = vipPoolMaps.get(0); + assertEquals((Long) 123L, vipPoolMap.getPoolId()); + assertEquals((Integer)80, vipPoolMap.getPort()); + + vipPoolMap = vipPoolMaps.get(1); + assertEquals((Long) 321L, vipPoolMap.getPoolId()); + assertEquals((Integer)443, vipPoolMap.getPort()); + } + + @Test + public void testSavePoolWithOneReal() throws GloboNetworkException { + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setRealList(Collections.singletonList(new GloboNetworkVipResponse.Real())); + cmd.setMethodBal("roundrobin"); + cmd.setHost("vip.domain.com"); + cmd.setPorts(Collections.singletonList("80:8080")); + + HealthCheckHelper build = HealthCheckHelper.build("vip.domain.com", "TCP", "", null); + cmd.setHealthcheckType(build.getHealthCheckType()); + cmd.setExpectedHealthcheck(build.getExpectedHealthCheck()); + cmd.setHealthcheck(build.getHealthCheck()); + cmd.setRegion("region"); + + List poolIds = new ArrayList<>(); // new VIP no pools created yet + Ipv4 ip = new Ipv4(); + ip.setNetworkId(1L); + + List poolMembers = new ArrayList<>(); + PoolV3.PoolMember poolMember = new PoolV3.PoolMember(); + poolMember.setPortReal(8080); + poolMember.setPriority(0); + poolMember.setMemberStatus(7); + poolMember.setEquipmentName("vm-01"); + poolMember.setEquipmentId(1L); + poolMember.setWeight(0); + + PoolV3.Ip ipPm = new PoolV3.Ip(); + ipPm.setId(1L); + ipPm.setIpFormated("10.0.0.1"); + + poolMember.setIp(ipPm); + poolMembers.add(poolMember); + + GloboNetworkResource.VipInfoHelper vipInfo = new GloboNetworkResource.VipInfoHelper(120L, null, null, null); + + PoolV3 expectedPool = mockPoolSave(null, 123L, true, 80, 8080, "10.0.0.1", + build.getHealthCheckType(), build.getExpectedHealthCheck(), build.getHealthCheck(), 0, + cmd.getServiceDownAction(), gnAPI); + + + List vipPoolMaps = _resource.createPools(gnAPI, poolMembers, vipInfo.getEnvironment(), cmd); + + VipPoolMap vipPoolMap = vipPoolMaps.get(0); + assertNotNull(vipPoolMap); + assertEquals(new Integer(80), vipPoolMap.getPort()); + verify(gnAPI.getPoolAPI(), times(1)).save(expectedPool); + } + + @Test + public void testSavePoolAddRealToExistingPool() throws GloboNetworkException { + //input 1 - vip + Pool pool = new Pool(); + pool.setDefaultPort(8080); + pool.setId(12L); + pool.setMaxconn(0); + + List poolIds = Collections.singletonList(pool.getId()); + Ipv4 ip = new Ipv4(); + ip.setNetworkId(1L); + + //input 2 - vip info + GloboNetworkResource.VipInfoHelper vipInfo = new GloboNetworkResource.VipInfoHelper(121L, null, null, null); + + //input 3 - poolMembers + List poolMembers = new ArrayList<>(); + PoolV3.PoolMember real1 = mockPoolMember(null, 8080, 1L, "10.0.0.1", 1L, "vm-01"); + real1.setLimit(10); + poolMembers.add(real1); + PoolV3.PoolMember real2 = mockPoolMember(null, 8080, 2L, "10.0.0.2", 2L, "vm-02"); + poolMembers.add(real2); + real1.setLimit(10); + + //input 4 - cmd + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setMethodBal("roundrobin"); + cmd.setHost("vip.domain.com"); + cmd.setPorts(Collections.singletonList("80:8080")); + HealthCheckHelper build = HealthCheckHelper.build("vip.domain.com", "TCP", "", null); + cmd.setHealthcheckType(build.getHealthCheckType()); + cmd.setExpectedHealthcheck(build.getExpectedHealthCheck()); + cmd.setHealthcheck(build.getHealthCheck()); + + //mock 1 - Pool find by id - v3 + PoolV3 poolv3GetById = mockPool(12L, "ACS_POOL_", 8080, "round-robin", build.getHealthCheckType(), build.getHealthCheck(), build.getExpectedHealthCheck(), "*", 5); + poolv3GetById.setMaxconn(10); + PoolV3.PoolMember poolM = mockPoolMember(200L, 8080, 1L, "10.0.0.1", 1L, "vm-01"); + poolM.setLimit(10); + poolv3GetById.getPoolMembers().add(poolM); + + when(gnAPI.getPoolAPI().getById(12L)).thenReturn(poolv3GetById); + when(gnAPI.getPoolAPI().getByIdsV3(Collections.singletonList(12L))).thenReturn(Collections.singletonList(poolv3GetById)); + + //mock 2 - Pool save pool + PoolV3 poolToSave = mockPool(12L, "ACS_POOL_", 8080, "round-robin", build.getHealthCheckType(), build.getHealthCheck(), build.getExpectedHealthCheck(), "*", 5); + poolToSave.setMaxconn(10); + + PoolV3.PoolMember poolMSaved = mockPoolMember(200L, 8080, 1L, "10.0.0.1", 1L, "vm-01"); + poolMSaved.setLimit(10); + poolToSave.getPoolMembers().add(poolMSaved); + PoolV3.PoolMember poolM2Saved = mockPoolMember(null, 8080, 2L, "10.0.0.2", 2L, "vm-02"); + poolM2Saved.setLimit(10); + poolToSave.getPoolMembers().add(poolM2Saved); + + when(gnAPI.getPoolAPI().save(poolToSave)).thenReturn(new PoolV3(12L)); + + + _resource.updatePools(gnAPI, poolIds, poolMembers, cmd.getPortPairs()); + verify(gnAPI.getPoolAPI(), times(1)).save(poolToSave); + } + + @Test + public void testForceSupportPoolOldVersion() { + assertFalse(_resource.forceSupportOldPoolVersion("HTTP", 8080)); + assertFalse(_resource.forceSupportOldPoolVersion("HTTP", 8081)); + assertFalse(_resource.forceSupportOldPoolVersion("HTTP", 80)); + assertFalse(_resource.forceSupportOldPoolVersion("HTTP", 1234)); + assertTrue(_resource.forceSupportOldPoolVersion("HTTP", 8443)); + assertTrue(_resource.forceSupportOldPoolVersion("HTTP", 443)); + assertTrue(_resource.forceSupportOldPoolVersion("UDP", 8443)); + assertTrue(_resource.forceSupportOldPoolVersion("UDP", 443)); + assertTrue(_resource.forceSupportOldPoolVersion("TCP", 443)); + assertTrue(_resource.forceSupportOldPoolVersion("TCP", 8443)); + assertFalse(_resource.forceSupportOldPoolVersion("HTTPS", 443)); + assertFalse(_resource.forceSupportOldPoolVersion("HTTPS", 8443)); + } + + private PoolV3.PoolMember mockPoolMember(Long id,Integer port, Long ipId, String ip, Long equipId, String equipName) { + PoolV3.PoolMember poolMSaved = new PoolV3.PoolMember(); + if ( id != null ){ + poolMSaved.setId(id); + } + poolMSaved.setPortReal(port); + poolMSaved.setWeight(0); + poolMSaved.setPriority(0); + poolMSaved.setEquipmentId(equipId); + poolMSaved.setEquipmentName(equipName); + + PoolV3.Ip ipppoolMSaved = new PoolV3.Ip(); + ipppoolMSaved.setIpFormated(ip); + ipppoolMSaved.setId(ipId); + poolMSaved.setIp(ipppoolMSaved); + return poolMSaved; + } + + @Test + public void testExecuteListAllExpectedHealthcheck() throws GloboNetworkException { + ListExpectedHealthchecksCommand command = new ListExpectedHealthchecksCommand(); + + List result = new ArrayList<>(); + result.add(new ExpectHealthcheck(1L, "OK")); + result.add(new ExpectHealthcheck(2L, "WORKING")); + + when(gnAPI.getExpectHealthcheckAPI().listHealthcheck()).thenReturn(result); + + Answer answer = _resource.executeRequest(command); + + assertNotNull(answer); + + GloboNetworkExpectHealthcheckResponse response = (GloboNetworkExpectHealthcheckResponse)answer; + + List expectedHealthchecks = response.getExpectedHealthchecks(); + + assertEquals(2, expectedHealthchecks.size()); + + GloboNetworkExpectHealthcheckResponse.ExpectedHealthcheck expectedHealthcheck = expectedHealthchecks.get(0); + assertEquals((Long) 1L, expectedHealthcheck.getId()); + assertEquals("OK", expectedHealthcheck.getExpected()); + + expectedHealthcheck = expectedHealthchecks.get(1); + assertEquals((Long) 2L, expectedHealthcheck.getId()); + assertEquals("WORKING", expectedHealthcheck.getExpected()); + } + + private long getNewIpID() { + return ++s_ipSequence; + } + + protected VipV3 createVipV3(Long vipId, String name, List ports) { + VipV3 vip = new VipV3(); + vip.setId(vipId); + vip.setName(name); + vip.setIpv4Id(1L); + VipV3.VipOptions option = new VipV3.VipOptions(); + option.setCacheGroupId(1L); + option.setPersistenceId(2L); + vip.setOptions(option); + + vip.setPorts(new ArrayList()); + for(int i=0; i < ports.size(); i++){ + String vipPort = ports.get(i).split(":")[0]; + + VipV3.Port port = new VipV3.Port(); + port.setPort(new Integer(vipPort)); + port.setPools(new ArrayList()); + + VipV3.Pool p = new VipV3.Pool(); + p.setPoolId(new Long(i)); + port.getPools().add(p); + + vip.getPorts().add(port); + } + return vip; + } + + private Ipv4 createIpv4(int oct1, int oct2, int oct3, int oct4) { + Ipv4 ip = new Ipv4(); + ip.setOct1(oct1); + ip.setOct2(oct2); + ip.setOct3(oct3); + ip.setOct4(oct4); + return ip; + } +} diff --git a/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/VipAPIFacadeTest.java b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/VipAPIFacadeTest.java new file mode 100644 index 000000000000..ae80bd82faa2 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/com/globo/globonetwork/cloudstack/resource/VipAPIFacadeTest.java @@ -0,0 +1,322 @@ +package com.globo.globonetwork.cloudstack.resource; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.lb.LoadBalancingRule; +import com.globo.globonetwork.client.api.GloboNetworkAPI; +import com.globo.globonetwork.client.api.OptionVipV3API; +import com.globo.globonetwork.client.api.VipV3API; +import com.globo.globonetwork.client.exception.GloboNetworkException; +import com.globo.globonetwork.client.model.VipPoolMap; +import com.globo.globonetwork.client.model.VipV3; +import com.globo.globonetwork.client.model.OptionVipV3; +import com.globo.globonetwork.client.model.Ipv4; +import com.globo.globonetwork.client.model.VipEnvironment; +import com.globo.globonetwork.cloudstack.commands.ApplyVipInGloboNetworkCommand; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.any; + +public class VipAPIFacadeTest { + + private GloboNetworkAPI globoNetworkAPI; + private VipV3API vipAPI; + private OptionVipV3API optionVipV3API; + + @Before + public void setUp() throws Exception { + globoNetworkAPI = mock(GloboNetworkAPI.class); + vipAPI = mock(VipV3API.class); + optionVipV3API = mock(OptionVipV3API.class); + when(globoNetworkAPI.getVipV3API()).thenReturn(vipAPI); + when(globoNetworkAPI.getOptionVipV3API()).thenReturn(optionVipV3API); + } + + @Test + public void testCreateVipAPIFacade() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, false); + assertTrue(facade.hasVip()); + } + + @Test + public void testCreateVipAPIFacadeGivenNullId() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(null, false); + assertFalse(facade.hasVip()); + } + + @Test + public void testCreateVip() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(null, false); + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setVipEnvironmentId(1L); + cmd.setHealthcheckType("TCP"); + VipEnvironment environment = new VipEnvironment(); + environment.setId(1L); + Ipv4 ip = new Ipv4(); + List vipPoolMaps = new ArrayList<>(); + VipPoolMap vipPoolMap = new VipPoolMap(1L, 1L, 1L, 80); + vipPoolMaps.add(vipPoolMap); + + mockVipOptions(environment); + + facade.save(cmd, cmd.getHost(), environment, ip, vipPoolMaps); + assertTrue(facade.hasVip()); + verify(vipAPI).save(any(VipV3.class)); + } + + @Test + public void testUpdateNotDeployedVip() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, false); + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + cmd.setVipEnvironmentId(1L); + cmd.setHealthcheckType("TCP"); + VipEnvironment environment = new VipEnvironment(); + environment.setId(1L); + Ipv4 ip = new Ipv4(); + List vipPoolMaps = new ArrayList<>(); + VipPoolMap vipPoolMap = new VipPoolMap(1L, 1L, 1L, 80); + vipPoolMaps.add(vipPoolMap); + + mockVipOptions(environment); + when(vipAPI.save(any(VipV3.class))).thenReturn(new VipV3()); + + facade.update(cmd, ip, vipPoolMaps); + assertTrue(facade.hasVip()); + verify(vipAPI).save(any(VipV3.class)); + } + + @Test + public void testUpdateVipCreated() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(21L, true); + facade.setVip(buildFakeVip(21L, true)); + + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + LoadBalancingRule.LbStickinessPolicy stickinessPolicy = new LoadBalancingRule.LbStickinessPolicy("Source-ip", null, false); + cmd.setPersistencePolicy(stickinessPolicy); + cmd.setVipEnvironmentId(1L); + + when(optionVipV3API.findOptionsByTypeAndName(1L, "Persistencia", "source-ip")).thenReturn(Collections.singletonList(new OptionVipV3(7L, "Persistencia","Source-ip"))); + + //execute + facade.update(cmd, null, null); + + //test + verify(vipAPI).updatePersistence(21L, 7L); + } + + @Test + public void testUpdateVipNotCreated() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(21L, false); + ApplyVipInGloboNetworkCommand cmd = new ApplyVipInGloboNetworkCommand(); + + cmd.setVipEnvironmentId(1L); + cmd.setHealthcheckType("TCP"); + VipEnvironment environment = new VipEnvironment(); + environment.setId(1L); + + mockVipOptions(environment); + + when(vipAPI.save(facade.getVip())).thenReturn(facade.getVip()); + + //execute + facade.update(cmd, null, null); + + //test + verify(vipAPI).save(facade.getVip()); + } + + @Test + public void testDeployVip() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, false); + facade.deploy(); + verify(vipAPI, times(1)).deploy(1L); + } + + @Test + public void testUndeployVip() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, true); + facade.undeploy(); + verify(vipAPI, times(1)).undeploy(1L); + } + + @Test + public void testDeleteVipKeepingIP() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, true); + facade.delete(true); + verify(vipAPI, times(1)).delete(1L, true); + } + + @Test + public void testDeleteVipNotKeepingIP() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, true); + facade.delete(false); + verify(vipAPI, times(1)).delete(1L, false); + } + + @Test + public void testGetPersistenceMethodNone(){ + assertEquals("(nenhum)", VipAPIFacade.getPersistenceMethod(new LoadBalancingRule.LbStickinessPolicy("None", null))); + assertEquals("(nenhum)", VipAPIFacade.getPersistenceMethod(null)); + } + + @Test + public void testGetPersistenceMethodGivenCookie(){ + assertEquals("cookie", VipAPIFacade.getPersistenceMethod(new LoadBalancingRule.LbStickinessPolicy("Cookie", null))); + } + + @Test + public void testGetPersistenceMethodGivenSourceIp(){ + assertEquals("source-ip", VipAPIFacade.getPersistenceMethod(new LoadBalancingRule.LbStickinessPolicy("Source-ip", null))); + } + + @Test + public void testGetPersistenceMethodGivenSourceIpWithPersistence(){ + assertEquals("source-ip com persist. entre portas", VipAPIFacade.getPersistenceMethod(new LoadBalancingRule.LbStickinessPolicy("Source-ip with persistence between ports", null))); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetPersistenceMethodGivenInvalidPersistence(){ + VipAPIFacade.getPersistenceMethod(new LoadBalancingRule.LbStickinessPolicy("jsession", null)); + } + + @Test + public void testGetPoolIds() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, true); + VipV3 vip = facade.getVip(); + VipV3.PortOptions options = new VipV3.PortOptions(1L, 2L); + VipV3.Pool pool1 = new VipV3.Pool(1L, 5L, null); + VipV3.Pool pool2 = new VipV3.Pool(2L, 2L, "/test"); + VipV3.Port port = new VipV3.Port(1L, 80, options, Arrays.asList(pool1, pool2)); + vip.setPorts(Collections.singletonList(port)); + + OptionVipV3 l7Rule = new OptionVipV3(5L, "l7_rule", "default_vip"); + when(optionVipV3API.findOptionsByTypeAndName(vip.getEnvironmentVipId(), "l7_rule", "default_vip")).thenReturn(Collections.singletonList(l7Rule)); + + List poolIds = facade.getPoolIds(); + + assertEquals(1, poolIds.size()); + assertEquals(new Long(1), poolIds.get(0)); + } + + @Test + public void testGetPoolIdsGivenNoPoolsFound() throws GloboNetworkException { + VipAPIFacade facade = createTestVipAPIFacade(1L, true); + VipV3 vip = facade.getVip(); + VipV3.PortOptions options = new VipV3.PortOptions(1L, 2L); + VipV3.Pool pool1 = new VipV3.Pool(1L, 100L, null); + VipV3.Port port = new VipV3.Port(1L, 80, options, Arrays.asList(pool1)); + vip.setPorts(Collections.singletonList(port)); + + OptionVipV3 l7Rule = new OptionVipV3(5L, "l7_rule", "default_vip"); + when(optionVipV3API.findOptionsByTypeAndName(vip.getEnvironmentVipId(), "l7_rule", "default_vip")).thenReturn(Collections.singletonList(l7Rule)); + + List poolIds = facade.getPoolIds(); + + assertEquals(0, poolIds.size()); + } + + @Test + public void testRemovePoolGivenVipWithOnePoolPerPort() throws GloboNetworkException { + VipV3 vip = new VipV3(); + vip.setId(1L); + + List pools = new ArrayList<>(); + VipV3.Pool pool = new VipV3.Pool(); + pool.setPoolId(1L); + pools.add(pool); + + List ports = new ArrayList<>(); + VipV3.Port port = new VipV3.Port(); + port.setPools(pools); + port.setPort(80); + + ports.add(port); + vip.setPorts(ports); + + VipAPIFacade facade = createTestVipAPIFacade(vip); + + facade.removePool(1L); + + assertEquals(0, vip.getPorts().size()); + verify(globoNetworkAPI.getVipV3API()).deployUpdate(vip); + } + + @Test + public void testRemovePoolGivenVipPortWithTwoPools() throws GloboNetworkException { + VipV3 vip = new VipV3(); + vip.setId(1L); + + List pools = new ArrayList<>(); + VipV3.Pool pool1 = new VipV3.Pool(); + pool1.setPoolId(1L); + VipV3.Pool pool2 = new VipV3.Pool(); + pool2.setPoolId(2L); + pools.add(pool1); + pools.add(pool2); + + List ports = new ArrayList<>(); + VipV3.Port port = new VipV3.Port(); + port.setPools(pools); + port.setPort(80); + + ports.add(port); + vip.setPorts(ports); + + VipAPIFacade facade = createTestVipAPIFacade(vip); + + facade.removePool(1L); + + assertEquals(1, vip.getPorts().size()); + assertEquals(1, vip.getPorts().get(0).getPools().size()); + assertEquals((Long) 2L, vip.getPorts().get(0).getPools().get(0).getPoolId()); + verify(globoNetworkAPI.getVipV3API()).deployUpdate(vip); + } + + private VipV3 buildFakeVip(Long vipId, Boolean created) throws GloboNetworkException { + VipV3 vip = new VipV3(); + vip.setId(vipId); + vip.setCreated(created); + vip.setOptions(new VipV3.VipOptions()); + return vip; + } + + private VipAPIFacade createTestVipAPIFacade(Long vipId, Boolean created) throws GloboNetworkException { + when(vipAPI.getById(vipId)).thenReturn(buildFakeVip(vipId, created)); + return new VipAPIFacade(vipId, globoNetworkAPI); + } + + private VipAPIFacade createTestVipAPIFacade(VipV3 vip) throws GloboNetworkException { + when(vipAPI.getById(vip.getId())).thenReturn(vip); + return new VipAPIFacade(vip.getId(), globoNetworkAPI); + } + + private void mockVipOptions(VipEnvironment environment) throws GloboNetworkException { + OptionVipV3 cache = new OptionVipV3(1L, "cache", "(nenhum)"); + OptionVipV3 trafficReturn = new OptionVipV3(2L, "Retorno de trafego", "Normal"); + OptionVipV3 timeout = new OptionVipV3(3L, "timeout", "5"); + OptionVipV3 persistence = new OptionVipV3(4L, "Persistencia", "(nenhum)"); + OptionVipV3 l7Rule = new OptionVipV3(5L, "l7_rule", "default_vip"); + OptionVipV3 l4Protocol = new OptionVipV3(5L, "l4_protocol", "TCP"); + OptionVipV3 l7Protocol = new OptionVipV3(5L, "l7_protocol", "Outros"); + + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "cache", "(nenhum)")).thenReturn(Collections.singletonList(cache)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "Retorno de trafego", "Normal")).thenReturn(Collections.singletonList(trafficReturn)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "timeout", "5")).thenReturn(Collections.singletonList(timeout)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "Persistencia", "(nenhum)")).thenReturn(Collections.singletonList(persistence)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "l7_rule", "default_vip")).thenReturn(Collections.singletonList(l7Rule)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "l4_protocol", "TCP")).thenReturn(Collections.singletonList(l4Protocol)); + when(optionVipV3API.findOptionsByTypeAndName(environment.getId(), "l7_protocol", "Outros")).thenReturn(Collections.singletonList(l7Protocol)); + } +} \ No newline at end of file diff --git a/plugins/network-elements/globonetwork/test/resources/db.properties b/plugins/network-elements/globonetwork/test/resources/db.properties new file mode 100644 index 000000000000..0dd35f647265 --- /dev/null +++ b/plugins/network-elements/globonetwork/test/resources/db.properties @@ -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. + + +# management server clustering parameters, change cluster.node.IP to the machine IP address +# in which the management server(Tomcat) is running +cluster.node.IP=127.0.0.1 +cluster.servlet.port=9090 +region.id=1 + +# CloudStack database settings +db.cloud.username=cloud +db.cloud.password=cloud +db.root.password= +db.cloud.host=localhost +db.cloud.port=3306 +db.cloud.name=cloud + +# CloudStack database tuning parameters +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 +db.cloud.timeBetweenEvictionRunsMillis=40000 +db.cloud.minEvictableIdleTimeMillis=240000 +db.cloud.poolPreparedStatements=false +db.cloud.url.params=prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096 + +# usage database settings +db.usage.username=cloud +db.usage.password=cloud +db.usage.host=localhost +db.usage.port=3306 +db.usage.name=cloud_usage + +# usage database tuning parameters +db.usage.maxActive=100 +db.usage.maxIdle=30 +db.usage.maxWait=10000 +db.usage.autoReconnect=true + +# awsapi database settings +db.awsapi.username=cloud +db.awsapi.password=cloud +db.awsapi.host=localhost +db.awsapi.port=3306 +db.awsapi.name=cloudbridge + +# Simulator database settings +db.simulator.username=cloud +db.simulator.password=cloud +db.simulator.host=localhost +db.simulator.port=3306 +db.simulator.name=simulator +db.simulator.maxActive=250 +db.simulator.maxIdle=30 +db.simulator.maxWait=10000 +db.simulator.autoReconnect=true diff --git a/plugins/network-elements/globonetwork/test/resources/log4j.properties b/plugins/network-elements/globonetwork/test/resources/log4j.properties new file mode 100644 index 000000000000..1bac606ff63d --- /dev/null +++ b/plugins/network-elements/globonetwork/test/resources/log4j.properties @@ -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. + +# Define the root logger with appender file +#log = /var/log/log4j +#log4j.rootLogger = DEBUG, FILE +log4j.rootLogger = DEBUG, stdout + +# File appender +#log4j.appender.FILE=org.apache.log4j.FileAppender +#log4j.appender.FILE.File=${log}/log.out +#log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +#log4j.appender.FILE.layout.conversionPattern=%m%n + +# Stdout appender +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml index 87a5617e1a6f..6d0e5aac9650 100644 --- a/plugins/network-elements/juniper-contrail/pom.xml +++ b/plugins/network-elements/juniper-contrail/pom.xml @@ -26,13 +26,6 @@ 4.11.1.0 ../../pom.xml - - - juniper-contrail - http://juniper.github.io/contrail-maven/snapshots - - - org.apache.cloudstack 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 38a836d2d6db..a8392af95eac 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 @@ -1327,8 +1327,6 @@ public List updateHealthChecks(Network network, Listuser-authenticators/md5 user-authenticators/pbkdf2 user-authenticators/plain-text + user-authenticators/oauth2 user-authenticators/saml2 user-authenticators/sha256salted network-elements/dns-notifier @@ -106,6 +107,8 @@ network-elements/internal-loadbalancer network-elements/vxlan network-elements/globodns + network-elements/globonetwork + network-elements/globoaclapi database/quota integrations/cloudian integrations/prometheus diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy index 1ff5fce42438..ddbbc6beb811 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapAuthenticatorSpec.groovy @@ -43,6 +43,59 @@ class LdapAuthenticatorSpec extends spock.lang.Specification { result.first() == false } + def "Test a failed authentication due to empty username"() { + given: "We have an LdapManager, userAccountDao and ldapAuthenticator and the user doesn't exist within cloudstack." + LdapManager ldapManager = Mock(LdapManager) + ldapManager.isLdapEnabled() >> true + ldapManager.canAuthenticate(_, _) >> true + + UserAccountDao userAccountDao = Mock(UserAccountDao) + userAccountDao.getUserAccount(_, _) >> new UserAccountVO() + + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + + when: "A user authenticates" + def result = ldapAuthenticator.authenticate("", "password", 0, null) + then: "their authentication fails" + result.first() == false + + when: "A user authenticates" + result = ldapAuthenticator.authenticate(null, "password", 0, null) + then: "their authentication fails" + result.first() == false + + when: "A user authenticates" + result = ldapAuthenticator.authenticate(null, null, 0, null) + then: "their authentication fails" + result.first() == false + + when: "A user authenticates" + result = ldapAuthenticator.authenticate("", "", 0, null) + then: "their authentication fails" + result.first() == false + } + + def "Test failed authentication due to empty password"() { + given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator" + def ldapManager = Mock(LdapManager) + ldapManager.isLdapEnabled() >> true + ldapManager.canAuthenticate(_, _) >> true + + UserAccountDao userAccountDao = Mock(UserAccountDao) + userAccountDao.getUserAccount(_, _) >> new UserAccountVO() + def ldapAuthenticator = new LdapAuthenticator(ldapManager, userAccountDao) + + when: "The user authenticates with empty password" + def result = ldapAuthenticator.authenticate("rmurphy", "", 0, null) + then: "their authentication fails" + result.first() == false + + when: "The user authenticates with null password" + result = ldapAuthenticator.authenticate("rmurphy", null, 0, null) + then: "their authentication fails" + result.first() == false + } + def "Test failed authentication due to ldap bind being unsuccessful"() { given: "We have an LdapManager, LdapConfiguration, userAccountDao and LdapAuthenticator" def ldapManager = Mock(LdapManager) diff --git a/plugins/user-authenticators/oauth2/pom.xml b/plugins/user-authenticators/oauth2/pom.xml new file mode 100644 index 000000000000..33ecc4f0daec --- /dev/null +++ b/plugins/user-authenticators/oauth2/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + cloud-plugin-user-authenticator-oauth2 + Apache CloudStack Plugin - User Authenticator OAuth 2 + + org.apache.cloudstack + cloudstack-plugins + 4.11.1.0 + ../../pom.xml + + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + 1.0.0 + + + diff --git a/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/module.properties b/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/module.properties new file mode 100644 index 000000000000..624ce1e24e15 --- /dev/null +++ b/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/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=oauth2 +parent=api \ No newline at end of file diff --git a/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/spring-oauth2-context.xml b/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/spring-oauth2-context.xml new file mode 100644 index 000000000000..98c49db5eb73 --- /dev/null +++ b/plugins/user-authenticators/oauth2/resources/META-INF/cloudstack/oauth2/spring-oauth2-context.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2Manager.java b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2Manager.java new file mode 100644 index 000000000000..fb9fba15013c --- /dev/null +++ b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2Manager.java @@ -0,0 +1,15 @@ +package org.apache.cloudstack.oauth2; + +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; + +import com.cloud.user.UserAccount; + +public interface OAuth2Manager extends PluggableAPIAuthenticator { + + public String generateAuthenticationUrl(String redirectUri); + + public UserAccount authenticate(String code, String redirectUri); + + public String getLogoutUrlWithProvider(); + +} diff --git a/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2ManagerImpl.java b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2ManagerImpl.java new file mode 100644 index 000000000000..0223708f35ff --- /dev/null +++ b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/OAuth2ManagerImpl.java @@ -0,0 +1,413 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES 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.oauth2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.oauth2.api.OAuth2LoginCmd; +import org.apache.cloudstack.oauth2.api.OAuth2RedirectCmd; +import org.apache.log4j.Logger; +import org.apache.oltu.oauth2.client.OAuthClient; +import org.apache.oltu.oauth2.client.URLConnectionClient; +import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; +import org.apache.oltu.oauth2.client.response.GitHubTokenResponse; +import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse; +import org.apache.oltu.oauth2.client.response.OAuthResourceResponse; +import org.apache.oltu.oauth2.common.OAuth; +import org.apache.oltu.oauth2.common.OAuthProviderType; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.apache.oltu.oauth2.common.message.types.GrantType; +import org.apache.oltu.oauth2.common.utils.JSONUtils; +import org.springframework.stereotype.Component; + +import com.cloud.domain.Domain; +import com.cloud.exception.CloudAuthenticationException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainManager; +import com.cloud.user.UserAccount; +import com.cloud.user.dao.UserAccountDao; +import com.cloud.utils.StringUtils; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.exception.CloudRuntimeException; + +@Component +public class OAuth2ManagerImpl extends AdapterBase implements OAuth2Manager, PluggableAPIAuthenticator, Configurable { + + public static final Logger s_logger = Logger.getLogger(OAuth2ManagerImpl.class); + + @Inject + UserAccountDao _userAccDao; + @Inject + DomainManager _domainMgr; + @Inject + AccountManager _accManager; + + /* Constants */ + private static final String defaultAccessScopeGitHub = "user"; + private String defaultUserInfoUrlGitHub = "https://api.github.com/user"; + private String defaultUserAttributeGitHub = "login"; + private String defaultLogoutUrlGitHub = "https://github.com/logout"; + private String defaultAccessScopeGoogle = "openid profile email"; + private String defaultUserInfoUrlGoogle = "https://www.googleapis.com/oauth2/v1/userinfo"; + private String defaultUserAttributeGoogle = "email"; + private String defaultLogoutUrlGoogle = "https://accounts.google.com/logout"; + + /* Authorization Provider */ + private static final ConfigKey AuthorizationProvider = new ConfigKey("Authentication", String.class, "oauth2.authorization.provider", "", + "OAuth2 provider name. Options are: GITHUB or GOOGLE. Leave it blank to use your own OAuth2 provider.", true, ConfigKey.Scope.Global); + + protected String getAuthorizationProvider() { + return AuthorizationProvider.value(); + } + + /* Client ID */ + private static final ConfigKey ClientID = new ConfigKey("Authentication", String.class, "oauth2.client.id", "", + "Client ID to be used for OAuth2 authentication", true, ConfigKey.Scope.Global); + + protected String getClientID() { + return ClientID.value(); + } + + /* Client Secret */ + private static final ConfigKey ClientSecret = new ConfigKey("Authentication", String.class, "oauth2.client.secret", "", + "Client Secret to be used for OAuth2 authentication", true, ConfigKey.Scope.Global); + + protected String getClientSecret() { + return ClientSecret.value(); + } + + /* Access Scope */ + private static final ConfigKey AccessScope = new ConfigKey("Authentication", String.class, "oauth2.access.scope", "", + "Access scope the user will be prompted to accept by the OAuth2 provider. If set, it should be a list of values separated by commas.", true, ConfigKey.Scope.Global); + + protected String getAccessScope() { + return AccessScope.value(); + } + + /* Authorization URL */ + private static final ConfigKey AuthorizationURL = new ConfigKey("Authentication", String.class, "oauth2.url.authorization", "", + "URL for OAuth2 authentication if you use your own OAuth2 provider. Otherwise, leave it blank.", true, ConfigKey.Scope.Global); + + protected String getAuthorizationURL() { + return AuthorizationURL.value(); + } + + /* Logout URL */ + private static final ConfigKey LogoutURL = new ConfigKey("Authentication", String.class, "oauth2.url.logout", "", + "URL for logging out using OAuth2 if you use your own OAuth2 provider. Otherwise, leave it blank.", true, ConfigKey.Scope.Global); + + protected String getLogoutURL() { + return LogoutURL.value(); + } + + /* Token URL */ + private static final ConfigKey TokenURL = new ConfigKey("Authentication", String.class, "oauth2.url.token", "", + "URL for OAuth2 code validation if you use your own OAuth2 provider. Otherwise, leave it blank.", true, ConfigKey.Scope.Global); + + protected String getTokenURL() { + return TokenURL.value(); + } + + /* User Info URL */ + private static final ConfigKey UserInfoURL = new ConfigKey("Authentication", String.class, "oauth2.url.user", "", + "URL to retrieve user information if you use your own OAuth2 provider. Otherwise, leave it blank.", true, ConfigKey.Scope.Global); + + protected String getUserInfoURL() { + return UserInfoURL.value(); + } + + /* User Attribute */ + private static final ConfigKey UserAttribute = new ConfigKey("Authentication", String.class, "oauth2.user.attribute", "", + "Attribute to be used for user authentication if you use your own OAuth2 provider. Otherwise, leave it blank.", true, ConfigKey.Scope.Global); + + protected String getUserAttribute() { + return UserAttribute.value(); + } + + /* OAuth2 Domain */ + private static final ConfigKey UserDomain = new ConfigKey("Authentication", String.class, "oauth2.user.domain", "/", "Domain name of OAuth2 Users", true, + ConfigKey.Scope.Global); + + protected String getUserDomain() { + return UserDomain.value(); + } + + /* Create new accounts to new users */ + private static final ConfigKey NewAccountWhenNotFound = new ConfigKey("Authentication", Boolean.class, "oauth2.auto.create.new.account", "true", + "Automatic create new account when user is not found", true, ConfigKey.Scope.Global); + + protected boolean createsNewAccountWhenNotFound() { + return NewAccountWhenNotFound.value(); + } + + /* Implementation */ + public String generateAuthenticationUrl(String returnUrl) { + if (!isProviderEnabled()) { + return null; + } + try { + AuthenticationRequestBuilder builder; + if (getProviderType() != null) { + builder = OAuthClientRequest.authorizationProvider(getProviderType()); + } else { + builder = OAuthClientRequest.authorizationLocation(getAuthorizationURL()); + } + OAuthClientRequest request = builder.setClientId(getClientID()).setScope(getAccessScopeWithProvider()).setResponseType("code").setRedirectURI(returnUrl) + .buildQueryMessage(); + return request.getLocationUri(); + } catch (OAuthSystemException e) { + throw new CloudRuntimeException(e.getLocalizedMessage()); + } + } + + protected String changeCodeToAccessToken(String code, String redirectUri) { + try { + TokenRequestBuilder builder; + if (getProviderType() != null) { + builder = OAuthClientRequest.tokenProvider(getProviderType()); + } else { + builder = OAuthClientRequest.tokenLocation(getTokenURL()); + } + OAuthClientRequest request = builder.setClientId(getClientID()).setClientSecret(getClientSecret()).setGrantType(GrantType.AUTHORIZATION_CODE).setCode(code) + .setRedirectURI(redirectUri).setScope(getAccessScopeWithProvider()).buildBodyMessage(); + OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); + OAuthAccessTokenResponse tokenResponse; + if (getProviderType() == OAuthProviderType.GITHUB) { + tokenResponse = oAuthClient.accessToken(request, GitHubTokenResponse.class); + } else { + tokenResponse = oAuthClient.accessToken(request); + } + String accessToken = tokenResponse.getAccessToken(); + return accessToken; + } catch (OAuthSystemException e) { + throw new CloudRuntimeException(e.getLocalizedMessage(), e); + } catch (OAuthProblemException e) { + throw new CloudRuntimeException(e.getLocalizedMessage(), e); + } + } + + protected String requestUsernameFromUserInfoProviderAPI(String accessToken) { + try { + OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest(getUserInfoURLWithProvider()).setAccessToken(accessToken).buildHeaderMessage(); + + OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); + OAuthResourceResponse resourceResponse = oAuthClient.resource(bearerClientRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class); + if (resourceResponse.getResponseCode() != 200 || "application/json".equalsIgnoreCase(resourceResponse.getContentType())) { + throw new CloudRuntimeException("Error getting user info in " + getUserInfoURLWithProvider() + ". Http status code = " + resourceResponse.getResponseCode() + + " content type = " + resourceResponse.getContentType()); + } + Map json = JSONUtils.parseJSON(resourceResponse.getBody()); + return (String)json.get(getUserAttributeWithProvider()); + } catch (OAuthSystemException e) { + throw new CloudRuntimeException(e.getLocalizedMessage(), e); + } catch (OAuthProblemException e) { + throw new CloudRuntimeException(e.getLocalizedMessage(), e); + } + } + + protected Domain getUserDomainVO() { + Domain domain = _domainMgr.findDomainByPath(getUserDomain()); + return domain; + } + + public UserAccount authenticate(String code, String redirectUri) throws CloudAuthenticationException { + + String accessToken = changeCodeToAccessToken(code, redirectUri); + String username = requestUsernameFromUserInfoProviderAPI(accessToken); + if (username == null) { + throw new CloudRuntimeException("Can't get username from OAuth Server."); + } + + Domain domain = getUserDomainVO(); + UserAccount userAcc = _userAccDao.getUserAccount(username, domain.getId()); + if (userAcc == null) { + if (!createsNewAccountWhenNotFound()) { + s_logger.info("User " + username + " not found in domain " + domain.getId()); + throw new CloudAuthenticationException("User " + username + " not found. Contact the administrator."); + } + String email = username.contains("@") ? username : ""; + s_logger.info("Creating new user/account with username=" + username + ", email=" + email + " in domain " + domain.getId()); + userAcc = _accManager.createUserAccount(username, UUID.randomUUID().toString(), username, username, username, null, username, + Account.ACCOUNT_TYPE_NORMAL, null, domain.getId(), null, null, null, null); + // if there are any conflict of username/account name raises an error and administrator take care, to avoid security problems. + if (userAcc == null) { + throw new CloudAuthenticationException("Unable to create new account " + username + ". Contact the administrator."); + } + } + return userAcc; + } + + @Override + public boolean start() { + if (!isProviderEnabled()) { + s_logger.info("OAuth2 Authentication provider is not enabled"); + } else { + if (getProviderType() == null) { + s_logger.info("OAuth2 Authentication provider is using custom provider"); + } else { + s_logger.info("OAuth2 Authentication provider is using provider " + getProviderType()); + } + } + return true; + } + + public boolean isProviderEnabled() { + if ((getProviderType() != null || (StringUtils.isNotBlank(getAuthorizationURL()) && StringUtils.isNotBlank(getTokenURL())) && StringUtils.isNotBlank(getClientID()) + && StringUtils.isNotBlank(getClientSecret()))) { + // check domain is valid + if (getUserDomainVO() != null) { + return true; + } + } + return false; + } + + @Override + public List> getAuthCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(OAuth2LoginCmd.class); + cmdList.add(OAuth2RedirectCmd.class); + return cmdList; + } + + @Override + public String getConfigComponentName() { + return OAuth2ManagerImpl.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] {AuthorizationProvider, AuthorizationURL, TokenURL, LogoutURL, ClientSecret, ClientID, AccessScope, UserInfoURL, UserAttribute, UserDomain, + NewAccountWhenNotFound}; + } + + protected OAuthProviderType getProviderType() { + try { + if (StringUtils.isNotBlank(getAuthorizationProvider())) { + return OAuthProviderType.valueOf(getAuthorizationProvider()); + } + } catch (IllegalArgumentException e) { + s_logger.warn("Unknown authorization provider: " + getAuthorizationProvider()); + } + return null; + } + + protected String getAccessScopeWithProvider() { + if (StringUtils.isNotBlank(getAccessScope())) { + // If it's set, return whatever was set + return getAccessScope(); + } else { + // If it's blank, return according to provider + OAuthProviderType providerType = getProviderType(); + if (providerType == null) { + // Custom provider + return getAccessScope(); + } else { + switch (providerType) { + case GITHUB: + return defaultAccessScopeGitHub; + case GOOGLE: + return defaultAccessScopeGoogle; + default: + return getAccessScope(); + } + } + } + } + + protected String getUserInfoURLWithProvider() { + if (StringUtils.isNotBlank(getUserInfoURL())) { + // If it's set, return whatever was set + return getUserInfoURL(); + } else { + // If it's blank, return according to provider + OAuthProviderType providerType = getProviderType(); + if (providerType == null) { + // Custom provider + return getUserInfoURL(); + } else { + switch (providerType) { + case GITHUB: + return defaultUserInfoUrlGitHub; + case GOOGLE: + return defaultUserInfoUrlGoogle; + default: + return getUserInfoURL(); + } + } + } + } + + protected String getUserAttributeWithProvider() { + if (StringUtils.isNotBlank(getUserAttribute())) { + // If it's set, return whatever was set + return getUserAttribute(); + } else { + // If it's blank, return according to provider + OAuthProviderType providerType = getProviderType(); + if (providerType == null) { + // Custom provider + return getUserAttribute(); + } else { + switch (providerType) { + case GITHUB: + return defaultUserAttributeGitHub; + case GOOGLE: + return defaultUserAttributeGoogle; + default: + return getUserAttribute(); + } + } + } + } + + public String getLogoutUrlWithProvider() { + if (StringUtils.isNotBlank(getLogoutURL())) { + // If it's set, return whatever was set + return getLogoutURL(); + } else { + // If it's blank, return according to provider + OAuthProviderType providerType = getProviderType(); + if (providerType == null) { + // Custom provider + return getLogoutURL(); + } else { + switch (providerType) { + case GITHUB: + return defaultLogoutUrlGitHub; + case GOOGLE: + return defaultLogoutUrlGoogle; + default: + return getLogoutURL(); + } + } + } + } + +} diff --git a/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2LoginCmd.java b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2LoginCmd.java new file mode 100644 index 000000000000..6f116d9067e8 --- /dev/null +++ b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2LoginCmd.java @@ -0,0 +1,187 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.oauth2.api; + +import java.net.InetAddress; +import java.security.SecureRandom; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiServerService; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.auth.APIAuthenticationType; +import org.apache.cloudstack.api.auth.APIAuthenticator; +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; +import org.apache.cloudstack.oauth2.OAuth2Manager; +import org.apache.cloudstack.oauth2.response.OAuth2UrlResponse; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; + +import com.cloud.api.response.ApiResponseSerializer; +import com.cloud.domain.DomainVO; +import com.cloud.exception.CloudAuthenticationException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainManager; +import com.cloud.user.UserAccount; +import com.cloud.user.UserVO; + +@APICommand(name = "oAuth2Login", description = "Authenticates using OAuth2", responseObject = OAuth2UrlResponse.class, since = "4.5.0") +public class OAuth2LoginCmd extends BaseCmd implements APIAuthenticator { + public static final Logger s_logger = Logger.getLogger(OAuth2LoginCmd.class.getName()); + + private static final String s_name = "oauth2urlresponse"; + + @Inject + ApiServerService _apiServer; + + OAuth2Manager _oauth2Manager; + + @Inject + AccountManager _accountMgr; + + @Inject + DomainManager _domainMgr; + + @Override + public void execute() throws ServerApiException { + // We should never reach here + throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly"); + } + + protected ResponseObject loginUser(HttpSession session, UserAccount userAcct, Long domainId, String loginIpAddress, Map requestParameters) + throws CloudAuthenticationException { + if (userAcct != null) { + String timezone = userAcct.getTimezone(); + float offsetInHrs = 0f; + if (timezone != null) { + TimeZone t = TimeZone.getTimeZone(timezone); + s_logger.info("Current user logged in under " + timezone + " timezone"); + + java.util.Date date = new java.util.Date(); + long longDate = date.getTime(); + float offsetInMs = (t.getOffset(longDate)); + offsetInHrs = offsetInMs / (1000 * 60 * 60); + s_logger.info("Timezone offset from UTC is: " + offsetInHrs); + } + + Account account = _accountMgr.getAccount(userAcct.getAccountId()); + + // set the userId and account object for everyone + session.setAttribute("userid", userAcct.getId()); + UserVO user = (UserVO)_accountMgr.getActiveUser(userAcct.getId()); + if (user.getUuid() != null) { + session.setAttribute("user_UUID", user.getUuid()); + } + + session.setAttribute("username", userAcct.getUsername()); + session.setAttribute("firstname", userAcct.getFirstname()); + session.setAttribute("lastname", userAcct.getLastname()); + session.setAttribute("accountobj", account); + session.setAttribute("account", account.getAccountName()); + + session.setAttribute("domainid", account.getDomainId()); + DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId()); + if (domain.getUuid() != null) { + session.setAttribute("domain_UUID", domain.getUuid()); + } + + session.setAttribute("type", Short.valueOf(account.getType()).toString()); + session.setAttribute("registrationtoken", userAcct.getRegistrationToken()); + session.setAttribute("registered", new Boolean(userAcct.isRegistered()).toString()); + + if (timezone != null) { + session.setAttribute("timezone", timezone); + session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString()); + } + + // (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that + // to the login response so that session-based authenticators know to send the key back + SecureRandom sesssionKeyRandom = new SecureRandom(); + byte sessionKeyBytes[] = new byte[20]; + sesssionKeyRandom.nextBytes(sessionKeyBytes); + final String sessionKey = Base64.encodeBase64URLSafeString(sessionKeyBytes); + session.setAttribute(ApiConstants.SESSIONKEY, sessionKey); + + return _responseGenerator.createLoginResponse(session); + } + throw new CloudAuthenticationException("Failed to authenticate user " + (userAcct == null ? "(null)" : userAcct.getUsername()) + " in domain " + domainId + + "; please provide valid credentials"); + } + + @Override + public String authenticate(String command, Map params, + HttpSession session, InetAddress remoteAddress, String responseType, + StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { + // FIXME Put string constants in ApiConstants + if (params.containsKey("code") && params.containsKey("redirect_uri")) { + String code = String.valueOf(params.get("code")[0]); + String redirectUri = String.valueOf(params.get("redirect_uri")[0]); + try { + UserAccount userAcc = _oauth2Manager.authenticate(code, redirectUri); + if (_apiServer.verifyUser(userAcc.getId())) { + return ApiResponseSerializer.toSerializedString(loginUser(session, userAcc, userAcc.getDomainId(), remoteAddress.getHostAddress(), params), responseType); + } + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to authenticate or retrieve user while performing OAuth2 based SSO"); + } catch (final CloudAuthenticationException ex) { + s_logger.error("Unable to authenticate user using OAuth2 plugin: " + ex.getLocalizedMessage()); + throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), ex.getLocalizedMessage(), + params, responseType)); + } + } + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid call to OAuth2 Command"); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_TYPE_NORMAL; + } + + @Override + public APIAuthenticationType getAPIType() { + return APIAuthenticationType.LOGIN_API; + } + + @Override + public void setAuthenticators(List authenticators) { + for (PluggableAPIAuthenticator authManager : authenticators) { + if (authManager instanceof OAuth2Manager) { + _oauth2Manager = (OAuth2Manager)authManager; + } + } + if (_oauth2Manager == null) { + s_logger.error("No suitable Pluggable Authentication Manager found for OAuth2 Login Cmd"); + } + } + +} diff --git a/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2RedirectCmd.java b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2RedirectCmd.java new file mode 100644 index 000000000000..dc6bfaea34a9 --- /dev/null +++ b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/api/OAuth2RedirectCmd.java @@ -0,0 +1,113 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.oauth2.api; + +import java.net.InetAddress; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiServerService; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.auth.APIAuthenticationType; +import org.apache.cloudstack.api.auth.APIAuthenticator; +import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; +import org.apache.cloudstack.oauth2.OAuth2Manager; +import org.apache.cloudstack.oauth2.response.OAuth2UrlResponse; +import org.apache.log4j.Logger; + +import com.cloud.api.response.ApiResponseSerializer; +import com.cloud.user.Account; +import com.cloud.utils.HttpUtils; + +@APICommand(name = "oauthRedirect", description = "Return redirect url for OAuth2 SSO", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, responseObject = OAuth2UrlResponse.class, since = "4.5.0") +public class OAuth2RedirectCmd extends BaseCmd implements APIAuthenticator { + public static final Logger s_logger = Logger.getLogger(OAuth2RedirectCmd.class.getName()); + + private static final String s_name = "oauth2urlresponse"; + + @Inject + ApiServerService _apiServer; + + OAuth2Manager _oauth2Manager; + + @Parameter(name = "redirect_uri", type = CommandType.STRING, required = false, description = "URL the user is returned to") + private String redirectUri; + + @Override + public void execute() throws ServerApiException { + // We should never reach here + throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication api, cannot be used directly"); + } + + @Override + public String authenticate(String command, Map params, + HttpSession session, InetAddress remoteAddress, String responseType, + StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { + // build redirect url + Object[] redirectUriObj = params.get("redirect_uri"); + String redirectUri = null; + if (redirectUriObj != null && redirectUriObj.length > 0) { + redirectUri = (String)redirectUriObj[0]; + } + String url = _oauth2Manager.generateAuthenticationUrl(redirectUri); + String logoutUrl = _oauth2Manager.getLogoutUrlWithProvider(); + OAuth2UrlResponse response = new OAuth2UrlResponse(); + response.setRedirectUri(url); + response.setLogoutUri(logoutUrl); + response.setObjectName("authenticationurl"); + response.setResponseName(getCommandName()); + return ApiResponseSerializer.toSerializedString(response, HttpUtils.RESPONSE_TYPE_JSON); + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_TYPE_NORMAL; + } + + + @Override + public APIAuthenticationType getAPIType() { + return APIAuthenticationType.LOGIN_API; + } + + @Override + public void setAuthenticators(List authenticators) { + for (PluggableAPIAuthenticator authManager : authenticators) { + if (authManager instanceof OAuth2Manager) { + _oauth2Manager = (OAuth2Manager)authManager; + } + } + if (_oauth2Manager == null) { + s_logger.error("No suitable Pluggable Authentication Manager found for OAuth2 Login Cmd"); + } + } + +} diff --git a/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/response/OAuth2UrlResponse.java b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/response/OAuth2UrlResponse.java new file mode 100644 index 000000000000..3053d72aa260 --- /dev/null +++ b/plugins/user-authenticators/oauth2/src/org/apache/cloudstack/oauth2/response/OAuth2UrlResponse.java @@ -0,0 +1,42 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package org.apache.cloudstack.oauth2.response; + +import org.apache.cloudstack.api.BaseResponse; + +public class OAuth2UrlResponse extends BaseResponse { + + private String redirectUri; + + private String logoutUri; + + public String getRedirectUri() { + return redirectUri; + } + + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + public String getLogoutUri() { + return logoutUri; + } + + public void setLogoutUri(String logoutUri) { + this.logoutUri = logoutUri; + } +} diff --git a/plugins/user-authenticators/oauth2/test/org/apache/cloudstack/oauth2/OAuth2ManagerImplTest.java b/plugins/user-authenticators/oauth2/test/org/apache/cloudstack/oauth2/OAuth2ManagerImplTest.java new file mode 100644 index 000000000000..6f2aac7b8164 --- /dev/null +++ b/plugins/user-authenticators/oauth2/test/org/apache/cloudstack/oauth2/OAuth2ManagerImplTest.java @@ -0,0 +1,106 @@ +package org.apache.cloudstack.oauth2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.URLEncoder; + +import javax.inject.Inject; + +import org.apache.cloudstack.test.utils.SpringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.cloud.domain.DomainVO; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainManager; +import com.cloud.user.dao.UserAccountDao; +import com.cloud.utils.component.ComponentContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) +public class OAuth2ManagerImplTest { + + @Inject + private OAuth2ManagerImpl _oAuth2Auth; + + private final String AUTHLOCATION = "https://www.example.com/oauth/authorize"; + private final String CLIENTID = "djAjpUQ53oXGY1SiUGFM"; + private final String CLIENTSECRET = "jaskm9AKnmadsiFNUFNAlnk15kn"; + private final String TOKENURL = "https://www.example.com/oauth/token"; + + @Before + public void setUp() throws Exception { + ComponentContext.initComponentsLifeCycle(); + _oAuth2Auth = spy(_oAuth2Auth); + + when(_oAuth2Auth.getAuthorizationURL()).thenReturn(AUTHLOCATION); + when(_oAuth2Auth.getClientID()).thenReturn(CLIENTID); + when(_oAuth2Auth.getClientSecret()).thenReturn(CLIENTSECRET); + when(_oAuth2Auth.getTokenURL()).thenReturn(TOKENURL); + when(_oAuth2Auth.getUserDomainVO()).thenReturn(new DomainVO()); + } + + @After + public void testDown() throws Exception { + } + + @Test + public void testGenerateAuthenticateUrl() throws Exception { + String returnUrl = "http://localhost:8080/client"; + String encodedUrl = URLEncoder.encode(returnUrl, "UTF-8"); + + String authUrl = _oAuth2Auth.generateAuthenticationUrl(returnUrl); + assertNotNull(authUrl); + assertEquals(AUTHLOCATION + "?response_type=code&redirect_uri=" + encodedUrl + "&client_id=" + CLIENTID, authUrl); + } + + @Configuration + @ComponentScan(basePackageClasses = {OAuth2ManagerImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + @Bean + public UserAccountDao userAccountDao() { + return mock(UserAccountDao.class); + } + + @Bean + public DomainManager domainManager() { + return mock(DomainManager.class); + } + + @Bean + public AccountManager accountManager() { + return mock(AccountManager.class); + } + + public static class Library implements TypeFilter { + + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + } +} diff --git a/scripts/vm/hypervisor/xenserver/perfmon.py b/scripts/vm/hypervisor/xenserver/perfmon.py index eb14e32aa545..b2479a6c89c2 100755 --- a/scripts/vm/hypervisor/xenserver/perfmon.py +++ b/scripts/vm/hypervisor/xenserver/perfmon.py @@ -65,15 +65,6 @@ def __init__(self): def get_nrows(self): return self.rows - def get_vm_list(self): - return self.vm_reports.keys() - - def get_vm_param_list(self, uuid): - report = self.vm_reports[uuid] - if not report: - return [] - return report.keys() - def get_total_cpu_core(self, uuid): report = self.vm_reports[uuid] if not report: @@ -87,46 +78,22 @@ def get_total_cpu_core(self, uuid): return result def get_vm_data(self, uuid, param, row): - #pp = pprint.PrettyPrinter(indent=4) - #pp.pprint(self.vm_reports) - report = self.vm_reports[uuid] - col = report[param] - return self.__lookup_data(col, row) - - def get_host_uuid(self): - report = self.host_report - if not report: - return None - return report.uuid - - def get_host_param_list(self): - report = self.host_report - if not report: - return [] - return report.keys() - - def get_host_data(self, param, row): - report = self.host_report - col = report[param] - return self.__lookup_data(col, row) - - def get_row_time(self, row): - return self.__lookup_timestamp(row) + #pp = pprint.PrettyPrinter(indent=4) + #pp.pprint(self.vm_reports) + for hostIndex in xrange(0, self.hostCount): + if uuid not in self.vm_reports[hostIndex]: + continue + report = self.vm_reports[hostIndex][uuid] + col = report[param] + return self.__lookup_data(col, row, hostIndex) # extract float from value () node by col,row - def __lookup_data(self, col, row): + def __lookup_data(self, col, row, hostIndex): # Note: the nodes are in reverse chronological order, and comprise # a timestamp node, followed by self.columns data nodes - node = self.data_node.childNodes[self.rows - 1 - row].childNodes[col + 1] + node = self.data_node[hostIndex].childNodes[self.rows - 1 - row].childNodes[col + 1] return float(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE - # extract int from value () node by row - def __lookup_timestamp(self, row): - # Note: the nodes are in reverse chronological order, and comprise - # a timestamp node, followed by self.columns data nodes - node = self.data_node.childNodes[self.rows - 1 - row].childNodes[0] - return int(node.firstChild.toxml()) # node.firstChild should have nodeType TEXT_NODE - def refresh(self, login, starttime, session, override_params): self.params['start'] = starttime params = override_params @@ -135,54 +102,61 @@ def refresh(self, login, starttime, session, override_params): paramstr = "&".join(["%s=%s" % (k, params[k]) for k in params]) # this is better than urllib.urlopen() as it raises an Exception on http 401 'Unauthorised' error # rather than drop into interactive mode - for host in login.host.get_all(): - #print "http://" + str(login.host.get_address(host)) + "/rrd_updates?%s" % paramstr - sock = urllib.URLopener().open("http://" + str(login.host.get_address(host)) + "/rrd_updates?%s" % paramstr) - xmlsource = sock.read() - sock.close() - xmldoc = minidom.parseString(xmlsource) - self.__parse_xmldoc(xmldoc) - # Update the time used on the next run - self.params['start'] = self.end_time + 1 # avoid retrieving same data twice - - def __parse_xmldoc(self, xmldoc): + self.hostCount = 0 + for host in login.host.get_all(): + #print "http://" + str(login.host.get_address(host)) + "/rrd_updates?%s" % paramstr + sock = urllib.URLopener().open("http://" + str(login.host.get_address(host)) + "/rrd_updates?%s" % paramstr) + xmlsource = sock.read() + sock.close() + xmldoc = minidom.parseString(xmlsource) + self.__parse_xmldoc(xmldoc, self.hostCount) + # Update the time used on the next run + self.params['start'] = self.end_time + 1 # avoid retrieving same data twice + self.hostCount += 1 + + def __parse_xmldoc(self, xmldoc, hostIndex): # The 1st node contains meta data (description of the data) # The 2nd node contains the data - self.meta_node = xmldoc.firstChild.childNodes[0] - self.data_node = xmldoc.firstChild.childNodes[1] - - def lookup_metadata_bytag(name): - return int(self.meta_node.getElementsByTagName(name)[0].firstChild.toxml()) + if not hasattr(self,'meta_node'): + self.meta_node = {} + self.meta_node[hostIndex] = xmldoc.firstChild.childNodes[0] + if not hasattr(self,'data_node'): + self.data_node = {} + self.data_node[hostIndex] = xmldoc.firstChild.childNodes[1] + + def lookup_metadata_bytag(name, hostIndex): + return int(self.meta_node[hostIndex].getElementsByTagName(name)[0].firstChild.toxml()) # rows = number of samples per variable # columns = number of variables - self.rows = lookup_metadata_bytag('rows') - self.columns = lookup_metadata_bytag('columns') + self.rows = lookup_metadata_bytag('rows', hostIndex) + self.columns = lookup_metadata_bytag('columns', hostIndex) # These indicate the period covered by the data - self.start_time = lookup_metadata_bytag('start') - self.step_time = lookup_metadata_bytag('step') - self.end_time = lookup_metadata_bytag('end') + self.start_time = lookup_metadata_bytag('start', hostIndex) + self.step_time = lookup_metadata_bytag('step', hostIndex) + self.end_time = lookup_metadata_bytag('end', hostIndex) # the Node describes the variables - self.legend = self.meta_node.getElementsByTagName('legend')[0] + self.legend = self.meta_node[hostIndex].getElementsByTagName('legend')[0] # vm_reports matches uuid to per VM report if not hasattr(self,'vm_reports'): - self.vm_reports = {} + self.vm_reports = {} + self.vm_reports[hostIndex] = {} # There is just one host_report and its uuid should not change! self.host_report = None # Handle each column. (I.e. each variable) for col in range(self.columns): - self.__handle_col(col) + self.__handle_col(col, hostIndex) - def __handle_col(self, col): + def __handle_col(self, col, hostIndex): # work out how to interpret col from the legend col_meta_data = self.legend.childNodes[col].firstChild.toxml() # vm_or_host will be 'vm' or 'host'. Note that the Control domain counts as a VM! (cf, vm_or_host, uuid, param) = col_meta_data.split(':') if vm_or_host == 'vm': # Create a report for this VM if it doesn't exist - if not uuid in self.vm_reports: - self.vm_reports[uuid] = VMReport(uuid) + if not uuid in self.vm_reports[hostIndex]: + self.vm_reports[hostIndex][uuid] = VMReport(uuid) # Update the VMReport with the col data and meta data - vm_report = self.vm_reports[uuid] + vm_report = self.vm_reports[hostIndex][uuid] vm_report[param] = col elif vm_or_host == 'host': # Create a report for the host if it doesn't exist @@ -198,9 +172,15 @@ def __handle_col(self, col): def getuuid(vm_name): status, output = commands.getstatusoutput("xe vm-list | grep "+vm_name+" -B 1 | head -n 1 | awk -F':' '{print $2}' | tr -d ' '") if (status != 0): - raise PerfMonException("Invalid vm name: %s" % vm_name) + raise PerfMonException("Invalid vm name: %s" % vm_name) return output +def get_vm_cpu_number(vm_uuid): + status, output = commands.getstatusoutput("xe vm-list params=VCPUs-number uuid=" + vm_uuid + " | head -n 1 | awk -F':' '{print $2}' | tr -d ' '") + if (status != 0): + raise PerfMonException("Invalid vm uuid: %s" % vm_uuid) + return int(output) + def get_vm_group_perfmon(args={}): login = XenAPI.xapi_local() login.login_with_password("","") @@ -228,19 +208,19 @@ def get_vm_group_perfmon(args={}): #for uuid in rrd_updates.get_vm_list(): for vm_count in xrange(1, total_vm + 1): - vm_name = args['vmname' + str(vm_count)] + vm_name = args['vmname' + str(vm_count)] vm_uuid = getuuid(vm_name) #print "Got values for VM: " + str(vm_count) + " " + vm_uuid for counter_count in xrange(1, total_counter + 1): - #refresh average - average_cpu = 0 - average_memory = 0 + #refresh average + average_cpu = 0 + average_memory = 0 counter = args['counter' + str(counter_count)] total_row = rrd_updates.get_nrows() duration = int(args['duration' + str(counter_count)]) / 60 duration_diff = total_row - duration if counter == "cpu": - total_cpu = rrd_updates.get_total_cpu_core(vm_uuid) + total_cpu = get_vm_cpu_number(vm_uuid) for row in xrange(duration_diff, total_row): for cpu in xrange(0, total_cpu): average_cpu += rrd_updates.get_vm_data(vm_uuid, "cpu" + str(cpu), row) @@ -257,5 +237,4 @@ def get_vm_group_perfmon(args={}): result += str(vm_count) + '.' + str(counter_count) + ':' + str(average_memory) else: result += ',' + str(vm_count) + '.' + str(counter_count) + ':' + str(average_memory) - return result - + return result \ No newline at end of file diff --git a/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_from_secondarystorage.sh b/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_from_secondarystorage.sh new file mode 100755 index 000000000000..a4e977bdd59d --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_from_secondarystorage.sh @@ -0,0 +1,188 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#set -x + +usage() { + printf "Usage: %s [vhd file in secondary storage] [uuid of the source sr] [name label] \n" $(basename $0) +} + +cleanup() +{ + if [ ! -z $localmp ]; then + umount -fl $localmp + if [ $? -eq 0 ]; then + rmdir $localmp + fi + fi +} + +if [ -z $1 ]; then + usage + echo "2#no mountpoint" + exit 0 +else + mountpoint=${1%/*} + vhdfilename=${1##*/} +fi + +if [ -z $2 ]; then + usage + echo "3#no uuid of the source sr" + exit 0 +else + sruuid=$2 +fi + +type=$(xe sr-param-get uuid=$sruuid param-name=type) +if [ $? -ne 0 ]; then + echo "4#sr $sruuid doesn't exist" + exit 0 +fi + +if [ -z $3 ]; then + usage + echo "3#no namelabel" + exit 0 +else + namelabel=$3 +fi + +localmp=/var/run/cloud_mount/$(uuidgen -r) + +mkdir -p $localmp +if [ $? -ne 0 ]; then + echo "5#can't make dir $localmp" + exit 0 +fi + +mount -o tcp,soft,ro,timeo=133,retrans=1 $mountpoint $localmp +if [ $? -ne 0 ]; then + echo "6#can't mount $mountpoint to $localmp" + exit 0 +fi + +vhdfile=$localmp/$vhdfilename +if [ ${vhdfile%.vhd} == ${vhdfile} ] ; then + vhdfile=$(ls $vhdfile/*.vhd) + if [ $? -ne 0 ]; then + echo "7#There is no vhd file under $mountpoint" + cleanup + exit 0 + fi +fi + + + +VHDUTIL="/usr/bin/vhd-util" + +copyvhd() +{ + local desvhd=$1 + local srcvhd=$2 + local vsize=$3 + local type=$4 + local parent=`$VHDUTIL query -p -n $srcvhd` + if [ $? -ne 0 ]; then + echo "30#failed to query $srcvhd" + cleanup + exit 0 + fi + if [ "${parent##*vhd has}" = " no parent" ]; then + dd if=$srcvhd of=$desvhd bs=2M + if [ $? -ne 0 ]; then + echo "31#failed to dd $srcvhd to $desvhd" + cleanup + exit 0 + fi + if [ $type != "nfs" -a $type != "ext" -a $type != "file" ]; then + dd if=$srcvhd of=$desvhd bs=512 seek=$(($(($vsize/512))-1)) count=1 + $VHDUTIL modify -s $vsize -n $desvhd + if [ $? -ne 0 ]; then + echo "32#failed to set new vhd physical size for vdi vdi $uuid" + cleanup + exit 0 + fi + fi + else + copyvhd $desvhd $parent $vsize $type + $VHDUTIL coalesce -p $desvhd -n $srcvhd + if [ $? -ne 0 ]; then + echo "32#failed to coalesce $desvhd to $srcvhd" + cleanup + exit 0 + fi + fi +} + +size=$($VHDUTIL query -v -n $vhdfile) +uuid=$(xe vdi-create sr-uuid=$sruuid virtual-size=${size}MiB type=user name-label=$namelabel) +if [ $? -ne 0 ]; then + echo "9#can not create vdi in sr $sruuid" + cleanup + exit 0 +fi + + +if [ $type == "nfs" -o $type == "ext" ]; then + desvhd=/run/sr-mount/$sruuid/$uuid.vhd + copyvhd $desvhd $vhdfile 0 $type + +elif [ $type == "lvmoiscsi" -o $type == "lvm" -o $type == "lvmohba" ]; then + lvsize=$(xe vdi-param-get uuid=$uuid param-name=physical-utilisation) + if [ $? -ne 0 ]; then + echo "12#failed to get physical size of vdi $uuid" + cleanup + exit 0 + fi + desvhd=/dev/VG_XenStorage-$sruuid/VHD-$uuid + lvchange -ay $desvhd + if [ $? -ne 0 ]; then + echo "10#lvm can not make VDI $uuid visible" + cleanup + exit 0 + fi + copyvhd $desvhd $vhdfile $lvsize $type +elif [ $type == "file" ]; then + pbd=`xe sr-param-list uuid=$sruuid |grep PBDs | awk '{print $3}'` + path=`xe pbd-param-list uuid=$pbd |grep device-config |awk '{print $4}'` + desvhd=$path/$uuid.vhd + copyvhd $desvhd $vhdfile 0 $type + +else + echo "15#doesn't support sr type $type" + cleanup + exit 0 +fi + +$VHDUTIL set -n $desvhd -f "hidden" -v "0" > /dev/null +if [ $? -ne 0 ]; then + echo "21#failed to set hidden to 0 $desvhd" + cleanup + exit 0 +fi +xe sr-scan uuid=$sruuid +if [ $? -ne 0 ]; then + echo "14#failed to scan sr $sruuid" + cleanup + exit 0 +fi + +echo "0#$uuid" +cleanup +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_to_secondarystorage.sh b/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_to_secondarystorage.sh new file mode 100755 index 000000000000..b315c07b3562 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xcposs/copy_vhd_to_secondarystorage.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#set -x + +usage() { + printf "Usage: %s [mountpoint in secondary storage] [uuid of the source vdi] [uuid of the source sr]\n" $(basename $0) +} + +cleanup() +{ + if [ ! -z $localmp ]; then + umount $localmp + if [ $? -eq 0 ]; then + rmdir $localmp + fi + fi +} + +if [ -z $1 ]; then + usage + echo "1#no mountpoint" + exit 0 +else + mountpoint=$1 +fi + +if [ -z $2 ]; then + usage + echo "2#no uuid of the source sr" + exit 0 +else + vdiuuid=$2 +fi + + +if [ -z $3 ]; then + usage + echo "3#no uuid of the source sr" + exit 0 +else + sruuid=$3 +fi + +type=$(xe sr-param-get uuid=$sruuid param-name=type) +if [ $? -ne 0 ]; then + echo "4#sr $sruuid doesn't exist" + exit 0 +fi + +localmp=/var/run/cloud_mount/$(uuidgen -r) + +mkdir -p $localmp +if [ $? -ne 0 ]; then + echo "5#can't make dir $localmp" + exit 0 +fi + +mount -o tcp,soft,timeo=133,retrans=1 $mountpoint $localmp +if [ $? -ne 0 ]; then + echo "6#can't mount $mountpoint to $localmp" + exit 0 +fi + +vhdfile=$localmp/${vdiuuid}.vhd + +if [ $type == "nfs" -o $type == "ext" ]; then + dd if=/var/run/sr-mount/$sruuid/${vdiuuid}.vhd of=$vhdfile bs=2M + if [ $? -ne 0 ]; then + rm -f $vhdfile + echo "8#failed to copy /var/run/sr-mount/$sruuid/${vdiuuid}.vhd to secondarystorage" + cleanup + exit 0 + fi +elif [ $type == "lvmoiscsi" -o $type == "lvm" -o $type == "lvmohba" ]; then + lvchange -ay /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid + if [ $? -ne 0 ]; then + echo "9#lvm can not make VDI $vdiuuid visible" + cleanup + exit 0 + fi + size=$(vhd-util query -s -n /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid) + if [ $? -ne 0 ]; then + echo "10#can not get physical size of /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid" + cleanup + exit 0 + fi +#in 2M unit + size=$((size>>21)) + size=$((size+1)) + dd if=/dev/VG_XenStorage-$sruuid/VHD-$vdiuuid of=$vhdfile bs=2M count=$size + if [ $? -ne 0 ]; then + rm -f $vhdfile + echo "8#failed to copy /dev/VG_XenStorage-$sruuid/VHD-$vdiuuid to secondarystorage" + cleanup + exit 0 + fi +#in byte unit + size=$((size<<21)) + vhd-util modify -s $size -n $vhdfile + if [ $? -ne 0 ]; then + rm -f $vhdfile + echo "11#failed to change $vhdfile physical size" + cleanup + exit 0 + fi +else + echo "15#doesn't support sr type $type" + cleanup + exit 0 +fi + +echo "0#$vdiuuid" +cleanup +exit 0 diff --git a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot new file mode 100644 index 000000000000..53f31a99eed4 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot @@ -0,0 +1,601 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Version @VERSION@ +# +# A plugin for executing script needed by vmops cloud + +import os, sys, time +import XenAPIPlugin +sys.path.append("/usr/lib/xcp/sm/") +import SR, VDI, SRCommand, util, lvutil +from util import CommandException +import vhdutil +import shutil +import lvhdutil +import errno +import subprocess +import xs_errors +import cleanup +import stat +import random + +VHD_UTIL = 'vhd-util' +VHD_PREFIX = 'VHD-' +CLOUD_DIR = '/run/cloud_mount' + +def echo(fn): + def wrapped(*v, **k): + name = fn.__name__ + util.SMlog("#### VMOPS enter %s ####" % name ) + res = fn(*v, **k) + util.SMlog("#### VMOPS exit %s ####" % name ) + return res + return wrapped + + +@echo +def create_secondary_storage_folder(session, args): + local_mount_path = None + + util.SMlog("create_secondary_storage_folder, args: " + str(args)) + + try: + try: + # Mount the remote resource folder locally + remote_mount_path = args["remoteMountPath"] + local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid()) + mount(remote_mount_path, local_mount_path) + + # Create the new folder + new_folder = local_mount_path + "/" + args["newFolder"] + if not os.path.isdir(new_folder): + current_umask = os.umask(0) + os.makedirs(new_folder) + os.umask(current_umask) + except OSError, (errno, strerror): + errMsg = "create_secondary_storage_folder failed: errno: " + str(errno) + ", strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + except: + errMsg = "create_secondary_storage_folder failed." + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + finally: + if local_mount_path != None: + # Unmount the local folder + umount(local_mount_path) + # Remove the local folder + os.system("rmdir " + local_mount_path) + + return "1" + +@echo +def delete_secondary_storage_folder(session, args): + local_mount_path = None + + util.SMlog("delete_secondary_storage_folder, args: " + str(args)) + + try: + try: + # Mount the remote resource folder locally + remote_mount_path = args["remoteMountPath"] + local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid()) + mount(remote_mount_path, local_mount_path) + + # Delete the specified folder + folder = local_mount_path + "/" + args["folder"] + if os.path.isdir(folder): + os.system("rm -f " + folder + "/*") + os.system("rmdir " + folder) + except OSError, (errno, strerror): + errMsg = "delete_secondary_storage_folder failed: errno: " + str(errno) + ", strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + except: + errMsg = "delete_secondary_storage_folder failed." + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + finally: + if local_mount_path != None: + # Unmount the local folder + umount(local_mount_path) + # Remove the local folder + os.system("rmdir " + local_mount_path) + + return "1" + +@echo +def post_create_private_template(session, args): + local_mount_path = None + try: + try: + # get local template folder + templatePath = args["templatePath"] + local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid()) + mount(templatePath, local_mount_path) + # Retrieve args + filename = args["templateFilename"] + name = args["templateName"] + description = args["templateDescription"] + checksum = args["checksum"] + file_size = args["size"] + virtual_size = args["virtualSize"] + template_id = args["templateId"] + + # Create the template.properties file + template_properties_install_path = local_mount_path + "/template.properties" + f = open(template_properties_install_path, "w") + f.write("filename=" + filename + "\n") + f.write("vhd=true\n") + f.write("id=" + template_id + "\n") + f.write("vhd.filename=" + filename + "\n") + f.write("public=false\n") + f.write("uniquename=" + name + "\n") + f.write("vhd.virtualsize=" + virtual_size + "\n") + f.write("virtualsize=" + virtual_size + "\n") + f.write("checksum=" + checksum + "\n") + f.write("hvm=true\n") + f.write("description=" + description + "\n") + f.write("vhd.size=" + str(file_size) + "\n") + f.write("size=" + str(file_size) + "\n") + f.close() + util.SMlog("Created template.properties file") + + # Set permissions + permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH + os.chmod(template_properties_install_path, permissions) + util.SMlog("Set permissions on template and template.properties") + + except: + errMsg = "post_create_private_template failed." + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + finally: + if local_mount_path != None: + # Unmount the local folder + umount(local_mount_path) + # Remove the local folder + os.system("rmdir " + local_mount_path) + return "1" + +def isfile(path, isISCSI): + errMsg = '' + exists = True + if isISCSI: + exists = checkVolumeAvailablility(path) + else: + exists = os.path.isfile(path) + + if not exists: + errMsg = "File " + path + " does not exist." + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return errMsg + +def copyfile(fromFile, toFile, isISCSI): + util.SMlog("Starting to copy " + fromFile + " to " + toFile) + errMsg = '' + try: + cmd = ['dd', 'if=' + fromFile, 'of=' + toFile, 'bs=4M'] + txt = util.pread2(cmd) + except: + try: + os.system("rm -f " + toFile) + except: + txt = '' + txt = '' + errMsg = "Error while copying " + fromFile + " to " + toFile + " in secondary storage" + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + util.SMlog("Successfully copied " + fromFile + " to " + toFile) + return errMsg + +def chdir(path): + try: + os.chdir(path) + except OSError, (errno, strerror): + errMsg = "Unable to chdir to " + path + " because of OSError with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + util.SMlog("Chdired to " + path) + return + +def scanParent(path): + # Do a scan for the parent for ISCSI volumes + # Note that the parent need not be visible on the XenServer + parentUUID = '' + try: + lvName = os.path.basename(path) + dirname = os.path.dirname(path) + vgName = os.path.basename(dirname) + vhdInfo = vhdutil.getVHDInfoLVM(lvName, lvhdutil.extractUuid, vgName) + parentUUID = vhdInfo.parentUuid + except: + errMsg = "Could not get vhd parent of " + path + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return parentUUID + +def getParent(path, isISCSI): + parentUUID = '' + try : + if isISCSI: + parentUUID = vhdutil.getParent(path, lvhdutil.extractUuid) + else: + parentUUID = vhdutil.getParent(path, cleanup.FileVDI.extractUuid) + except: + errMsg = "Could not get vhd parent of " + path + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return parentUUID + +def getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI): + snapshotVHD = getVHD(snapshotUuid, isISCSI) + snapshotPath = os.path.join(primarySRPath, snapshotVHD) + + baseCopyUuid = '' + if isISCSI: + checkVolumeAvailablility(snapshotPath) + baseCopyUuid = scanParent(snapshotPath) + else: + baseCopyUuid = getParent(snapshotPath, isISCSI) + + util.SMlog("Base copy of snapshotUuid: " + snapshotUuid + " is " + baseCopyUuid) + return baseCopyUuid + +def setParent(parent, child): + try: + cmd = [VHD_UTIL, "modify", "-p", parent, "-n", child] + txt = util.pread2(cmd) + except: + errMsg = "Unexpected error while trying to set parent of " + child + " to " + parent + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + util.SMlog("Successfully set parent of " + child + " to " + parent) + return + +def rename(originalVHD, newVHD): + try: + os.rename(originalVHD, newVHD) + except OSError, (errno, strerror): + errMsg = "OSError while renaming " + origiinalVHD + " to " + newVHD + "with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return + +def makedirs(path): + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError, (errno, strerror): + umount(path) + if os.path.isdir(path): + return + errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return + +def mount(remoteDir, localDir): + makedirs(localDir) + options = "soft,tcp,timeo=133,retrans=1" + try: + cmd = ['mount', '-o', options, remoteDir, localDir] + txt = util.pread2(cmd) + except: + txt = '' + errMsg = "Unexpected error while trying to mount " + remoteDir + " to " + localDir + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + util.SMlog("Successfully mounted " + remoteDir + " to " + localDir) + + return + +def umount(localDir): + try: + cmd = ['umount', localDir] + util.pread2(cmd) + except CommandException: + errMsg = "CommandException raised while trying to umount " + localDir + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + util.SMlog("Successfully unmounted " + localDir) + return + +def mountSnapshotsDir(secondaryStorageMountPath, localMountPointPath, path): + # The aim is to mount secondaryStorageMountPath on + # And create / dir on it, if it doesn't exist already. + # Assuming that secondaryStorageMountPath exists remotely + + # Just mount secondaryStorageMountPath//SecondaryStorageHost/ everytime + # Never unmount. + # path is like "snapshots/account/volumeId", we mount secondary_storage:/snapshots + relativeDir = path.split("/")[0] + restDir = "/".join(path.split("/")[1:]) + snapshotsDir = os.path.join(secondaryStorageMountPath, relativeDir) + + makedirs(localMountPointPath) + # if something is not mounted already on localMountPointPath, + # mount secondaryStorageMountPath on localMountPath + if os.path.ismount(localMountPointPath): + # There is more than one secondary storage per zone. + # And we are mounting each sec storage under a zone-specific directory + # So two secondary storage snapshot dirs will never get mounted on the same point on the same XenServer. + util.SMlog("The remote snapshots directory has already been mounted on " + localMountPointPath) + else: + mount(snapshotsDir, localMountPointPath) + + # Create accountId/instanceId dir on localMountPointPath, if it doesn't exist + backupsDir = os.path.join(localMountPointPath, restDir) + makedirs(backupsDir) + return backupsDir + +def unmountAll(path): + try: + for dir in os.listdir(path): + if dir.isdigit(): + util.SMlog("Unmounting Sub-Directory: " + dir) + localMountPointPath = os.path.join(path, dir) + umount(localMountPointPath) + except: + util.SMlog("Ignoring the error while trying to unmount the snapshots dir") + +@echo +def unmountSnapshotsDir(session, args): + dcId = args['dcId'] + localMountPointPath = os.path.join(CLOUD_DIR, dcId) + localMountPointPath = os.path.join(localMountPointPath, "snapshots") + unmountAll(localMountPointPath) + try: + umount(localMountPointPath) + except: + util.SMlog("Ignoring the error while trying to unmount the snapshots dir.") + + return "1" + +def getPrimarySRPath(session, primaryStorageSRUuid, isISCSI): + sr = session.xenapi.SR.get_by_uuid(primaryStorageSRUuid) + srrec = session.xenapi.SR.get_record(sr) + srtype = srrec["type"] + if srtype == "file": + pbd = session.xenapi.SR.get_PBDs(sr)[0] + pbdrec = session.xenapi.PBD.get_record(pbd) + primarySRPath = pbdrec["device_config"]["location"] + return primarySRPath + elif isISCSI: + primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid + return os.path.join(lvhdutil.VG_LOCATION, primarySRDir) + else: + return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid) + +def getBackupVHD(UUID): + return UUID + '.' + SR.DEFAULT_TAP + +def getVHD(UUID, isISCSI): + if isISCSI: + return VHD_PREFIX + UUID + else: + return UUID + '.' + SR.DEFAULT_TAP + +def getIsTrueString(stringValue): + booleanValue = False + if (stringValue and stringValue == 'true'): + booleanValue = True + return booleanValue + +def makeUnavailable(uuid, primarySRPath, isISCSI): + if not isISCSI: + return + VHD = getVHD(uuid, isISCSI) + path = os.path.join(primarySRPath, VHD) + manageAvailability(path, '-an') + return + +def manageAvailability(path, value): + if path.__contains__("/var/run/sr-mount"): + return + util.SMlog("Setting availability of " + path + " to " + value) + try: + cmd = ['/usr/sbin/lvchange', value, path] + util.pread2(cmd) + except: #CommandException, (rc, cmdListStr, stderr): + #errMsg = "CommandException thrown while executing: " + cmdListStr + " with return code: " + str(rc) + " and stderr: " + stderr + errMsg = "Unexpected exception thrown by lvchange" + util.SMlog(errMsg) + if value == "-ay": + # Raise an error only if we are trying to make it available. + # Just warn if we are trying to make it unavailable after the + # snapshot operation is done. + raise xs_errors.XenError(errMsg) + return + + +def checkVolumeAvailablility(path): + try: + if not isVolumeAvailable(path): + # The VHD file is not available on XenSever. The volume is probably + # inactive or detached. + # Do lvchange -ay to make it available on XenServer + manageAvailability(path, '-ay') + except: + errMsg = "Could not determine status of ISCSI path: " + path + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + success = False + i = 0 + while i < 6: + i = i + 1 + # Check if the vhd is actually visible by checking for the link + # set isISCSI to true + success = isVolumeAvailable(path) + if success: + util.SMlog("Made vhd: " + path + " available and confirmed that it is visible") + break + + # Sleep for 10 seconds before checking again. + time.sleep(10) + + # If not visible within 1 min fail + if not success: + util.SMlog("Could not make vhd: " + path + " available despite waiting for 1 minute. Does it exist?") + + return success + +def isVolumeAvailable(path): + # Check if iscsi volume is available on this XenServer. + status = "0" + try: + p = subprocess.Popen(["/bin/bash", "-c", "if [ -L " + path + " ]; then echo 1; else echo 0;fi"], stdout=subprocess.PIPE) + status = p.communicate()[0].strip("\n") + except: + errMsg = "Could not determine status of ISCSI path: " + path + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + return (status == "1") + +def getVhdParent(session, args): + util.SMlog("getParent with " + str(args)) + primaryStorageSRUuid = args['primaryStorageSRUuid'] + snapshotUuid = args['snapshotUuid'] + isISCSI = getIsTrueString(args['isISCSI']) + + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) + util.SMlog("primarySRPath: " + primarySRPath) + + baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) + + return baseCopyUuid + + +def backupSnapshot(session, args): + util.SMlog("Called backupSnapshot with " + str(args)) + primaryStorageSRUuid = args['primaryStorageSRUuid'] + secondaryStorageMountPath = args['secondaryStorageMountPath'] + snapshotUuid = args['snapshotUuid'] + prevBackupUuid = args['prevBackupUuid'] + backupUuid = args['backupUuid'] + isISCSI = getIsTrueString(args['isISCSI']) + path = args['path'] + localMountPoint = args['localMountPoint'] + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) + util.SMlog("primarySRPath: " + primarySRPath) + + baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) + baseCopyVHD = getVHD(baseCopyUuid, isISCSI) + baseCopyPath = os.path.join(primarySRPath, baseCopyVHD) + util.SMlog("Base copy path: " + baseCopyPath) + + + # Mount secondary storage mount path on XenServer along the path + # /var/run/sr-mount//snapshots/ and create / dir + # on it. + backupsDir = mountSnapshotsDir(secondaryStorageMountPath, localMountPoint, path) + util.SMlog("Backups dir " + backupsDir) + prevBackupUuid = prevBackupUuid.split("/")[-1] + # Check existence of snapshot on primary storage + isfile(baseCopyPath, isISCSI) + if prevBackupUuid: + # Check existence of prevBackupFile + prevBackupVHD = getBackupVHD(prevBackupUuid) + prevBackupFile = os.path.join(backupsDir, prevBackupVHD) + isfile(prevBackupFile, False) + + # copy baseCopyPath to backupsDir with new uuid + backupVHD = getBackupVHD(backupUuid) + backupFile = os.path.join(backupsDir, backupVHD) + util.SMlog("Back up " + baseCopyUuid + " to Secondary Storage as " + backupUuid) + copyfile(baseCopyPath, backupFile, isISCSI) + vhdutil.setHidden(backupFile, False) + + # Because the primary storage is always scanned, the parent of this base copy is always the first base copy. + # We don't want that, we want a chain of VHDs each of which is a delta from the previous. + # So set the parent of the current baseCopyVHD to prevBackupVHD + if prevBackupUuid: + # If there was a previous snapshot + setParent(prevBackupFile, backupFile) + + txt = "1#" + backupUuid + return txt + +@echo +def deleteSnapshotBackup(session, args): + util.SMlog("Calling deleteSnapshotBackup with " + str(args)) + secondaryStorageMountPath = args['secondaryStorageMountPath'] + backupUUID = args['backupUUID'] + path = args['path'] + localMountPoint = args['localMountPoint'] + + backupsDir = mountSnapshotsDir(secondaryStorageMountPath, localMountPoint, path) + # chdir to the backupsDir for convenience + chdir(backupsDir) + + backupVHD = getBackupVHD(backupUUID) + util.SMlog("checking existence of " + backupVHD) + + # The backupVHD is on secondary which is NFS and not ISCSI. + if not os.path.isfile(backupVHD): + util.SMlog("backupVHD " + backupVHD + "does not exist. Not trying to delete it") + return "1" + util.SMlog("backupVHD " + backupVHD + " exists.") + + # Just delete the backupVHD + try: + os.remove(backupVHD) + except OSError, (errno, strerror): + errMsg = "OSError while removing " + backupVHD + " with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + + return "1" + +@echo +def revert_memory_snapshot(session, args): + util.SMlog("Calling revert_memory_snapshot with " + str(args)) + vmName = args['vmName'] + snapshotUUID = args['snapshotUUID'] + oldVmUuid = args['oldVmUuid'] + snapshotMemory = args['snapshotMemory'] + hostUUID = args['hostUUID'] + try: + cmd = '''xe vbd-list vm-uuid=%s | grep 'vdi-uuid' | grep -v 'not in database' | sed -e 's/vdi-uuid ( RO)://g' ''' % oldVmUuid + vdiUuids = os.popen(cmd).read().split() + cmd2 = '''xe vm-param-get param-name=power-state uuid=''' + oldVmUuid + if os.popen(cmd2).read().split()[0] != 'halted': + os.system("xe vm-shutdown force=true vm=" + vmName) + os.system("xe vm-destroy uuid=" + oldVmUuid) + os.system("xe snapshot-revert snapshot-uuid=" + snapshotUUID) + if snapshotMemory == 'true': + os.system("xe vm-resume vm=" + vmName + " on=" + hostUUID) + for vdiUuid in vdiUuids: + os.system("xe vdi-destroy uuid=" + vdiUuid) + except OSError, (errno, strerror): + errMsg = "OSError while reverting vm " + vmName + " to snapshot " + snapshotUUID + " with errno: " + str(errno) + " and strerr: " + strerror + util.SMlog(errMsg) + raise xs_errors.XenError(errMsg) + return "0" + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot}) + + diff --git a/server/conf/log4j-cloud.xml.in b/server/conf/log4j-cloud.xml.in index 9dc81e57f63e..fef666a27e12 100755 --- a/server/conf/log4j-cloud.xml.in +++ b/server/conf/log4j-cloud.xml.in @@ -50,6 +50,23 @@ under the License. + + + + + + + + + + + + + + + + + @@ -104,6 +121,10 @@ under the License. + + + + @@ -124,10 +145,11 @@ under the License. - + + diff --git a/server/pom.xml b/server/pom.xml index 1d2eb89ff38f..abb17ff774ce 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -151,6 +151,16 @@ opensaml ${cs.opensaml.version} + + org.elasticsearch + elasticsearch + 1.4.2 + + + org.snmp4j + snmp4j + 1.10.1 + 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 c7715a866c4f..50c847c56f01 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 @@ -240,9 +240,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml b/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml index e0171626d30f..496ee06b5782 100644 --- a/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml +++ b/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml @@ -26,7 +26,7 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > - + @@ -78,5 +78,4 @@ - \ No newline at end of file diff --git a/server/resources/META-INF/cloudstack/server-network/spring-server-network-context.xml b/server/resources/META-INF/cloudstack/server-network/spring-server-network-context.xml index 3df3a05182dd..4e5994f2ab57 100644 --- a/server/resources/META-INF/cloudstack/server-network/spring-server-network-context.xml +++ b/server/resources/META-INF/cloudstack/server-network/spring-server-network-context.xml @@ -42,9 +42,14 @@ + + diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 22ed2437d4de..02adc0ffab83 100644 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -16,6 +16,10 @@ // under the License. package com.cloud.api; +import com.cloud.network.as.AutoScaleVmProfileNetworkMapVO; +import com.cloud.network.as.dao.AutoScaleVmProfileNetworkMapDao; +import com.cloud.network.dao.LoadBalancerOptionsDao; +import com.cloud.network.dao.LoadBalancerOptionsVO; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; @@ -188,6 +192,10 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerNetworkMapDao; +import com.cloud.network.dao.LoadBalancerNetworkMapVO; +import com.cloud.network.dao.LoadBalancerPortMapDao; +import com.cloud.network.dao.LoadBalancerPortMapVO; import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDomainDao; @@ -346,6 +354,9 @@ public class ApiDBUtils { static AccountGuestVlanMapDao s_accountGuestVlanMapDao; static IPAddressDao s_ipAddressDao; static LoadBalancerDao s_loadBalancerDao; + static LoadBalancerNetworkMapDao s_lbNetMapDao; + static LoadBalancerOptionsDao s_lbOptionsDao; + static LoadBalancerPortMapDao s_lbPortMapDao; static SecurityGroupDao s_securityGroupDao; static SecurityGroupJoinDao s_securityGroupJoinDao; static ServiceOfferingJoinDao s_serviceOfferingJoinDao; @@ -392,6 +403,7 @@ public class ApiDBUtils { static AutoScaleVmGroupPolicyMapDao s_asVmGroupPolicyMapDao; static AutoScalePolicyDao s_asPolicyDao; static AutoScaleVmProfileDao s_asVmProfileDao; + static AutoScaleVmProfileNetworkMapDao s_asVmProfileNetworkMapDao; static AutoScaleVmGroupDao s_asVmGroupDao; static CounterDao s_counterDao; static ResourceTagJoinDao s_tagJoinDao; @@ -491,6 +503,12 @@ public class ApiDBUtils { @Inject private LoadBalancerDao loadBalancerDao; @Inject + private LoadBalancerNetworkMapDao lbNetMapDao; + @Inject + private LoadBalancerOptionsDao lbOptionsDao; + @Inject + private LoadBalancerPortMapDao lbPortMapDao; + @Inject private SecurityGroupDao securityGroupDao; @Inject private SecurityGroupJoinDao securityGroupJoinDao; @@ -576,6 +594,8 @@ public class ApiDBUtils { @Inject private AutoScalePolicyConditionMapDao asPolicyConditionMapDao; @Inject + private AutoScaleVmProfileNetworkMapDao asVmProfileNetworkMapDao; + @Inject private AutoScaleVmGroupPolicyMapDao asVmGroupPolicyMapDao; @Inject private AutoScalePolicyDao asPolicyDao; @@ -697,6 +717,9 @@ void init() { s_hostDao = hostDao; s_ipAddressDao = ipAddressDao; s_loadBalancerDao = loadBalancerDao; + s_lbNetMapDao = lbNetMapDao; + s_lbOptionsDao = lbOptionsDao; + s_lbPortMapDao = lbPortMapDao; s_networkRuleConfigDao = networkRuleConfigDao; s_podDao = podDao; s_serviceOfferingDao = serviceOfferingDao; @@ -737,6 +760,7 @@ void init() { s_sshKeyPairDao = sshKeyPairDao; s_userVmDetailsDao = userVmDetailsDao; s_asConditionDao = asConditionDao; + s_asVmProfileNetworkMapDao = asVmProfileNetworkMapDao; s_asPolicyDao = asPolicyDao; s_asPolicyConditionMapDao = asPolicyConditionMapDao; s_counterDao = counterDao; @@ -1501,6 +1525,10 @@ public static AutoScaleVmProfileVO findAutoScaleVmProfileById(long profileId) { return s_asVmProfileDao.findById(profileId); } + public static List listAutoScaleVmProfileNetworkMapByProfileId(long profileId){ + return s_asVmProfileNetworkMapDao.listByVmProfileId(profileId); + } + public static AutoScaleVmGroupVO findAutoScaleVmGroupById(long groupId) { return s_asVmGroupDao.findById(groupId); } @@ -2000,4 +2028,16 @@ public static boolean isAdmin(Account account) { public static List listResourceTagViewByResourceUUID(String resourceUUID, ResourceObjectType resourceType) { return s_tagJoinDao.listBy(resourceUUID, resourceType); } + + public static List listLoadBalancerAdditionalNetworks(long loadBalancerId) { + return s_lbNetMapDao.listByLoadBalancerId(loadBalancerId); + } + + public static List listLoadBalancerAdditionalPorts(long loadBalancerId) { + return s_lbPortMapDao.listByLoadBalancerId(loadBalancerId); + } + + public static List listLoadBalancerOptions(long loadBalancerId) { + return s_lbOptionsDao.listByLoadBalancerId(loadBalancerId); + } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 85ffaf59c03f..fe376350ae24 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -16,6 +16,11 @@ // under the License. package com.cloud.api; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.AutoScaleVmProfileNetworkMapVO; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.dao.LoadBalancerOptionsVO; +import com.cloud.network.lb.LoadBalancingRulesService; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.agent.api.VgpuTypesInfo; @@ -100,6 +105,8 @@ import com.cloud.network.as.Counter; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerNetworkMapVO; +import com.cloud.network.dao.LoadBalancerPortMapVO; import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; @@ -182,10 +189,13 @@ import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.snapshot.VMSnapshot; +import java.util.Enumeration; +import javax.servlet.http.HttpSession; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants.HostDetails; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.ResponseGenerator; @@ -235,6 +245,7 @@ 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.LoginCmdResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; @@ -298,6 +309,10 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; @@ -362,6 +377,14 @@ public class ApiResponseHelper implements ResponseGenerator { @Inject private IPAddressDao userIpAddressDao; + @Inject + protected LoadBalancingRulesService lbService; + @Inject + AutoScaleVmGroupVmMapDao _autoScaleVmGroupVmMapDao; + + @Inject + GloboResourceConfigurationDao globoResourceConfig; + @Override public UserResponse createUserResponse(User user) { UserAccountJoinVO vUser = ApiDBUtils.newUserView(user); @@ -499,6 +522,7 @@ public SnapshotResponse createSnapshotResponse(Snapshot snapshot) { DataCenter zone = ApiDBUtils.findZoneById(volume.getDataCenterId()); if (zone != null) { snapshotResponse.setZoneId(zone.getUuid()); + snapshotResponse.setZoneName(zone.getName()); } if (volume.getVolumeType() == Volume.Type.ROOT) { @@ -934,6 +958,51 @@ public LoadBalancerResponse createLoadBalancerResponse(LoadBalancer loadBalancer Network ntwk = ApiDBUtils.findNetworkById(loadBalancer.getNetworkId()); lbResponse.setNetworkId(ntwk.getUuid()); + List additionalNetworks = new ArrayList(); + List lbNetMaps = ApiDBUtils.listLoadBalancerAdditionalNetworks(loadBalancer.getId()); + for (LoadBalancerNetworkMapVO lbNetMapVO : lbNetMaps) { + Network network = ApiDBUtils.findNetworkById(lbNetMapVO.getNetworkId()); + additionalNetworks.add(network.getUuid()); + } + lbResponse.setAdditionalNetworks(additionalNetworks); + + List additionalPorts = new ArrayList(); + List lbPortMaps = ApiDBUtils.listLoadBalancerAdditionalPorts(loadBalancer.getId()); + for (LoadBalancerPortMapVO lbPortMapVO : lbPortMaps) { + if (lbPortMapVO.getLoadBalancerId() == loadBalancer.getId()) { // FIXME Double-check lbID because query doesn't seem to be working + additionalPorts.add(lbPortMapVO.getPublicPort() + ":" + lbPortMapVO.getPrivatePort()); + } + } + lbResponse.setAdditionalPortMap(additionalPorts); + + List lbOptions = ApiDBUtils.listLoadBalancerOptions(loadBalancer.getId()); + if (lbOptions != null) { + for (LoadBalancerOptionsVO lbOption : lbOptions) { + if (lbOption.getLoadBalancerId() == loadBalancer.getId()) { + lbResponse.setCache(lbOption.getCache()); + lbResponse.setHealthCheckDestination(lbOption.getHealthCheckDestination()); + lbResponse.setServiceDownAction(lbOption.getServiceDownAction()); + } + } + } + + List configs = globoResourceConfig.getConfiguration(GloboResourceType.LOAD_BALANCER, loadBalancer.getUuid(), GloboResourceKey.linkedLoadBalancer); + if (!configs.isEmpty()){ + GloboResourceConfigurationVO linkedConfig = configs.get(0); + LoadBalancer target = lbService.findByUuid(linkedConfig.getValue()); + if (target != null) { + lbResponse.setLinkedParentLoadBalancer(target, linkedConfig); + } else { + s_logger.warn("Load balancer target not found, please remove config " + linkedConfig.getId()); + } + } + + List children = globoResourceConfig.getConfigsByValue(GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer, loadBalancer.getUuid()); + for (GloboResourceConfigurationVO configChild : children) { + LoadBalancer childUuid = lbService.findByUuid(configChild.getResourceUuid()); + lbResponse.addLinkChild(childUuid, configChild); + } + lbResponse.setObjectName("loadbalancer"); return lbResponse; } @@ -2030,6 +2099,7 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) NetworkResponse response = new NetworkResponse(); response.setId(network.getUuid()); response.setName(network.getName()); + response.setGuruName(network.getGuruName()); response.setDisplaytext(network.getDisplayText()); if (network.getBroadcastDomainType() != null) { response.setBroadcastDomainType(network.getBroadcastDomainType().toString()); @@ -3013,6 +3083,13 @@ public AutoScaleVmProfileResponse createAutoScaleVmProfileResponse(AutoScaleVmPr if (user != null) { response.setAutoscaleUserId(user.getUuid()); } + + List networkIds = new ArrayList<>(); + for(AutoScaleVmProfileNetworkMapVO asProfileNetMap : ApiDBUtils.listAutoScaleVmProfileNetworkMapByProfileId(profile.getId())){ + networkIds.add(ApiDBUtils.findNetworkById(asProfileNetMap.getNetworkId()).getUuid()); + } + response.setNetworkIds(networkIds); + response.setUserData(profile.getUserData()); response.setObjectName("autoscalevmprofile"); // Populates the account information in the response @@ -3027,6 +3104,8 @@ public AutoScalePolicyResponse createAutoScalePolicyResponse(AutoScalePolicy pol response.setDuration(policy.getDuration()); response.setQuietTime(policy.getQuietTime()); response.setAction(policy.getAction()); + response.setStep(policy.getStep()); + response.setLogicalOperator(policy.getLogicalOperator().name()); List vos = ApiDBUtils.getAutoScalePolicyConditions(policy.getId()); ArrayList conditions = new ArrayList(vos.size()); for (ConditionVO vo : vos) { @@ -3045,11 +3124,29 @@ public AutoScalePolicyResponse createAutoScalePolicyResponse(AutoScalePolicy pol public AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup) { AutoScaleVmGroupResponse response = new AutoScaleVmGroupResponse(); response.setId(vmGroup.getUuid()); + if(vmGroup.getVmPrefixName() == null || vmGroup.getVmPrefixName().isEmpty()){ + response.setAutoScaleGroupName("as-group"); + } else { + response.setAutoScaleGroupName(vmGroup.getVmPrefixName()); + } response.setMinMembers(vmGroup.getMinMembers()); response.setMaxMembers(vmGroup.getMaxMembers()); + List autoScaleVmGroupVmMapVos = _autoScaleVmGroupVmMapDao.listByGroup(vmGroup.getId()); + response.setAutoScaleGroupCountMembers(autoScaleVmGroupVmMapVos.size()); + + //set tag information + List tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.AutoScaleVmGroup, vmGroup.getId()); + List tagResponses = new ArrayList(); + for (ResourceTag tag : tags) { + ResourceTagResponse tagResponse = createResourceTagResponse(tag, true); + tagResponses.add(tagResponse); + } + response.setTags(tagResponses); + response.setState(vmGroup.getState()); response.setInterval(vmGroup.getInterval()); response.setForDisplay(vmGroup.isDisplay()); + AutoScaleVmProfileVO profile = ApiDBUtils.findAutoScaleVmProfileById(vmGroup.getProfileId()); if (profile != null) { response.setProfileId(profile.getUuid()); @@ -3951,6 +4048,53 @@ public ListResponse createUpgradeRouterTemplateRe return response; } + public LoginCmdResponse createLoginResponse(HttpSession session) { + LoginCmdResponse response = new LoginCmdResponse(); + response.setTimeout(session.getMaxInactiveInterval()); + + final String user_UUID = (String)session.getAttribute("user_UUID"); + session.removeAttribute("user_UUID"); + response.setUserId(user_UUID); + + final String domain_UUID = (String)session.getAttribute("domain_UUID"); + session.removeAttribute("domain_UUID"); + response.setDomainId(domain_UUID); + + final Enumeration attrNames = session.getAttributeNames(); + if (attrNames != null) { + while (attrNames.hasMoreElements()) { + final String attrName = (String) attrNames.nextElement(); + final Object attrObj = session.getAttribute(attrName); + if (ApiConstants.USERNAME.equalsIgnoreCase(attrName)) { + response.setUsername(attrObj.toString()); + } + if (ApiConstants.ACCOUNT.equalsIgnoreCase(attrName)) { + response.setAccount(attrObj.toString()); + } + if (ApiConstants.FIRSTNAME.equalsIgnoreCase(attrName)) { + response.setFirstName(attrObj.toString()); + } + if (ApiConstants.LASTNAME.equalsIgnoreCase(attrName)) { + response.setLastName(attrObj.toString()); + } + if (ApiConstants.TYPE.equalsIgnoreCase(attrName)) { + response.setType((attrObj.toString())); + } + if (ApiConstants.TIMEZONE.equalsIgnoreCase(attrName)) { + response.setTimeZone(attrObj.toString()); + } + if (ApiConstants.REGISTERED.equalsIgnoreCase(attrName)) { + response.setRegistered(attrObj.toString()); + } + if (ApiConstants.SESSIONKEY.equalsIgnoreCase(attrName)) { + response.setSessionKey(attrObj.toString()); + } + } + } + response.setResponseName("loginresponse"); + return response; + } + @Override public SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey) { SSHKeyPairResponse response = new SSHKeyPairResponse(sshkeyPair.getName(), sshkeyPair.getFingerprint()); @@ -3964,4 +4108,5 @@ public SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolea response.setDomainName(domain.getName()); return response; } + } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index a97984a2be21..2f488790569d 100644 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -47,6 +47,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.ReflectUtil; import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.UserCloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; @@ -69,6 +70,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -234,6 +236,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer private static ExecutorService s_executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory( "ApiServer")); + @Inject + MessageBus _messageBus; + @Inject + ResponseGenerator _responseGenerator; + static final ConfigKey EnableSecureSessionCookie = new ConfigKey("Advanced", Boolean.class, "enable.secure.session.cookie", "false", "Session cookie is marked as secure if this is enabled. Secure cookies only work when HTTPS is used.", false); @@ -563,10 +570,10 @@ public String handleRequest(final Map params, final String responseType, final S } } } catch (final InvalidParameterValueException ex) { - s_logger.info(ex.getMessage()); + s_logger.info(ex.getMessage(), ex); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex); } catch (final IllegalArgumentException ex) { - s_logger.info(ex.getMessage()); + s_logger.info(ex.getMessage(), ex); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex); } catch (final PermissionDeniedException ex) { final ArrayList idList = ex.getIdProxyList(); @@ -578,16 +585,16 @@ public String handleRequest(final Map params, final String responseType, final S buf.append(obj.getUuid()); buf.append(" "); } - s_logger.info("PermissionDenied: " + ex.getMessage() + " on objs: [" + buf.toString() + "]"); + s_logger.info("PermissionDenied: " + ex.getMessage() + " on objs: [" + buf.toString() + "]", ex); } else { - s_logger.info("PermissionDenied: " + ex.getMessage()); + s_logger.info("PermissionDenied: " + ex.getMessage(), ex); } throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, ex.getMessage(), ex); } catch (final AccountLimitException ex) { - s_logger.info(ex.getMessage()); + s_logger.info(ex.getMessage(), ex); throw new ServerApiException(ApiErrorCode.ACCOUNT_RESOURCE_LIMIT_ERROR, ex.getMessage(), ex); } catch (final InsufficientCapacityException ex) { - s_logger.info(ex.getMessage()); + s_logger.info(ex.getMessage(), ex); String errorMsg = ex.getMessage(); if (!accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) { // hide internal details to non-admin user for security reason @@ -595,7 +602,7 @@ public String handleRequest(final Map params, final String responseType, final S } throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex); } catch (final ResourceAllocationException ex) { - s_logger.info(ex.getMessage()); + s_logger.info(ex.getMessage(), ex); throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage(), ex); } catch (final ResourceUnavailableException ex) { s_logger.info(ex.getMessage()); @@ -606,8 +613,11 @@ public String handleRequest(final Map params, final String responseType, final S } throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, errorMsg, ex); } catch (final ServerApiException ex) { - s_logger.info(ex.getDescription()); + s_logger.info(ex.getDescription(), ex); throw ex; + } catch (final UserCloudRuntimeException ex) { + String errorMsg = ex.getMessage(); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg, ex); } catch (final Exception ex) { s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command), ex); String errorMsg = ex.getMessage(); diff --git a/server/src/com/cloud/api/ResponseObjectTypeAdapter.java b/server/src/com/cloud/api/ResponseObjectTypeAdapter.java index 44baedc933b1..8761b62abc11 100644 --- a/server/src/com/cloud/api/ResponseObjectTypeAdapter.java +++ b/server/src/com/cloud/api/ResponseObjectTypeAdapter.java @@ -42,6 +42,7 @@ public JsonElement serialize(ResponseObject responseObj, Type typeOfResponseObj, } else if (responseObj instanceof ExceptionResponse) { obj.addProperty("errorcode", ((ExceptionResponse)responseObj).getErrorCode()); obj.addProperty("errortext", ((ExceptionResponse)responseObj).getErrorText()); + obj.addProperty("context", ((ExceptionResponse)responseObj).getContext()); return obj; } else { obj.add(responseObj.getObjectName(), ApiResponseGsonHelper.getBuilder().create().toJsonTree(responseObj)); diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 9d24417e732d..88e131267f12 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.api.query; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.dao.NicDetailsDao; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -329,6 +332,12 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private DomainRouterDao _routerDao; + @Inject + private VolumeDetailsDao _volumeDetailDao; + + @Inject + private NicDetailsDao _nicDetailDao; + @Inject UserVmDetailsDao _userVmDetailDao; @@ -752,8 +761,8 @@ public ListResponse searchForUserVMs(ListVMsCmd cmd) { if (cmd instanceof ListVMsCmdByAdmin) { respView = ResponseView.Full; } - List vmResponses = ViewResponseHelper.createUserVmResponse(respView, "virtualmachine", cmd.getDetails(), result.first().toArray(new UserVmJoinVO[result.first().size()])); + List vmResponses = ViewResponseHelper.createUserVmResponse(respView, "virtualmachine", cmd.getDetails(), result.first().toArray(new UserVmJoinVO[result.first().size()])); response.setResponses(vmResponses, result.second()); return response; } @@ -1267,7 +1276,12 @@ private Pair, Integer> searchForRoutersInternal(BaseLis public ListResponse listProjects(ListProjectsCmd cmd) { Pair, Integer> projects = listProjectsInternal(cmd); ListResponse response = new ListResponse(); - List projectResponses = ViewResponseHelper.createProjectResponse(projects.first().toArray(new ProjectJoinVO[projects.first().size()])); + List projectResponses = null; + if (cmd.isSimple()){ + projectResponses = ViewResponseHelper.createSimpleProjectResponse(projects.first().toArray(new ProjectJoinVO[projects.first().size()])); + }else{ + projectResponses = ViewResponseHelper.createProjectResponse(projects.first().toArray(new ProjectJoinVO[projects.first().size()])); + } response.setResponses(projectResponses, projects.second()); return response; } @@ -1765,7 +1779,7 @@ private Pair, Integer> searchForVolumesInternal(ListVolumesCm } if (display != null) { - sc.setParameters("display", display); + sc.setParameters("displayVolume", display); } setIdsListToSearchCriteria(sc, ids); @@ -3683,6 +3697,15 @@ protected ResourceDetailResponse createResourceDetailsResponse(ResourceDetail re return resourceDetailResponse; } + @Override + public Pair, Integer> listGloboVm(Long id, String name, Long projectId, Map tags) { + try { + return _userVmJoinDao.list(id, name, projectId, tags); + }catch (Exception e ) { + throw new CloudRuntimeException("Error list virtual machines.", e); + } + } + @Override public String getConfigComponentName() { return QueryService.class.getSimpleName(); diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java b/server/src/com/cloud/api/query/ViewResponseHelper.java index 949bc1733b28..a730a01b45f8 100644 --- a/server/src/com/cloud/api/query/ViewResponseHelper.java +++ b/server/src/com/cloud/api/query/ViewResponseHelper.java @@ -29,6 +29,7 @@ import com.cloud.configuration.Resource; import com.cloud.domain.Domain; import org.apache.log4j.Logger; + import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants.DomainDetails; import org.apache.cloudstack.api.ApiConstants.HostDetails; @@ -212,6 +213,32 @@ public static List createProjectResponse(ProjectJoinVO... proje return new ArrayList(prjDataList.values()); } + public static List createSimpleProjectResponse(ProjectJoinVO... projects) { + Hashtable prjDataList = new Hashtable<>(); + for(ProjectJoinVO project : projects){ + ProjectResponse projectResponse = new ProjectResponse(); + projectResponse.setName(project.getName()); + projectResponse.setDisplaytext(project.getDisplayText()); + projectResponse.setBusinessServiceId(project.getBusinessServiceId()); + projectResponse.setClientId(project.getClientId()); + projectResponse.setComponentId(project.getComponentId()); + projectResponse.setSubComponentId(project.getSubComponentId()); + projectResponse.setProductId(project.getProductId()); + projectResponse.setDetailedUsage(project.isDetailedUsage()); + projectResponse.setId(project.getUuid()); + if (project.getState() != null) { + projectResponse.setState(project.getState().toString()); + } + projectResponse.setVmTotal(ApiDBUtils.getResourceCount(Resource.ResourceType.user_vm, project.getProjectAccountId())); + projectResponse.setDomainId(project.getDomainUuid()); + projectResponse.setDomain(project.getDomainName()); + projectResponse.setOwner(project.getOwner()); + projectResponse.setObjectName("project"); + prjDataList.put(project.getId(), projectResponse); + } + return new ArrayList<>(prjDataList.values()); + } + public static List createProjectAccountResponse(ProjectAccountJoinVO... projectAccounts) { List responseList = new ArrayList(); for (ProjectAccountJoinVO proj : projectAccounts) { diff --git a/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java index 25598b35b275..4f9c1c08012e 100644 --- a/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java @@ -72,6 +72,12 @@ public ProjectResponse newProjectResponse(ProjectJoinVO proj) { response.setId(proj.getUuid()); response.setName(proj.getName()); response.setDisplaytext(proj.getDisplayText()); + response.setBusinessServiceId(proj.getBusinessServiceId()); + response.setClientId(proj.getClientId()); + response.setComponentId(proj.getComponentId()); + response.setSubComponentId(proj.getSubComponentId()); + response.setProductId(proj.getProductId()); + response.setDetailedUsage(proj.isDetailedUsage()); if (proj.getState() != null) { response.setState(proj.getState().toString()); } diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDao.java b/server/src/com/cloud/api/query/dao/UserVmJoinDao.java index 87af584024bf..c7ca134e5351 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDao.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDao.java @@ -16,9 +16,11 @@ // under the License. package com.cloud.api.query.dao; +import com.cloud.utils.Pair; import java.util.EnumSet; import java.util.List; +import java.util.Map; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.response.UserVmResponse; @@ -39,4 +41,6 @@ public interface UserVmJoinDao extends GenericDao { List searchByIds(Long... ids); List listActiveByIsoId(Long isoId); + + public Pair, Integer> list(Long id, String name, Long projectId, Map tags); } diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index f0a0a56e3c63..10283f9591a3 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -16,6 +16,12 @@ // under the License. package com.cloud.api.query.dao; +import com.cloud.server.ResourceTag; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.cloud.utils.db.TransactionLegacy; +import java.sql.ResultSet; +import java.sql.SQLException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.EnumSet; @@ -76,6 +82,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation VmDetailSearch; private final SearchBuilder activeVmByIsoSearch; + private static final String VIEW_GLOBO_VM_COLUMNS = "id, uuid, name, display_name, instance_name, state, ha_enabled, account_id, account_name, account_type, project_name, project_id, account_id, domain_id, display_vm, service_offering_uuid, service_offering_name, os_id, os_name, host_name, data_center_id, data_center_name"; protected UserVmJoinDaoImpl() { @@ -501,4 +508,144 @@ public List newUserVmView(UserVm... userVms) { return uvms; } + public Pair, Integer> list(Long id, String name, Long projectId, Map tags) { + List uuids = null; + if ( tags != null) { + uuids = listVmUUidsByTag(projectId, tags); + } + + List result = new ArrayList(); + if ( uuids != null && uuids.size() == 0) { + return new Pair(result, result.size()); + } + + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List params = new ArrayList(); + StringBuilder sql = new StringBuilder("SELECT ").append(VIEW_GLOBO_VM_COLUMNS).append(" FROM view_globo_vm vm WHERE 1=1"); + + if ( id != null ){ + sql.append(" AND vm.id = ? "); + } + + if ( name != null ){ + sql.append(" AND vm.name = ? "); + } + + if ( projectId != null ){ + sql.append(" AND vm.project_id = ? "); + } + + if (uuids != null && uuids.size() > 0){ + sql.append(" AND vm.uuid IN ('" + StringUtils.join("','", uuids.toArray()) + "')"); + } + + try { + java.sql.PreparedStatement pstmt = txn.prepareStatement(sql.toString()); + int paramCount = 1; + if (id != null){ + pstmt.setLong(paramCount++, id); + } + + if (name != null){ + pstmt.setString(paramCount++, name); + } + + if (projectId != null){ + pstmt.setLong(paramCount++, projectId); + } + + s_logger.debug("List Globo VM SQL: " + sql + " Params: " + params); + ResultSet rs = pstmt.executeQuery(); + + while (rs.next()) { + UserVmResponse userVmResponse = resultSetToVmResponse(rs); + result.add(userVmResponse); + } + }catch (Exception e) { + throw new RuntimeException("Error selecting vm data.", e); + } + + return new Pair(result, result.size()); + } + + protected List listVmUUidsByTag(Long projectId, Map tags){ + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + List params = new ArrayList(); + String sql = createQueryByTags(projectId, tags, params); + + try { + java.sql.PreparedStatement pstmt = txn.prepareStatement(sql); + for (int i= 0; i < params.size(); i++) { + pstmt.setObject(i + 1, params.get(i)); + } + + s_logger.debug("List Globo VM SQL: "+ sql + " Params: " + params); + ResultSet rs = pstmt.executeQuery(); + List uuids = new ArrayList(); + while(rs.next()) { + uuids.add(rs.getString("resource_uuid")); + } + return uuids; + } catch (Exception e ) { + throw new RuntimeException("Error selecting resource_id by tag: " + sql + " params: " + params, e); + } + } + + protected String createQueryByTags(Long projectId, Map tags, List params) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT tg.resource_uuid as resource_uuid FROM resource_tag_view tg WHERE tg.resource_type = '") + .append(ResourceTag.ResourceObjectType.UserVm).append("'"); + + if (projectId != null) { + sql.append(" AND tg.project_id = ? "); + params.add(projectId); + } + + if (tags != null && tags.size() > 0) { + sql.append(" AND ("); + int i = 0; + for (String key : tags.keySet()) { + String value = tags.get(key); + + if (tags.size() > 1 && i != 0) { + sql.append(" OR"); + } + sql.append(" (tg.key = ? AND tg.value = ?)"); + + params.add(key); + params.add(value); + i++; + } + params.add(tags.size()); + sql.append(") GROUP BY tg.resource_uuid HAVING count(tg.resource_uuid) = ?"); + } + return sql.toString(); + } + + private UserVmResponse resultSetToVmResponse(ResultSet rs) throws SQLException { + UserVmResponse vmResponse = new UserVmResponse(); + + vmResponse.setId(rs.getString("uuid")); + vmResponse.setDisplayName(rs.getString("display_name") != null ? rs.getString("display_name") : rs.getString("name")); + vmResponse.setName(rs.getString("name")); + vmResponse.setInstanceName(rs.getString("instance_name")); + vmResponse.setState(rs.getString("state")); + vmResponse.setHaEnable(rs.getBoolean("ha_enabled")); + + vmResponse.setProjectName(rs.getString("project_name")); + vmResponse.setAccountName(rs.getString("account_name")); + + vmResponse.setDisplayVm(rs.getBoolean("display_vm")); + vmResponse.setServiceOfferingName(rs.getString("service_offering_name")); + vmResponse.setServiceOfferingId(rs.getString("service_offering_uuid")); + vmResponse.setOsTypeName(rs.getString("os_name")); + vmResponse.setOsTypeId(rs.getLong("os_id")); + vmResponse.setHostName(rs.getString("host_name")); + vmResponse.setZoneId(rs.getString("data_center_id")); + vmResponse.setZoneName(rs.getString("data_center_name")); + vmResponse.setObjectName("virtualmachine"); + + return vmResponse; + } } diff --git a/server/src/com/cloud/api/query/vo/GloboVmVO.java b/server/src/com/cloud/api/query/vo/GloboVmVO.java new file mode 100644 index 000000000000..d03d4f75966a --- /dev/null +++ b/server/src/com/cloud/api/query/vo/GloboVmVO.java @@ -0,0 +1,191 @@ +package com.cloud.api.query.vo; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "view_globo_vm") +public class GloboVmVO extends BaseViewVO{ + + @Id + @Column(name = "id", updatable = false, nullable = false) + private long id; + + @Column(name = "uuid", updatable = false, nullable = false, length = 255) + private String uuid = null; + + @Column(name = "display_name", updatable = false, nullable = false, length = 255) + private String displayName = null; + + @Column(name = "instance_name", updatable = false, nullable = false, length = 255) + private String instanceName = null; + + @Column(name = "host_name", updatable = false, nullable = false, length = 255) + private String hostname = null; + + @Column(name = "state", updatable = false, nullable = false, length = 255) + private String state = null; + + @Column(name = "ha_enabled", updatable = false, nullable = false) + private boolean haEnabled; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "project_id") + private long projectId; + + @Column(name = "project_name", updatable = false, nullable = false, length = 255) + private String projectName = null; + + @Column(name = "project_uuid", updatable = false, nullable = false, length = 255) + private String projectUuid = null; + + + @Column(name = "os_name", updatable = false, nullable = false, length = 255) + private String osName; + + @Column(name = "dc_name", updatable = false, nullable = false, length = 255) + private String dcName; + + @Column(name = "service_offering_name", updatable = false, nullable = false, length = 255) + private String serviceOfferingName; + + @Column(name = "display_vm", updatable = false, nullable = false) + private boolean displayVm; + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getInstanceName() { + return instanceName; + } + + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public boolean isHaEnabled() { + return haEnabled; + } + + public void setHaEnabled(boolean haEnabled) { + this.haEnabled = haEnabled; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public String getOsName() { + return osName; + } + + public void setOsName(String osName) { + this.osName = osName; + } + + public String getDcName() { + return dcName; + } + + public void setDcName(String dcName) { + this.dcName = dcName; + } + + public void setId(long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + + public long getProjectId() { + return projectId; + } + + public void setProjectId(long projectId) { + this.projectId = projectId; + } + + public String getProjectName() { + return projectName; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public String getProjectUuid() { + return projectUuid; + } + + public void setProjectUuid(String projectUuid) { + this.projectUuid = projectUuid; + } + + public void setServiceOfferingName(String serviceOfferingName) { + this.serviceOfferingName = serviceOfferingName; + } + + public void setDisplayVm(boolean displayVm) { + this.displayVm = displayVm; + } + + @Override + public long getId() { + return id; + } + + public String getServiceOfferingName() { + return serviceOfferingName; + } + + public boolean isDisplayVm() { + return displayVm; + } +} diff --git a/server/src/com/cloud/api/query/vo/ProjectJoinVO.java b/server/src/com/cloud/api/query/vo/ProjectJoinVO.java index 32488ff1b22c..cc0dbf4979e6 100644 --- a/server/src/com/cloud/api/query/vo/ProjectJoinVO.java +++ b/server/src/com/cloud/api/query/vo/ProjectJoinVO.java @@ -65,6 +65,24 @@ public class ProjectJoinVO extends BaseViewVO implements InternalIdentity, Ident @Column(name = "account_id") private long accountId; + @Column(name = "business_service_id") + private String businessServiceId; + + @Column(name = "client_id") + private String clientId; + + @Column(name = "component_id") + private String componentId; + + @Column(name = "sub_component_id") + private String subComponentId; + + @Column(name = "product_id") + private String productId; + + @Column(name = "detailed_usage") + private boolean detailedUsage; + @Column(name = "domain_id") private long domainId; @@ -132,6 +150,30 @@ public long getDomainId() { return domainId; } + public String getBusinessServiceId() { + return businessServiceId; + } + + public String getClientId() { + return clientId; + } + + public String getComponentId() { + return componentId; + } + + public String getSubComponentId() { + return subComponentId; + } + + public String getProductId() { + return productId; + } + + public Boolean isDetailedUsage() { + return detailedUsage; + } + public String getDomainUuid() { return domainUuid; } diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 2b9717de1e29..525be2539b8d 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -95,6 +95,7 @@ public static String toJSONSerializedString(ResponseObject result, StringBuilder if (result instanceof ListResponse) { List responses = ((ListResponse)result).getResponses(); Integer count = ((ListResponse)result).getCount(); + String context = ((ListResponse)result).getContext(); boolean nonZeroCount = (count != null && count.longValue() != 0); if (nonZeroCount) { sb.append("{\"").append(ApiConstants.COUNT).append("\":").append(count); diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 80642f513759..624ba5224200 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -95,7 +95,6 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.log4j.Logger; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -232,6 +231,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import org.apache.log4j.Logger; public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable { public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class); @@ -1657,6 +1657,14 @@ protected void checkIfZoneIsDeletable(final long zoneId) { //} } + + protected boolean shouldHaveRemovedQuery(String tableName) { + final String[] tablesArray = {"host_pod_ref", "host", "volumes", "physical_network", "vm_instance"}; + final List tables = Arrays.asList(tablesArray); + + return tables.contains(tableName); + } + private void checkZoneParameters(final String zoneName, final String dns1, final String dns2, final String internalDns1, final String internalDns2, final boolean checkForDuplicates, final Long domainId, final String allocationStateStr, final String ip6Dns1, final String ip6Dns2) { if (checkForDuplicates) { @@ -5501,6 +5509,17 @@ public PortableIpRange createPortableIpRange(final CreatePortableIpRangeCmd cmd) final String netmask = cmd.getNetmask(); String vlanId = cmd.getVlan(); + return createPortableIpRange(regionId, startIP, endIP, gateway, netmask, vlanId); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RANGE_CREATE, + eventDescription = "creating portable ip range", async = false) + public PortableIpRangeVO createPortableIpRange(final Integer regionId, + final String startIP, final String endIP, final String gateway, + final String netmask, String vlanId) { + final RegionVO region = _regionDao.findById(regionId); if (region == null) { throw new InvalidParameterValueException("Invalid region ID: " + regionId); diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index e6c0a8da6ecf..c43ad0f9a307 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -663,9 +663,7 @@ public ConsoleProxyVO startNew(long dataCenterId) throws ConcurrentOperationExce new ConsoleProxyAlertEventArgs(ConsoleProxyAlertEventArgs.PROXY_CREATED, dataCenterId, proxy.getId(), proxy, null)); return proxy; } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to allocate console proxy storage, remove the console proxy record from DB, proxy id: " + proxyVmId); - } + s_logger.warn("Unable to allocate console proxy storage, remove the console proxy record from DB, proxy id: " + proxyVmId); } return null; } @@ -1339,7 +1337,7 @@ public boolean configure(String name, Map params) throws Configu } if (s_logger.isInfoEnabled()) { - s_logger.info("Console Proxy Manager is configured."); + s_logger.debug("Console Proxy Manager is configured."); } return true; } diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 64fabb99dd5c..8e2305d6a671 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -1447,6 +1447,7 @@ protected Pair>, List> findSuitablePoolsFo useLocalStorage = useLocalStorageForSystemVM.booleanValue(); s_logger.debug("System VMs will use " + (useLocalStorage ? "local" : "shared") + " storage for zone id=" + plan.getDataCenterId()); } + } else { useLocalStorage = diskOffering.getUseLocalStorage(); diff --git a/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClient.java b/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClient.java new file mode 100644 index 000000000000..9a60039cdd7f --- /dev/null +++ b/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClient.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.globodictionary.apiclient; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; + +import java.util.List; + +public interface GloboDictionaryAPIClient { + + GloboDictionaryEntity get(GloboDictionaryService.GloboDictionaryEntityType type, String id); + + List list(GloboDictionaryService.GloboDictionaryEntityType type); +} \ No newline at end of file diff --git a/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImpl.java b/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImpl.java new file mode 100644 index 000000000000..72cb3ec89ae2 --- /dev/null +++ b/server/src/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImpl.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 com.cloud.globodictionary.apiclient; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; +import com.cloud.globodictionary.apiclient.model.GloboDictionaryEntityVO; +import com.cloud.utils.exception.CloudRuntimeException; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + + +@Component +public class GloboDictionaryAPIClientImpl implements GloboDictionaryAPIClient, Configurable { + + public static final Logger s_logger = Logger.getLogger(GloboDictionaryAPIClientImpl.class); + + private static final ConfigKey GloboDictionaryEndpoint = new ConfigKey<>("Globo Dictionary", String.class, "globodictionary.api.endpoint", "", + "Globo Dictionary API Endpoint", true, ConfigKey.Scope.Global); + + private final HttpClient httpClient; + private static final String API_ID_QUERY_PARAMETER = "id_service_now"; + + public GloboDictionaryAPIClientImpl() { + this.httpClient = new HttpClient(new MultiThreadedHttpConnectionManager()); + } + + public GloboDictionaryAPIClientImpl(HttpClient httpClient){ + this.httpClient = httpClient; + } + + @Override + public GloboDictionaryEntity get(GloboDictionaryService.GloboDictionaryEntityType type, String id) { + return this.getDictionaryEntity(type, id); + } + + @Override + public List list(GloboDictionaryService.GloboDictionaryEntityType type) { + return this.listDictionaryEntity(type); + } + + private List listDictionaryEntity(GloboDictionaryService.GloboDictionaryEntityType entityType) { + String response = this.makeHttpRequest(entityType); + List globoDictionaryEntities = new Gson().fromJson(response, new TypeToken>() {}.getType()); + Collections.sort(globoDictionaryEntities); + return globoDictionaryEntities; + } + + private GloboDictionaryEntity getDictionaryEntity(GloboDictionaryService.GloboDictionaryEntityType entityType, String id) { + String response = this.makeHttpRequest(entityType, API_ID_QUERY_PARAMETER + "=" + id); + List entities = new Gson().fromJson(response, new TypeToken>() { }.getType()); + if(entities != null && entities.size() > 0){ + return entities.get(0); + } + return null; + } + + private String makeHttpRequest(GloboDictionaryService.GloboDictionaryEntityType entityType) { + return this.makeHttpRequest(entityType, ""); + } + + private String makeHttpRequest(GloboDictionaryService.GloboDictionaryEntityType entityType, String queryString) { + GetMethod getMethod = this.createRequest(entityType, queryString); + try { + int status = this.httpClient.executeMethod(getMethod); + if(status == HttpStatus.SC_OK) { + return getMethod.getResponseBodyAsString(); + }else{ + s_logger.error("[DictionaryAPI] Invalid API status code " + status); + s_logger.error("[DictionaryAPI] Invalid API response body " + getMethod.getResponseBodyAsString()); + throw new CloudRuntimeException("Error listing " + entityType.getFriendlyName()); + } + } catch (IOException e) { + s_logger.error("[DictionaryAPI] Communication failure", e); + throw new CloudRuntimeException("Error listing " + entityType.getFriendlyName(), e); + } finally { + getMethod.releaseConnection(); + } + } + + protected GetMethod createRequest(GloboDictionaryService.GloboDictionaryEntityType entityType, String queryString) { + return new GetMethod(GloboDictionaryEndpoint.value() + entityType.getUri() + "?" + queryString); + } + + @Override + public String getConfigComponentName() { + return this.getClass().getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] {GloboDictionaryEndpoint}; + } +} diff --git a/server/src/com/cloud/globodictionary/apiclient/model/GloboDictionaryEntityVO.java b/server/src/com/cloud/globodictionary/apiclient/model/GloboDictionaryEntityVO.java new file mode 100644 index 000000000000..b7402d1f2249 --- /dev/null +++ b/server/src/com/cloud/globodictionary/apiclient/model/GloboDictionaryEntityVO.java @@ -0,0 +1,93 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globodictionary.apiclient.model; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.google.gson.annotations.SerializedName; + +public class GloboDictionaryEntityVO implements GloboDictionaryEntity { + + @SerializedName("id_service_now") + protected String id; + + @SerializedName("nome") + protected String name; + + @SerializedName("status") + protected String status; + + public GloboDictionaryEntityVO() { + } + + public GloboDictionaryEntityVO(String id, String name, String status) { + this.id = id; + this.name = name; + this.status = status; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public boolean isActive() { + return this.getStatus().equals("Ativo") || this.getStatus().equals("Em uso"); + } + + public int compareTo(GloboDictionaryEntity entity) { + return this.name.compareTo(entity.getName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GloboDictionaryEntityVO that = (GloboDictionaryEntityVO) o; + + if (!id.equals(that.id)) return false; + if (!name.equals(that.name)) return false; + return status.equals(that.status); + } + + @Override + public int hashCode() { + int result = id.hashCode(); + result = 31 * result + name.hashCode(); + result = 31 * result + status.hashCode(); + return result; + } +} diff --git a/server/src/com/cloud/globodictionary/manager/GloboDictionaryManager.java b/server/src/com/cloud/globodictionary/manager/GloboDictionaryManager.java new file mode 100644 index 000000000000..3fac23362594 --- /dev/null +++ b/server/src/com/cloud/globodictionary/manager/GloboDictionaryManager.java @@ -0,0 +1,82 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.cloud.globodictionary.manager; + +import com.cloud.globodictionary.GloboDictionaryService; +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.utils.component.PluggableService; +import com.cloud.globodictionary.apiclient.GloboDictionaryAPIClient; +import org.apache.cloudstack.api.command.user.globodictionary.ListBusinessServicesCmd; +import org.apache.cloudstack.api.command.user.globodictionary.ListClientsCmd; +import org.apache.cloudstack.api.command.user.globodictionary.ListComponentsCmd; +import org.apache.cloudstack.api.command.user.globodictionary.ListSubComponentsCmd; +import org.apache.cloudstack.api.command.user.globodictionary.ListProductsCmd; + +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GloboDictionaryManager implements GloboDictionaryService, PluggableService { + + @Inject + private GloboDictionaryAPIClient dictionaryAPIClient; + + + public GloboDictionaryManager() { + } + + public GloboDictionaryManager(GloboDictionaryAPIClient dictionaryAPIClient) { + this.dictionaryAPIClient = dictionaryAPIClient; + } + + @Override + public List list(GloboDictionaryEntityType type) { + return this.filterActives(dictionaryAPIClient.list(type)); + } + + @Override + public GloboDictionaryEntity get(GloboDictionaryEntityType type, String id) { + GloboDictionaryEntity businessService = dictionaryAPIClient.get(type, id); + if (businessService != null && businessService.isActive()) { + return businessService; + } + return null; + } + + private List filterActives(List entities){ + List actives = new ArrayList<>(); + for(GloboDictionaryEntity entity : entities){ + if(entity.isActive()){ + actives.add(entity); + } + } + return actives; + } + + public List> getCommands() { + List> cmdList = new ArrayList<>(); + cmdList.add(ListBusinessServicesCmd.class); + cmdList.add(ListClientsCmd.class); + cmdList.add(ListComponentsCmd.class); + cmdList.add(ListSubComponentsCmd.class); + cmdList.add(ListProductsCmd.class); + return cmdList; + } +} diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 71acc834d9a9..4d8d2c737545 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -1201,6 +1201,14 @@ public PublicIp doInTransaction(TransactionStatus status) throws InsufficientAdd public IpAddress allocatePortableIp(final Account ipOwner, Account caller, final long dcId, final Long networkId, final Long vpcID) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException { + return allocatePortableIp(ipOwner, caller, dcId, networkId, vpcID, null); + } + + @Override + @DB + public IpAddress allocatePortableIp(final Account ipOwner, Account caller, final long dcId, final Long networkId, final Long vpcID, final String requestedIp) + throws ConcurrentOperationException, + ResourceAllocationException, InsufficientAddressCapacityException { GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); IPAddressVO ipaddr; @@ -1211,17 +1219,30 @@ public IpAddress allocatePortableIp(final Account ipOwner, Account caller, final ipaddr = Transaction.execute(new TransactionCallbackWithException() { @Override public IPAddressVO doInTransaction(TransactionStatus status) throws InsufficientAddressCapacityException { - PortableIpVO allocatedPortableIp; + PortableIpVO allocatedPortableIp = null; - List portableIpVOs = _portableIpDao.listByRegionIdAndState(1, PortableIp.State.Free); + List portableIpVOs = _portableIpDao.listByRegionId(1); if (portableIpVOs == null || portableIpVOs.isEmpty()) { InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available portable IP addresses", Region.class, new Long(1)); throw ex; } + //allocate first portable IP to the user if not specified + if (requestedIp == null) { + allocatedPortableIp = portableIpVOs.get(0); + } else { + for (PortableIpVO portableIp : portableIpVOs) { + if (portableIp.getAddress().equals(requestedIp)) { + allocatedPortableIp = portableIp; + break; + } + } + if (allocatedPortableIp == null) { + InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find portable IP addresses " + requestedIp, Region.class, new Long(1)); + throw ex; + } + } - // allocate first portable IP to the user - allocatedPortableIp = portableIpVOs.get(0); allocatedPortableIp.setAllocatedTime(new Date()); allocatedPortableIp.setAllocatedToAccountId(ipOwner.getAccountId()); allocatedPortableIp.setAllocatedInDomainId(ipOwner.getDomainId()); diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 33a817872835..6ef44fdc5ce7 100644 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -1411,7 +1411,7 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa } @Override - public Pair, Integer> searchForNetworks(ListNetworksCmd cmd) { + public List searchForAllNetworks(ListNetworksCmd cmd) { Long id = cmd.getId(); String keyword = cmd.getKeyword(); Long zoneId = cmd.getZoneId(); @@ -1504,7 +1504,7 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd if (domainId != null) { path = _domainDao.findById(domainId).getPath(); } else { - path = _domainDao.findById(caller.getDomainId()).getPath(); + path = _domainDao.findById(caller.getDomainId()).getPath(); } if (listAll && domainId == null) { @@ -1553,11 +1553,11 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); } - SearchBuilder accountSearch = _accountDao.createSearchBuilder(); + SearchBuilder accountSearch = _accountDao.createSearchBuilder(); accountSearch.and("typeNEQ", accountSearch.entity().getType(), SearchCriteria.Op.NEQ); accountSearch.and("typeEQ", accountSearch.entity().getType(), SearchCriteria.Op.EQ); - sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER); + sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER); List networksToReturn = new ArrayList(); @@ -1628,7 +1628,11 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd networksToReturn = networksForDeploy; } - + return networksToReturn; + } + @Override + public Pair, Integer> searchForNetworks(ListNetworksCmd cmd) { + List networksToReturn = searchForAllNetworks(cmd); //Now apply pagination List wPagination = StringUtils.applyPagination(networksToReturn, cmd.getStartIndex(), cmd.getPageSizeVal()); if (wPagination != null) { diff --git a/server/src/com/cloud/network/as/AutoScaleCounterProcessor.java b/server/src/com/cloud/network/as/AutoScaleCounterProcessor.java new file mode 100644 index 000000000000..e51fb8268acb --- /dev/null +++ b/server/src/com/cloud/network/as/AutoScaleCounterProcessor.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.as; + +import com.cloud.server.as.VirtualMachineAddress; + +import java.util.List; +import java.util.Map; + +public interface AutoScaleCounterProcessor { + boolean process(AutoScaleVmGroup asGroup, List ipAddresses, Map counters); +} diff --git a/server/src/com/cloud/network/as/AutoScaleCounterProcessorImpl.java b/server/src/com/cloud/network/as/AutoScaleCounterProcessorImpl.java new file mode 100644 index 000000000000..65fd8f20a3e3 --- /dev/null +++ b/server/src/com/cloud/network/as/AutoScaleCounterProcessorImpl.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import com.cloud.server.as.VirtualMachineAddress; +import com.google.gson.JsonObject; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Reads metrics from VMS via SNMP and writes them to logstash via UDP. + * Each VM will have its metrics read by an isolated thread + */ +public class AutoScaleCounterProcessorImpl implements AutoScaleCounterProcessor, Configurable { + + @Inject + LogStashClient logStashClient; + @Inject + SNMPClient snmpClient; + ExecutorService threadExecutorPool; + + private static final ConfigKey ThreadPoolSize = new ConfigKey<>("Advanced", Integer.class, "autoscale.counter.threadpoolsize", "10", "Auto scale counter reader thread pool size", false, ConfigKey.Scope.Global); + + public static Logger s_logger = Logger.getLogger(AutoScaleCounterProcessorImpl.class.getName()); + + public AutoScaleCounterProcessorImpl(){ + threadExecutorPool = Executors.newFixedThreadPool(ThreadPoolSize.value()); + } + + @Override + public boolean process(final AutoScaleVmGroup asGroup, final List virtualMachines, final Map counters) { + s_logger.debug("[AutoScale] Processing counters of virtual machines from AutoScaleGroup " + asGroup.getUuid() + ". Total of virtual machines to be processed is " + virtualMachines.size()); + for(final VirtualMachineAddress virtualMachine : virtualMachines){ + threadExecutorPool.execute(new Runnable() { + @Override + public void run() { + try { + processCounters(asGroup, virtualMachine, counters); + } catch(Exception e) { + s_logger.error("Error processing VM counters", e); + } + } + }); + } + return true; + } + + protected void processCounters(AutoScaleVmGroup asGroup, VirtualMachineAddress virtualMachine, Map counters){ + Map metrics = snmpClient.read(virtualMachine.getIpAddress(), counters); + if(metrics != null){ + List messages = this.createLogStashMessage(asGroup, metrics, virtualMachine); + for(String message : messages){ + boolean success = logStashClient.send(message); + if(!success){ + s_logger.error("Error sending message to LogStash: " + message); + } + } + } + } + + protected List createLogStashMessage(AutoScaleVmGroup asGroup, Map metrics, VirtualMachineAddress virtualMachine){ + List messages = new ArrayList<>(); + for(String metricName : metrics.keySet()){ + Double metricValue = metrics.get(metricName); + JsonObject message = new JsonObject(); + message.addProperty("client", "cloudstack"); + message.addProperty("autoScaleGroupUuid", asGroup.getUuid()); + message.addProperty("hostname", virtualMachine.getHostName()); + message.addProperty("metric", metricName); + message.addProperty("value", metricValue); + message.addProperty("count", 1); + messages.add(message.toString()); + } + return messages; + } + + @Override + public String getConfigComponentName() { + return AutoScaleCounterProcessorImpl.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ ThreadPoolSize }; + } +} diff --git a/server/src/com/cloud/network/as/AutoScaleManager.java b/server/src/com/cloud/network/as/AutoScaleManager.java index 410fb8f923ef..c42dd2ca5d9f 100644 --- a/server/src/com/cloud/network/as/AutoScaleManager.java +++ b/server/src/com/cloud/network/as/AutoScaleManager.java @@ -16,11 +16,20 @@ // under the License. package com.cloud.network.as; +import com.cloud.tags.ResourceTagVO; + +import java.util.List; +import java.util.Map; + public interface AutoScaleManager extends AutoScaleService { void cleanUpAutoScaleResources(Long accountId); void doScaleUp(long groupId, Integer numVm); - void doScaleDown(long groupId); + void doScaleDown(long groupId, Integer numVm); + + void applyTagToAutoScaleGroupVm(Long autoscaleGroupId, ResourceTagVO resourceTag); + + void deleteTagsFromAutoScaleGroupVms(List resourceIds, Map tags); } diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java index 0d5da2ff5b49..61f34e4e3fc4 100644 --- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java @@ -21,14 +21,29 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.Arrays; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import com.cloud.event.ActionEventUtils; +import com.cloud.network.as.dao.AutoScaleVmProfileNetworkMapDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.server.ResourceTag; +import com.cloud.server.TaggedResourceService; +import com.cloud.tags.ResourceTagVO; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import com.google.gson.Gson; @@ -91,9 +106,14 @@ import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.LoadBalancerNetworkMapVO; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.LoadBalancerNetworkMapDao; +import com.cloud.vm.dao.NicDao; +import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.network.rules.LoadBalancer; import com.cloud.offering.ServiceOffering; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.template.TemplateManager; @@ -123,10 +143,12 @@ import com.cloud.utils.net.NetUtils; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmService; +import com.cloud.vm.Nic; -public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManager, AutoScaleService { + +public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManager, AutoScaleService, Configurable { private static final Logger s_logger = Logger.getLogger(AutoScaleManagerImpl.class); - private ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1); + protected ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1); @Inject protected DispatchChainFactory dispatchChainFactory = null; @@ -155,6 +177,8 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScale @Inject AutoScaleVmProfileDao _autoScaleVmProfileDao; @Inject + AutoScaleVmProfileNetworkMapDao _autoScaleVmProfileNetworkMapDao; + @Inject AutoScalePolicyDao _autoScalePolicyDao; @Inject AutoScalePolicyConditionMapDao _autoScalePolicyConditionMapDao; @@ -181,7 +205,22 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScale @Inject LoadBalancerVMMapDao _lbVmMapDao; @Inject + NicDao _nicDao; + @Inject + LoadBalancerNetworkMapDao _lbNetMapDao; + @Inject LoadBalancingRulesService _loadBalancingRulesService; + @Inject + ResourceTagDao _resourceTagDao; + @Inject + UserVmDao _userVmDao; + @Inject + TaggedResourceService _taggedResourceService; + + private static final int MAX_HTTP_POST_LENGTH = 16 * 2048; + + private static final ConfigKey AutoScaledVmPrefix = new ConfigKey("Advanced", String.class, "autoscale.vm.prefix", "as-vm-", + "Auto scaled virtual machine name prefix", true, ConfigKey.Scope.Global); public List getSupportedAutoScaleCounters(long networkid) { String capability = _lbRulesMgr.getLBCapability(networkid, Capability.AutoScaleCounters.getName()); @@ -294,7 +333,7 @@ private List getAutoScalePolicies(String paramName, List networkIds, final boolean removeNetworks) { long templateId = vmProfile.getTemplateId(); long autoscaleUserId = vmProfile.getAutoScaleUserId(); int destroyVmGraceperiod = vmProfile.getDestroyVmGraceperiod(); @@ -310,9 +349,11 @@ protected AutoScaleVmProfileVO checkValidityAndPersist(AutoScaleVmProfileVO vmPr } User user = _userDao.findById(autoscaleUserId); - if (user.getAccountId() != vmProfile.getAccountId()) { + //Validate auto scale user against the vmprofile account is not needed anymore, + //because the profile may be associated with the project account not an user account. + /*if (user.getAccountId() != vmProfile.getAccountId()) { throw new InvalidParameterValueException("AutoScale User id does not belong to the same account"); - } + }*/ String apiKey = user.getApiKey(); String secretKey = user.getSecretKey(); @@ -330,9 +371,65 @@ protected AutoScaleVmProfileVO checkValidityAndPersist(AutoScaleVmProfileVO vmPr throw new InvalidParameterValueException("Global setting endpointe.url has to be set to the Management Server's API end point"); } - vmProfile = _autoScaleVmProfileDao.persist(vmProfile); + validateUserData(vmProfile.getUserData()); - return vmProfile; + return Transaction.execute(new TransactionCallback() { + + @Override + public AutoScaleVmProfileVO doInTransaction(TransactionStatus status) { + AutoScaleVmProfileVO newVmProfile = _autoScaleVmProfileDao.persist(vmProfile); + + if (networkIds != null && !networkIds.isEmpty()) { + SearchBuilder networkSearch = _networkDao.createSearchBuilder(); + networkSearch.and("ids", networkSearch.entity().getId(), Op.IN); + networkSearch.done(); + SearchCriteria sc = networkSearch.create(); + + sc.setParameters("ids", networkIds.toArray(new Object[0])); + List networks = _networkDao.search(sc, null); + + ControlledEntity[] sameOwnerEntities = networks.toArray(new ControlledEntity[networks.size() + 1]); + sameOwnerEntities[sameOwnerEntities.length - 1] = newVmProfile; + _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, sameOwnerEntities); + + if (networkIds.size() != networks.size()) { + throw new InvalidParameterValueException("Unable to find the network specified"); + } + + /* For update case remove the existing mappings and create fresh ones */ + _autoScaleVmProfileNetworkMapDao.removeByVmProfileId(newVmProfile.getId()); + + Set networkSet = new LinkedHashSet<>(networkIds); //convert to set to remove duplicates + for (Long networkId : networkSet) { + _autoScaleVmProfileNetworkMapDao.persist(new AutoScaleVmProfileNetworkMapVO(newVmProfile.getId(), networkId)); + } + }else if(removeNetworks){ + _autoScaleVmProfileNetworkMapDao.removeByVmProfileId(newVmProfile.getId()); + } + + return newVmProfile; + } + }); + } + + protected void validateUserData(String userData) { + byte[] decodedUserData; + if (userData != null) { + if (!Base64.isBase64(userData)) { + throw new InvalidParameterValueException("User data is not base64 encoded"); + } + if (userData.length() >= MAX_HTTP_POST_LENGTH) { + throw new InvalidParameterValueException("User data is too long for an http POST request"); + } + decodedUserData = Base64.decodeBase64(userData.getBytes()); + if (decodedUserData.length > MAX_HTTP_POST_LENGTH) { + throw new InvalidParameterValueException("User data is too long for POST request"); + } + + if (decodedUserData.length < 1) { + throw new InvalidParameterValueException("User data is too short"); + } + } } @Override @@ -373,13 +470,13 @@ public AutoScaleVmProfile createAutoScaleVmProfile(CreateAutoScaleVmProfileCmd c AutoScaleVmProfileVO profileVO = new AutoScaleVmProfileVO(cmd.getZoneId(), cmd.getDomainId(), cmd.getAccountId(), cmd.getServiceOfferingId(), cmd.getTemplateId(), cmd.getOtherDeployParams(), - cmd.getCounterParamList(), cmd.getDestroyVmGraceperiod(), autoscaleUserId); + cmd.getCounterParamList(), cmd.getDestroyVmGraceperiod(), autoscaleUserId, cmd.getUserData()); if (cmd.getDisplay() != null) { profileVO.setDisplay(cmd.getDisplay()); } - profileVO = checkValidityAndPersist(profileVO); + profileVO = checkValidityAndPersist(profileVO, cmd.getNetworkIds(), cmd.isRemoveNetworks()); s_logger.info("Successfully create AutoScale Vm Profile with Id: " + profileVO.getId()); return profileVO; @@ -423,6 +520,10 @@ public AutoScaleVmProfile updateAutoScaleVmProfile(UpdateAutoScaleVmProfileCmd c vmProfile.setDisplay(cmd.getDisplay()); } + if(cmd.getUserData() != null){ + vmProfile.setUserData(cmd.getUserData()); + } + List vmGroupList = _autoScaleVmGroupDao.listByAll(null, profileId); for (AutoScaleVmGroupVO vmGroupVO : vmGroupList) { if (physicalParameterUpdate && !vmGroupVO.getState().equals(AutoScaleVmGroup.State_Disabled)) { @@ -430,7 +531,7 @@ public AutoScaleVmProfile updateAutoScaleVmProfile(UpdateAutoScaleVmProfileCmd c } } - vmProfile = checkValidityAndPersist(vmProfile); + vmProfile = checkValidityAndPersist(vmProfile, cmd.getNetworkIds(), cmd.isRemoveNetworks()); s_logger.info("Updated Auto Scale Vm Profile id:" + vmProfile.getId()); return vmProfile; @@ -458,6 +559,7 @@ public List listAutoScaleVmProfiles(ListAutoScaleV String otherDeployParams = cmd.getOtherDeployParams(); Long serviceOffId = cmd.getServiceOfferingId(); Long zoneId = cmd.getZoneId(); + Long projectId = cmd.getProjectId(); Boolean display = cmd.getDisplay(); SearchWrapper searchWrapper = new SearchWrapper(_autoScaleVmProfileDao, AutoScaleVmProfileVO.class, cmd, cmd.getId()); @@ -489,6 +591,10 @@ public List listAutoScaleVmProfiles(ListAutoScaleV sc.setParameters("zoneId", zoneId); } + if (projectId != null) { + sc.setParameters("accountIdIN", projectId); + } + if (display != null) { sc.setParameters("display", display); } @@ -562,6 +668,14 @@ public AutoScalePolicy createAutoScalePolicy(CreateAutoScalePolicyCmd cmd) { int duration = cmd.getDuration(); Integer quietTime = cmd.getQuietTime(); String action = cmd.getAction(); + Integer step = cmd.getStep(); + AutoScalePolicy.LogicalOperator operator; + + try { + operator = AutoScalePolicy.LogicalOperator.valueOf(cmd.getLogicalOperator()); + } catch (IllegalArgumentException ex) { + throw new InvalidParameterValueException("The logical Operator " + cmd.getLogicalOperator() + " does not exist; Unable to create policy."); + } if (quietTime == null) { quietTime = NetUtils.DEFAULT_AUTOSCALE_POLICY_QUIET_TIME; @@ -572,7 +686,7 @@ public AutoScalePolicy createAutoScalePolicy(CreateAutoScalePolicyCmd cmd) { throw new InvalidParameterValueException("action is invalid, only 'scaleup' and 'scaledown' is supported"); } - AutoScalePolicyVO policyVO = new AutoScalePolicyVO(cmd.getDomainId(), cmd.getAccountId(), duration, quietTime, null, action); + AutoScalePolicyVO policyVO = new AutoScalePolicyVO(cmd.getDomainId(), cmd.getAccountId(), duration, quietTime, null, action, step, operator); policyVO = checkValidityAndPersist(policyVO, cmd.getConditionIds()); s_logger.info("Successfully created AutoScale Policy with Id: " + policyVO.getId()); @@ -678,6 +792,7 @@ public List listAutoScalePolicies(ListAutoScalePolici Long conditionId = cmd.getConditionId(); String action = cmd.getAction(); Long vmGroupId = cmd.getVmGroupId(); + Long projectId = cmd.getProjectId(); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("action", sb.entity().getAction(), SearchCriteria.Op.EQ); @@ -704,6 +819,10 @@ public List listAutoScalePolicies(ListAutoScalePolici sc.setParameters("action", action); } + if (projectId != null) { + sc.setParameters("accountIdIN", projectId); + } + if (conditionId != null) { sc.setJoinParameters("asPolicyConditionSearch", "conditionId", conditionId); } @@ -721,7 +840,16 @@ public AutoScalePolicy updateAutoScalePolicy(UpdateAutoScalePolicyCmd cmd) { Long policyId = cmd.getId(); Integer duration = cmd.getDuration(); Integer quietTime = cmd.getQuietTime(); + Integer step = cmd.getStep(); List conditionIds = cmd.getConditionIds(); + AutoScalePolicy.LogicalOperator operator; + + try { + operator = AutoScalePolicy.LogicalOperator.valueOf(cmd.getLogicalOperator()); + } catch (IllegalArgumentException ex) { + throw new InvalidParameterValueException("The logical Operator " + cmd.getLogicalOperator() + " does not exist; Unable to update policy."); + } + AutoScalePolicyVO policy = getEntityInDatabase(CallContext.current().getCallingAccount(), "Auto Scale Policy", policyId, _autoScalePolicyDao); if (duration != null) { @@ -732,6 +860,14 @@ public AutoScalePolicy updateAutoScalePolicy(UpdateAutoScalePolicyCmd cmd) { policy.setQuietTime(quietTime); } + if (step != null) { + policy.setStep(step); + } + + if(operator != null){ + policy.setLogicalOperator(operator); + } + List vmGroupPolicyList = _autoScaleVmGroupPolicyMapDao.listByPolicyId(policyId); for (AutoScaleVmGroupPolicyMapVO vmGroupPolicy : vmGroupPolicyList) { AutoScaleVmGroupVO vmGroupVO = _autoScaleVmGroupDao.findById(vmGroupPolicy.getVmGroupId()); @@ -774,13 +910,13 @@ public AutoScaleVmGroup createAutoScaleVmGroup(CreateAutoScaleVmGroupCmd cmd) { throw new InvalidParameterValueException("an AutoScaleVmGroup is already attached to the lb rule, the existing vm group has to be first deleted"); } - if (_lb2VmMapDao.isVmAttachedToLoadBalancer(loadBalancer.getId())) { - throw new InvalidParameterValueException( - "there are Vms already bound to the specified LoadBalancing Rule. User bound Vms and AutoScaled Vm Group cannot co-exist on a Load Balancing Rule"); - } +// if (_lb2VmMapDao.isVmAttachedToLoadBalancer(loadBalancer.getId())) { +// throw new InvalidParameterValueException( +// "there are Vms already bound to the specified LoadBalancing Rule. User bound Vms and AutoScaled Vm Group cannot co-exist on a Load Balancing Rule"); +// } AutoScaleVmGroupVO vmGroupVO = new AutoScaleVmGroupVO(cmd.getLbRuleId(), zoneId, loadBalancer.getDomainId(), loadBalancer.getAccountId(), minMembers, maxMembers, - loadBalancer.getDefaultPortStart(), interval, null, cmd.getProfileId(), AutoScaleVmGroup.State_New); + loadBalancer.getDefaultPortStart(), interval, null, cmd.getProfileId(), AutoScaleVmGroup.State_New, cmd.getVmPrefixName()); if (forDisplay != null) { vmGroupVO.setDisplay(forDisplay); @@ -795,7 +931,7 @@ public AutoScaleVmGroup createAutoScaleVmGroup(CreateAutoScaleVmGroupCmd cmd) { @Override @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_CREATE, eventDescription = "creating autoscale vm group", async = true) public boolean configureAutoScaleVmGroup(CreateAutoScaleVmGroupCmd cmd) throws ResourceUnavailableException { - return configureAutoScaleVmGroup(cmd.getEntityId(), AutoScaleVmGroup.State_New); + return configureAutoScaleVmGroup(cmd.getEntityId(), AutoScaleVmGroup.State_Disabled); } public boolean isLoadBalancerBasedAutoScaleVmGroup(AutoScaleVmGroup vmGroup) { @@ -847,6 +983,65 @@ public boolean deleteAutoScaleVmGroup(final long id) { } } + //removing VMS before removing auto scale group + this.destroyVmGroupVMs(id); + + return Transaction.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(TransactionStatus status) { + boolean success = _autoScaleVmGroupDao.remove(id); + + if (!success) { + s_logger.warn("Failed to remove AutoScale Group db object"); + return false; + } + + success = _autoScaleVmGroupPolicyMapDao.removeByGroupId(id); + if (!success) { + s_logger.warn("Failed to remove AutoScale Group Policy mappings"); + return false; + } + + s_logger.info("Successfully deleted autoscale vm group id : " + id); + return success; // Successfull + } + }); + + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_DELETE, eventDescription = "deleting autoscale vm group with dependencies") + public boolean deleteAutoScaleVmGroupWithDependencies(final long id) { + AutoScaleVmGroupVO autoScaleVmGroupVO = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", id, _autoScaleVmGroupDao); + + if (autoScaleVmGroupVO.getState().equals(AutoScaleVmGroup.State_New)) { + /* This condition is for handling failures during creation command */ + return _autoScaleVmGroupDao.remove(id); + } + String bakupState = autoScaleVmGroupVO.getState(); + autoScaleVmGroupVO.setState(AutoScaleVmGroup.State_Revoke); + _autoScaleVmGroupDao.persist(autoScaleVmGroupVO); + boolean success = false; + + try { + success = configureAutoScaleVmGroup(id, bakupState); + } catch (ResourceUnavailableException e) { + autoScaleVmGroupVO.setState(bakupState); + _autoScaleVmGroupDao.persist(autoScaleVmGroupVO); + } finally { + if (!success) { + s_logger.warn("Could not delete AutoScale Vm Group id : " + id); + return false; + } + } + + //removing VMS before removing auto scale group + this.destroyVmGroupVMs(id); + + // Set up objects to be deleted + final Long vmProfileId = autoScaleVmGroupVO.getProfileId(); + return Transaction.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { @@ -857,12 +1052,48 @@ public Boolean doInTransaction(TransactionStatus status) { return false; } + success = deleteAutoScaleVmProfile(vmProfileId); + if (!success) { + s_logger.warn("Failed to remove AutoScale VM Profile associated to Autoscale VM Group"); + return false; + } + + // Save up policies to be deleted + List policiesMapList = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(id); + + // Remove mapping between AutoScale VM Group and policies so policies are not in use anymore success = _autoScaleVmGroupPolicyMapDao.removeByGroupId(id); if (!success) { s_logger.warn("Failed to remove AutoScale Group Policy mappings"); return false; } + for(AutoScaleVmGroupPolicyMapVO policyMapVO : policiesMapList) { + Long policyId = policyMapVO.getPolicyId(); + List conditionsMapList = _autoScalePolicyConditionMapDao.findByPolicyId(policyId); + + success = deleteAutoScalePolicy(policyId); + if (!success) { + s_logger.warn("Failed to remove AutoScale Policy " + policyId); + return false; + } + + for(AutoScalePolicyConditionMapVO conditionMapVO : conditionsMapList) { + Long conditionId = conditionMapVO.getConditionId(); + try { + success = deleteCondition(conditionId); + } catch (ResourceInUseException e) { + s_logger.warn("Failed to remove condition " + conditionId); + return false; + } + + if (!success) { + s_logger.warn("Failed to remove condition " + conditionId); + return false; + } + } + } + s_logger.info("Successfully deleted autoscale vm group id : " + id); return success; // Successfull } @@ -870,6 +1101,18 @@ public Boolean doInTransaction(TransactionStatus status) { } + protected void destroyVmGroupVMs(long groupId) { + AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId); + asGroup.setMinMembers(0); + asGroup.setState(AutoScaleVmGroup.State_Disabled); + _autoScaleVmGroupDao.persist(asGroup); + + for(AutoScaleVmGroupVmMapVO autoScaleVmGroupVmMap : _autoScaleVmGroupVmMapDao.listByGroup(groupId)){ + _autoScaleVmGroupVmMapDao.remove(groupId, autoScaleVmGroupVmMap.getInstanceId()); + destroyVM(autoScaleVmGroupVmMap.getInstanceId()); + } + } + @Override public List listAutoScaleVmGroups(ListAutoScaleVmGroupsCmd cmd) { Long id = cmd.getId(); @@ -877,6 +1120,7 @@ public List listAutoScaleVmGroups(ListAutoScaleVmGro Long loadBalancerId = cmd.getLoadBalancerId(); Long profileId = cmd.getProfileId(); Long zoneId = cmd.getZoneId(); + Long projectId = cmd.getProjectId(); Boolean forDisplay = cmd.getDisplay(); SearchWrapper searchWrapper = new SearchWrapper(_autoScaleVmGroupDao, AutoScaleVmGroupVO.class, cmd, cmd.getId()); @@ -907,6 +1151,9 @@ public List listAutoScaleVmGroups(ListAutoScaleVmGro if (zoneId != null) { sc.setParameters("zoneId", zoneId); } + if (projectId != null) { + sc.setParameters("accountIdIN", projectId); + } if (policyId != null) { sc.setJoinParameters("asVmGroupPolicySearch", "policyId", policyId); } @@ -1170,6 +1417,7 @@ public List listConditions(ListConditionsCmd cmd) { Long id = cmd.getId(); Long counterId = cmd.getCounterId(); Long policyId = cmd.getPolicyId(); + Long projectId = cmd.getProjectId(); SearchWrapper searchWrapper = new SearchWrapper(_conditionDao, ConditionVO.class, cmd, cmd.getId()); SearchBuilder sb = searchWrapper.getSearchBuilder(); if (policyId != null) { @@ -1193,6 +1441,10 @@ public List listConditions(ListConditionsCmd cmd) { sc.setParameters("counterId", counterId); } + if (projectId != null) { + sc.setParameters("accountIdIN", projectId); + } + if (policyId != null) { sc.setJoinParameters("asPolicyConditionSearch", "policyId", policyId); } @@ -1275,7 +1527,7 @@ private boolean checkConditionUp(AutoScaleVmGroupVO asGroup, Integer numVm) { return true; } - private boolean checkConditionDown(AutoScaleVmGroupVO asGroup) { + protected boolean checkConditionDown(AutoScaleVmGroupVO asGroup) { Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId()); Integer minVm = asGroup.getMinMembers(); if (currentVM - 1 < minVm) { @@ -1285,12 +1537,12 @@ private boolean checkConditionDown(AutoScaleVmGroupVO asGroup) { return true; } - private long createNewVM(AutoScaleVmGroupVO asGroup) { + protected UserVm createNewVM(AutoScaleVmGroupVO asGroup) { AutoScaleVmProfileVO profileVo = _autoScaleVmProfileDao.findById(asGroup.getProfileId()); long templateId = profileVo.getTemplateId(); long serviceOfferingId = profileVo.getServiceOfferingId(); if (templateId == -1) { - return -1; + return null; } // create new VM into DB try { @@ -1321,34 +1573,35 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { UserVm vm = null; IpAddresses addrs = new IpAddresses(null, null); + String instanceName = createInstanceName(asGroup); + Map customParameters = new HashMap<>(); + if (zone.getNetworkType() == NetworkType.Basic) { - vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + - getCurrentTimeStampString(), - "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, + vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, instanceName, + instanceName, null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, null, true, null, null, null, null, null, null); } else { if (zone.isSecurityGroupEnabled()) { vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null, - owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), - "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, - null, null, true, null, null, null, null, null, null); + owner, instanceName, + instanceName, null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, + null, null, true, null, null, customParameters, null, null, null); } else { - vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + - getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), - null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null); + List networkIds = new ArrayList<>(); + Long mainNetworkId = getDestinationNetworkId(asGroup); + zone = getZone(mainNetworkId); + networkIds.add(mainNetworkId); + networkIds.addAll(getAdditionalNetWorkIds(profileVo, zone)); + vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, instanceName, instanceName, + null, null, null, template.getHypervisorType(), HTTPMethod.POST, profileVo.getUserData(), null, null, addrs, true, null, null, customParameters, null, null, null); } } - if (vm != null) { - return vm.getId(); - } else { - return -1; - } + return vm; } catch (InsufficientCapacityException ex) { - s_logger.info(ex); - s_logger.trace(ex.getMessage(), ex); + s_logger.warn(ex.getMessage(), ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); } catch (ResourceUnavailableException ex) { s_logger.warn("Exception: ", ex); @@ -1362,6 +1615,88 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { } } + protected String createInstanceName(AutoScaleVmGroupVO asGroup) { + String vmPrefixName = asGroup.getVmPrefixName(); + if (vmPrefixName.isEmpty() || vmPrefixName.equals("")){ + return AutoScaledVmPrefix.value() + asGroup.getId() + "-" + getCurrentTimeStampString(); + } + return vmPrefixName + asGroup.getId() + "-" + getCurrentTimeStampString(); + } + + private List getAdditionalNetWorkIds(AutoScaleVmProfileVO profileVo, DataCenter zone) { + List networkIds = new ArrayList<>(); + for(AutoScaleVmProfileNetworkMapVO asProfileNetMap : _autoScaleVmProfileNetworkMapDao.listByVmProfileId(profileVo.getId())){ + NetworkVO network = _networkDao.findById(asProfileNetMap.getNetworkId()); + if(network.getDataCenterId() == zone.getId()) { + networkIds.add(asProfileNetMap.getNetworkId()); + } + } + return networkIds; + } + + private DataCenter getZone(Long mainNetworkId) { + return _entityMgr.findById(DataCenter.class, _networkDao.findById(mainNetworkId).getDataCenterId()); + } + + protected Long getDestinationNetworkId(AutoScaleVmGroup asGroup){ + LoadBalancer lb = _loadBalancingRulesService.findById(asGroup.getLoadBalancerId()); + List lbNetMapList = _lbNetMapDao.listByLoadBalancerId(lb.getId()); + + Map networkInstanceCount = new HashMap<>(); + networkInstanceCount.put(lb.getNetworkId(), 0); + for (LoadBalancerNetworkMapVO lbNetMap : lbNetMapList) { + networkInstanceCount.put(lbNetMap.getNetworkId(), 0); + } + + for(LoadBalancingRule.LbDestination destination : getExistingDestinations(lb, lbNetMapList)){ + Integer currentCount = networkInstanceCount.get(destination.getNetworkId()); + networkInstanceCount.put(destination.getNetworkId(), currentCount + 1); + } + + Map.Entry chosenNetworkId = null; + for (Map.Entry entry : networkInstanceCount.entrySet()) { + if (chosenNetworkId == null || chosenNetworkId.getValue() > entry.getValue()) { + chosenNetworkId = entry; + } + } + if(chosenNetworkId == null){ + return lb.getNetworkId(); + }else{ + return chosenNetworkId.getKey(); + } + } + + private List getExistingDestinations(LoadBalancer lb, List lbNetMapList) { + List dstList = new ArrayList<>(); + List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId()); + + for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { + Nic nic = this.getLbInstanceNic(lb, lbVmMap.getInstanceId(), lbNetMapList); + LoadBalancingRule.LbDestination lbDst = new LoadBalancingRule.LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), null, nic.getNetworkId(), lbVmMap.getInstanceId(), lbVmMap.isRevoke()); + AutoScaleVmGroupVmMapVO autoScaleVmMap = _autoScaleVmGroupVmMapDao.findByVmId(lbVmMap.getInstanceId()); + if(autoScaleVmMap != null){ + dstList.add(lbDst); + } + + } + + return dstList; + } + + private Nic getLbInstanceNic(LoadBalancer lb, long vmId, List lbNetMapList) { + Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vmId); + if (nic != null) { + return nic; + } + for (LoadBalancerNetworkMapVO lbNetMap : lbNetMapList) { + nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lbNetMap.getNetworkId(), vmId); + if (nic != null) { + return nic; + } + } + return null; + } + private String getCurrentTimeStampString() { Date current = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); @@ -1369,7 +1704,7 @@ private String getCurrentTimeStampString() { return sdf.format(current); } - private boolean startNewVM(long vmId) { + protected void startNewVM(long vmId) { try { CallContext.current().setEventDetails("Vm Id: " + vmId); _userVmManager.startVirtualMachine(vmId, null, null, null); @@ -1386,39 +1721,41 @@ private boolean startNewVM(long vmId) { message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); } } - s_logger.info(ex); s_logger.info(message.toString(), ex); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); } - return true; } - private boolean assignLBruleToNewVm(long vmId, AutoScaleVmGroupVO asGroup) { - List lstVmId = new ArrayList(); + protected boolean assignLBruleToNewVm(UserVm vm, AutoScaleVmGroupVO asGroup) { long lbId = asGroup.getLoadBalancerId(); List LbVmMapVos = _lbVmMapDao.listByLoadBalancerId(lbId); if ((LbVmMapVos != null) && (LbVmMapVos.size() > 0)) { for (LoadBalancerVMMapVO LbVmMapVo : LbVmMapVos) { long instanceId = LbVmMapVo.getInstanceId(); - if (instanceId == vmId) { + if (instanceId == vm.getId()) { s_logger.warn("the new VM is already mapped to LB rule. What's wrong?"); return true; } } } - lstVmId.add(new Long(vmId)); - return _loadBalancingRulesService.assignToLoadBalancer(lbId, lstVmId, new HashMap>()); - + try { + return _loadBalancingRulesService.assignToLoadBalancer(lbId, Arrays.asList(new Long(vm.getId())), new HashMap>()); + }catch(Exception e){ + // Rolling back VM creation as it cannot be assigned to the load balancer + destroyVM(vm.getId()); + return false; + } } - private long removeLBrule(AutoScaleVmGroupVO asGroup) { + protected long removeLBrule(AutoScaleVmGroupVO asGroup) { long lbId = asGroup.getLoadBalancerId(); + long asGroupId = asGroup.getId(); long instanceId = -1; - List LbVmMapVos = _lbVmMapDao.listByLoadBalancerId(lbId); - if ((LbVmMapVos != null) && (LbVmMapVos.size() > 0)) { - for (LoadBalancerVMMapVO LbVmMapVo : LbVmMapVos) { - instanceId = LbVmMapVo.getInstanceId(); + List autoScaleVmGroupVmMapVos = _autoScaleVmGroupVmMapDao.listByGroup(asGroupId); + if ((autoScaleVmGroupVmMapVos != null) && (autoScaleVmGroupVmMapVos.size() > 0)) { + for (AutoScaleVmGroupVmMapVO autoScaleVmGroupVmMapVO : autoScaleVmGroupVmMapVos) { + instanceId = autoScaleVmGroupVmMapVO.getInstanceId(); } } // take last VM out of the list @@ -1431,102 +1768,208 @@ private long removeLBrule(AutoScaleVmGroupVO asGroup) { return -1; } - @Override - public void doScaleUp(long groupId, Integer numVm) { + public void applyTagToAutoScaleGroupVm(Long groupId, ResourceTagVO resourceTagVO) { AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId); - if (asGroup == null) { - s_logger.error("Can not find the groupid " + groupId + " for scaling up"); - return; - } - if (!checkConditionUp(asGroup, numVm)) { - return; - } - for (int i = 0; i < numVm; i++) { - long vmId = createNewVM(asGroup); - if (vmId == -1) { - s_logger.error("Can not deploy new VM for scaling up in the group " - + asGroup.getId() + ". Waiting for next round"); - break; + if (asGroup != null) { + List autoScaleVmGroupVmMapVOs = _autoScaleVmGroupVmMapDao.listByGroup(asGroup.getId()); + List resourceIds = new ArrayList(); + Map tags = new HashMap(); + for(AutoScaleVmGroupVmMapVO autoscaleVmGroupVmMapVO: autoScaleVmGroupVmMapVOs) { + UserVmVO userVmVO = _userVmDao.findById(autoscaleVmGroupVmMapVO.getInstanceId()); + if (userVmVO != null) { + resourceIds.add(String.valueOf(userVmVO.getId())); + } } - if (startNewVM(vmId)) { - if (assignLBruleToNewVm(vmId, asGroup)) { - // persist to DB - AutoScaleVmGroupVmMapVO GroupVmVO = new AutoScaleVmGroupVmMapVO( - asGroup.getId(), vmId); - _autoScaleVmGroupVmMapDao.persist(GroupVmVO); - // update last_quiettime - List GroupPolicyVOs = _autoScaleVmGroupPolicyMapDao - .listByVmGroupId(groupId); - for (AutoScaleVmGroupPolicyMapVO GroupPolicyVO : GroupPolicyVOs) { - AutoScalePolicyVO vo = _autoScalePolicyDao - .findById(GroupPolicyVO.getPolicyId()); - if (vo.getAction().equals("scaleup")) { - vo.setLastQuiteTime(new Date()); - _autoScalePolicyDao.persist(vo); - break; - } + tags.put(resourceTagVO.getKey(), resourceTagVO.getValue()); + _taggedResourceService.createTags(resourceIds, ResourceTag.ResourceObjectType.UserVm, tags, resourceTagVO.getCustomer()); + } + } + + @Override + public void deleteTagsFromAutoScaleGroupVms(List resourceIds, Map tags) { + for (String resourceUuid: resourceIds) { + AutoScaleVmGroupVO autoscaleVmGroupVO = _autoScaleVmGroupDao.findByUuid(resourceUuid); + if (autoscaleVmGroupVO != null) { + List autoScaleVmGroupVmMapVOs = _autoScaleVmGroupVmMapDao.listByGroup(autoscaleVmGroupVO.getId()); + for (AutoScaleVmGroupVmMapVO autoScaleVmGroupVmMapVO: autoScaleVmGroupVmMapVOs) { + List vmResourceIds = new ArrayList(); + vmResourceIds.add(String.valueOf(autoScaleVmGroupVmMapVO.getInstanceId())); + try { + _taggedResourceService.deleteTags(vmResourceIds, ResourceTag.ResourceObjectType.UserVm, tags); + } catch(InvalidParameterValueException ex) { + String message = ex.getMessage(); + s_logger.info(message + " Tags were already removed for the resource: "+vmResourceIds.get(0)); } - } else { - s_logger.error("Can not assign LB rule for this new VM"); - break; } - } else { - s_logger.error("Can not deploy new VM for scaling up in the group " - + asGroup.getId() + ". Waiting for next round"); - break; } } } @Override - public void doScaleDown(final long groupId) { + public void doScaleUp(long groupId, Integer numVm) { AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId); if (asGroup == null) { s_logger.error("Can not find the groupid " + groupId + " for scaling up"); return; } - if (!checkConditionDown(asGroup)) { - return; - } - final long vmId = removeLBrule(asGroup); - if (vmId != -1) { - long profileId = asGroup.getProfileId(); - - // update group-vm mapping - _autoScaleVmGroupVmMapDao.remove(groupId, vmId); - // update last_quiettime - List GroupPolicyVOs = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(groupId); - for (AutoScaleVmGroupPolicyMapVO GroupPolicyVO : GroupPolicyVOs) { - AutoScalePolicyVO vo = _autoScalePolicyDao.findById(GroupPolicyVO.getPolicyId()); - if (vo.getAction().equals("scaledown")) { - vo.setLastQuiteTime(new Date()); - _autoScalePolicyDao.persist(vo); + + try { + for (int i = 0; i < numVm; i++) { + if (!checkConditionUp(asGroup, numVm)) { + return; + } + + UserVm vm = createNewVM(asGroup); + if (vm == null) { + s_logger.error("Can not deploy new VM for scaling up in the group " + asGroup.getId() + ". Waiting for next round"); + break; + } + + addVirtualMachineTags(asGroup, vm); + try { + startNewVM(vm.getId()); + } catch(Exception ex){ + destroyVM(vm.getId()); + throw ex; + } + + if (assignLBruleToNewVm(vm, asGroup)) { + _autoScaleVmGroupVmMapDao.persist(new AutoScaleVmGroupVmMapVO(asGroup.getId(), vm.getId())); + updateLastQuietTime(groupId, "scaleup"); + createScaleUpSuccessEvent(asGroup.getUuid(), "VM " + vm.getDisplayName() + " was created as result of a scale up action. Auto Scale Id: " + asGroup.getUuid()); + } else { + createScaleUpFailedEvent(asGroup.getUuid(), "VM could not be assigned to LB for Auto Scale Id: " + asGroup.getUuid()); + s_logger.error("Can not assign LB rule for this new VM"); break; } } + } catch(ServerApiException ex) { + String message = "It was not possible do start VM for Auto Scale Id: " + asGroup.getUuid() + " Reason: " + ex.getDescription(); + createScaleUpFailedEvent(asGroup.getUuid(), message); + s_logger.error(message, ex); + throw ex; + } catch(Exception ex){ + String message = "It was not possible do start VM for Auto Scale Id: " + asGroup.getUuid() + " Reason: unexpected error during execution"; + createScaleUpFailedEvent(asGroup.getUuid(), message); + s_logger.error(message, ex); + throw ex; + } + } - // get destroyvmgrace param - AutoScaleVmProfileVO asProfile = _autoScaleVmProfileDao.findById(profileId); - Integer destroyVmGracePeriod = asProfile.getDestroyVmGraceperiod(); - if (destroyVmGracePeriod >= 0) { - _executor.schedule(new Runnable() { - @Override - public void run() { - try { + private void addVirtualMachineTags(AutoScaleVmGroupVO asGroup, UserVm vm) { + List resourceTags = _resourceTagDao.listBy(asGroup.getId(), ResourceTag.ResourceObjectType.AutoScaleVmGroup); - _userVmManager.destroyVm(vmId, false); + for (ResourceTag resourceTag : resourceTags) { + ResourceTagVO tag = new ResourceTagVO(resourceTag.getKey(), resourceTag.getValue(), resourceTag.getAccountId(), + resourceTag.getDomainId(), vm.getId(), ResourceTag.ResourceObjectType.UserVm, resourceTag.getCustomer(), + vm.getUuid()); + _resourceTagDao.persist(tag); + } + } - } catch (ResourceUnavailableException e) { - e.printStackTrace(); - } catch (ConcurrentOperationException e) { - e.printStackTrace(); - } + @Override + public void doScaleDown(final long groupId, Integer numVm) { + final AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId); + for(int i = 0; i < numVm; i++) { + try{ + if (asGroup == null) { + s_logger.error("Can not find the groupid " + groupId + " for scaling up"); + return; + } + if (!checkConditionDown(asGroup)) { + return; + } + final long vmId = removeLBrule(asGroup); + if (vmId != -1) { + _autoScaleVmGroupVmMapDao.remove(groupId, vmId); + + updateLastQuietTime(groupId, "scaledown"); + + Integer destroyVmGracePeriod = getDestroyVmGracePeriod(asGroup); + if (destroyVmGracePeriod >= 0) { + _executor.schedule(new Runnable() { + @Override + public void run() { + if(destroyVM(vmId)){ + createScaleDownSuccessEvent(asGroup.getUuid(), "VM was destroyed as result of a scale down action. Auto Scale Id: " + groupId); + }else{ + createScaleDownFailedEvent(asGroup.getUuid(), "Scale down action failed for Auto Scale group id: " + groupId); + } + } + }, destroyVmGracePeriod, TimeUnit.SECONDS); } - }, destroyVmGracePeriod, TimeUnit.SECONDS); + } else { + String message = "Failed to remove LB rule for the VM being destroyed."; + createScaleDownFailedEvent(asGroup.getUuid(), "Scale down action failed for Auto Scale group id: " + groupId + ". Reason: " + message); + s_logger.error(message); + } + }catch(Exception ex){ + String message = "Scale down action failed for Auto Scale group id: " + groupId; + createScaleDownFailedEvent(asGroup.getUuid(), message); + s_logger.error(message, ex); + throw ex; } - } else { - s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more."); } } + private Integer getDestroyVmGracePeriod(AutoScaleVmGroupVO asGroup) { + AutoScaleVmProfileVO asProfile = _autoScaleVmProfileDao.findById(asGroup.getProfileId()); + return asProfile.getDestroyVmGraceperiod(); + } + + protected void updateLastQuietTime(long groupId, String action) { + for (AutoScaleVmGroupPolicyMapVO groupPolicyMap : _autoScaleVmGroupPolicyMapDao.listByVmGroupId(groupId)) { + AutoScalePolicyVO policy = _autoScalePolicyDao.findById(groupPolicyMap.getPolicyId()); + if (policy.getAction().equals(action)) { + policy.setLastQuiteTime(new Date()); + _autoScalePolicyDao.persist(policy); + break; + } + } + } + + private boolean destroyVM(long vmId) { + try { + _userVmManager.destroyVm(vmId, true); + return true; + } catch (ResourceUnavailableException | ConcurrentOperationException e) { + s_logger.error("It was not possible to destroy VM id: " + vmId, e); + } + return false; + } + + public void createScaleUpSuccessEvent(String asGroupId, String description){ + createEvent(asGroupId, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP, description); + } + + public void createScaleUpFailedEvent(String asGroupId, String description){ + createEvent(asGroupId, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED, description); + } + + public void createScaleDownSuccessEvent(String asGroupId, String description){ + createEvent(asGroupId, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, description); + } + + public void createScaleDownFailedEvent(String asGroupId, String description){ + createEvent(asGroupId, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED, description); + } + + protected void createEvent(String asGroupId, String eventType, String description) { + CallContext callContext = CallContext.current(); + AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findByUuid(asGroupId); + AutoScaleVmProfileVO profile = _autoScaleVmProfileDao.findById(asGroup.getProfileId()); + User user = _userDao.getUser(profile.getAutoScaleUserId()); + callContext.getContextParameters().put(AutoScaleVmGroup.class.getName(), asGroupId); + ActionEventUtils.onActionEvent(user.getId(), user.getAccountId(), asGroup.getDomainId(), eventType, description); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { AutoScaledVmPrefix }; + } + + @Override + public String getConfigComponentName() { + return AutoScaleManagerImpl.class.getSimpleName(); + } } diff --git a/server/src/com/cloud/network/as/AutoScaleStatsCollector.java b/server/src/com/cloud/network/as/AutoScaleStatsCollector.java new file mode 100644 index 000000000000..a54bb63eb0e7 --- /dev/null +++ b/server/src/com/cloud/network/as/AutoScaleStatsCollector.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.network.as; + +import com.cloud.agent.AgentManager; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.AutoScaleVmProfileDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.utils.Pair; +import com.cloud.vm.VMInstanceVO; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Map; +import java.util.List; + +public abstract class AutoScaleStatsCollector { + + @Inject + protected AgentManager _agentMgr; + @Inject + protected AutoScaleVmGroupVmMapDao _asGroupVmDao; + @Inject + protected AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao; + @Inject + protected AutoScalePolicyDao _asPolicyDao; + @Inject + protected AutoScalePolicyConditionMapDao _asConditionMapDao; + @Inject + protected ConditionDao _asConditionDao; + @Inject + protected CounterDao _asCounterDao; + @Inject + protected AutoScaleVmProfileDao _asProfileDao; + @Inject + protected ServiceOfferingDao _serviceOfferingDao; + + /** + * @return Map - Map containing the sum of each counter. the key is the counter name and the value the respective value + */ + public abstract Map retrieveMetrics(AutoScaleVmGroup asGroup, List vmList); + + protected List> getPairOfCounterNameAndDuration(AutoScaleVmGroup groupVo) { + if (groupVo == null) + return null; + + List> result = new ArrayList<>(); + //list policy map + List groupPolicyMap = _asGroupPolicyDao.listByVmGroupId(groupVo.getId()); + if (groupPolicyMap == null) + return null; + + List countersAdded = new ArrayList<>(); + for (AutoScaleVmGroupPolicyMapVO gpMap : groupPolicyMap) { + //get duration + AutoScalePolicyVO policyVo = _asPolicyDao.findById(gpMap.getPolicyId()); + Integer duration = policyVo.getDuration(); + //get collection of counter name + List policyConditionMap = _asConditionMapDao.findByPolicyId(policyVo.getId()); + for (AutoScalePolicyConditionMapVO pcMap : policyConditionMap) { + String counterName = getCounterNameByCondition(pcMap.getConditionId()); + //avoid the same counter being added more than once + if(!countersAdded.contains(counterName)){ + countersAdded.add(counterName); + counterName += "," + pcMap.getConditionId(); + result.add(new Pair<>(counterName, duration)); + } + } + } + + return result; + } + + protected String getCounterNameByCondition(long conditionId) { + ConditionVO condition = _asConditionDao.findById(conditionId); + if (condition == null) + return ""; + + long counterId = condition.getCounterid(); + CounterVO counter = _asCounterDao.findById(counterId); + if (counter == null) + return ""; + + return counter.getSource().toString(); + } +} diff --git a/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactory.java b/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactory.java new file mode 100644 index 000000000000..c2bd712ae654 --- /dev/null +++ b/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactory.java @@ -0,0 +1,6 @@ +package com.cloud.network.as; + +public interface AutoScaleStatsCollectorFactory { + + public AutoScaleStatsCollector getStatsCollector(); +} diff --git a/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactoryImpl.java b/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactoryImpl.java new file mode 100644 index 000000000000..e5cb8d7ae72d --- /dev/null +++ b/server/src/com/cloud/network/as/AutoScaleStatsCollectorFactoryImpl.java @@ -0,0 +1,40 @@ +package com.cloud.network.as; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; + +import javax.inject.Inject; + +public class AutoScaleStatsCollectorFactoryImpl implements AutoScaleStatsCollectorFactory, Configurable { + + @Inject + RRDAutoScaleStatsCollector rrdAutoScaleStatsCollector; + @Inject + ElasticSearchAutoScaleStatsCollector elasticSearchAutoScaleStatsCollector; + + private static final String RRD = "rrd"; + private static final String ELASTIC_SEARCH = "elasticsearch"; + + private static final ConfigKey StatsDataSource = new ConfigKey("Advanced", String.class, "autoscale.stats.datasource", "rrd", + "Auto scale VM stats data source (rrd/elasticsearch)", true, ConfigKey.Scope.Global); + + @Override + public AutoScaleStatsCollector getStatsCollector() { + if(RRD.equals(StatsDataSource.value())){ + return rrdAutoScaleStatsCollector; + }else if(ELASTIC_SEARCH.equals(StatsDataSource.value())){ + return elasticSearchAutoScaleStatsCollector; + } + return rrdAutoScaleStatsCollector; //rrd as default option + } + + @Override + public String getConfigComponentName() { + return AutoScaleStatsCollectorFactoryImpl.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ StatsDataSource }; + } +} diff --git a/server/src/com/cloud/network/as/ElasticSearchAutoScaleStatsCollector.java b/server/src/com/cloud/network/as/ElasticSearchAutoScaleStatsCollector.java new file mode 100644 index 000000000000..a986e64447a8 --- /dev/null +++ b/server/src/com/cloud/network/as/ElasticSearchAutoScaleStatsCollector.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.network.as; + +import com.cloud.utils.Pair; +import com.cloud.vm.VMInstanceVO; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.InetSocketTransportAddress; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ElasticSearchAutoScaleStatsCollector extends AutoScaleStatsCollector implements Configurable{ + + protected TransportClient elasticSearchClient; + + private static final ConfigKey ElasticSearchHost = new ConfigKey<>("Advanced", String.class, "autoscale.elasticsearch.host", null, + "Elastic search server host name", false, ConfigKey.Scope.Global); + + private static final ConfigKey ElasticSearchPort = new ConfigKey<>("Advanced", Integer.class, "autoscale.elasticsearch.port", null, + "Elastic search server transport module port", false, ConfigKey.Scope.Global); + + private static final ConfigKey ElasticSearchClusterName = new ConfigKey<>("Advanced", String.class, "autoscale.elasticsearch.cluster", null, + "Elastic search server cluster name", false, ConfigKey.Scope.Global); + + private static final ConfigKey ElasticSearchIndexName = new ConfigKey<>("Advanced", String.class, "autoscale.elasticsearch.index", null, + "Elastic search index name", false, ConfigKey.Scope.Global); + + private static final Logger s_logger = Logger.getLogger(ElasticSearchAutoScaleStatsCollector.class.getName()); + + public ElasticSearchAutoScaleStatsCollector(){ + buildConnection(); + } + + @Override + public Map retrieveMetrics(AutoScaleVmGroup asGroup, List vmList) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("[AutoScale] Collecting ElasticSearch data."); + } + + if(!connectionIsSet()) + return null; + + Map avgSummary = new HashMap<>(); + List> counterNameAndDuration = this.getPairOfCounterNameAndDuration(asGroup); + + try { + for (Pair counter : counterNameAndDuration) { + String counterName = counter.first().split(",")[0]; + Integer duration = counter.second(); + SearchResponse response = this.queryForStats(asGroup.getUuid(), counterName, duration); + avgSummary.put(counterName, this.getStatAverage(response)); + } + }catch (RuntimeException ex){ + s_logger.error("[AutoScale] Error while reading AutoScale group " + asGroup.getId() + " stats", ex); + } + return avgSummary; + } + + private Double getStatAverage(SearchResponse response) { + Double average = null; + Aggregations aggregations = response.getAggregations(); + if(aggregations != null){ + average = ((InternalAvg) aggregations.asMap().get("counter_average")).getValue(); + average = (average >= 0.0 ? average : null); + } + return average; + } + + private SearchResponse queryForStats(String autoScaleGroupUUID, String counterName, Integer duration) { + return elasticSearchClient.prepareSearch(ElasticSearchIndexName.value()) + .setTypes(counterName) + .setFrom(0).setSize(0) + .setQuery(QueryBuilders.filteredQuery( + QueryBuilders.matchAllQuery(), + FilterBuilders.andFilter( + FilterBuilders.rangeFilter("@timestamp").from("now-" + duration + "s/s").to("now"), + FilterBuilders.termFilter("autoScaleGroupUuid.raw", autoScaleGroupUUID) + ) + ) + ) + .addAggregation(AggregationBuilders.avg("counter_average").field("value")) + .execute() + .actionGet(); + } + + private boolean connectionIsSet() { + if(elasticSearchClient == null){ + buildConnection(); + } + return elasticSearchClient != null; + } + + private void buildConnection(){ + if(ElasticSearchHost.value() != null && ElasticSearchPort.value() != null && ElasticSearchClusterName.value() != null){ + Settings settings = ImmutableSettings.settingsBuilder() + .put("cluster.name", ElasticSearchClusterName.value()) + .put("client.transport.sniff", true) + .put("client.transport.ping_timeout", "30s") + .put("client.transport.nodes_sampler_interval", "10s") + .build(); + this.elasticSearchClient = new TransportClient(settings); + elasticSearchClient.addTransportAddress(new InetSocketTransportAddress(ElasticSearchHost.value(), ElasticSearchPort.value())); + } + } + + @Override + public String getConfigComponentName() { + return ElasticSearchAutoScaleStatsCollector.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ ElasticSearchPort, ElasticSearchHost, ElasticSearchClusterName, ElasticSearchIndexName }; + } +} diff --git a/server/src/com/cloud/network/as/LogStashClient.java b/server/src/com/cloud/network/as/LogStashClient.java new file mode 100644 index 000000000000..b6f3b0cbe90a --- /dev/null +++ b/server/src/com/cloud/network/as/LogStashClient.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 com.cloud.network.as; + +public interface LogStashClient { + + public boolean send(String message); +} diff --git a/server/src/com/cloud/network/as/LogStashClientImpl.java b/server/src/com/cloud/network/as/LogStashClientImpl.java new file mode 100644 index 000000000000..356db8bbc1bb --- /dev/null +++ b/server/src/com/cloud/network/as/LogStashClientImpl.java @@ -0,0 +1,48 @@ +package com.cloud.network.as; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +public class LogStashClientImpl implements LogStashClient, Configurable { + + private static final ConfigKey LogStashServer = new ConfigKey<>("Advanced", String.class, "autoscale.logstash.host", "", "Auto scale metrics target logstash host", false, ConfigKey.Scope.Global); + private static final ConfigKey LogStashPort = new ConfigKey<>("Advanced", Integer.class, "autoscale.logstash.port", null, "Auto scale metrics target logstash port", false, ConfigKey.Scope.Global); + + public static Logger s_logger = Logger.getLogger(LogStashClientImpl.class.getName()); + + @Override + public boolean send(String message) { + DatagramSocket socket = null; + try { + socket = new DatagramSocket(); + + byte[] data = message.getBytes(); + DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(LogStashServer.value()), LogStashPort.value()); + + socket.send(packet); + socket.setSoTimeout(500); + } catch (Exception e) { + s_logger.error("Error sending data to logstash", e); + return false; + } finally { + if (socket != null) + socket.close(); + } + return true; + } + + @Override + public String getConfigComponentName() { + return LogStashClientImpl.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ LogStashServer, LogStashPort}; + } +} diff --git a/server/src/com/cloud/network/as/RRDAutoScaleStatsCollector.java b/server/src/com/cloud/network/as/RRDAutoScaleStatsCollector.java new file mode 100644 index 000000000000..4d43ff663dc2 --- /dev/null +++ b/server/src/com/cloud/network/as/RRDAutoScaleStatsCollector.java @@ -0,0 +1,133 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.as; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.PerformanceMonitorCommand; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.utils.Pair; +import com.cloud.vm.VMInstanceVO; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class RRDAutoScaleStatsCollector extends AutoScaleStatsCollector { + + public static final Logger s_logger = Logger.getLogger(RRDAutoScaleStatsCollector.class.getName()); + + @Override + public Map retrieveMetrics(AutoScaleVmGroup asGroup, List vmList) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("[AutoScale] Collecting RRDs data."); + } + + if(vmList == null || vmList.size() == 0){ + return null; + } + + try { + Map params = this.buildPerformanceMonitorParams(asGroup, vmList); + PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20); + + // get random hostid because all vms are in a cluster + Long receiveHost = vmList.get(0).getHostId(); + Answer answer = _agentMgr.send(receiveHost, perfMon); + + if (answer == null || "fail".equals(answer) || !answer.getResult()) { + s_logger.debug("[AutoScale] Failed to send data to node"); + return null; + } + + //.: + //Ex.: 1.1:0.5,2.1:0.7 + String result = answer.getDetails(); + s_logger.debug("[AutoScale] RRDs collection answer: " + result); + + // extract data + Map counterSummary = new HashMap<>(); + String[] counterElements = result.split(","); + if (counterElements.length > 0) { + for (String string : counterElements) { + String[] counterValues = string.split(":"); + String[] counter_vm = counterValues[0].split("\\."); + + Long conditionId = Long.parseLong(params.get("con" + counter_vm[1])); + Double counterValue = Double.parseDouble(counterValues[1]); + String counterName = this.getCounterNameByCondition(conditionId); + + // Summary of all counter by counterId key + if (counterSummary.get(counterName) == null) { + /* initialize if data is not set */ + counterSummary.put(counterName, 0D); + } + + if (Counter.Source.memory.toString().equals(counterName)) { + // calculate memory in percent + AutoScaleVmProfileVO vmProfile = _asProfileDao.findById(asGroup.getProfileId()); + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vmProfile.getServiceOfferingId()); + // get current RAM percent + counterValue = counterValue / serviceOffering.getRamSize(); + } + + counterSummary.put(counterName, counterSummary.get(counterName) + counterValue); + } + + for(String counterName : counterSummary.keySet()){ + counterSummary.put(counterName, counterSummary.get(counterName) / vmList.size()); + } + return counterSummary; + } + } catch (Exception e){ + s_logger.error("[AutoScale] Error while reading AutoScale group " + asGroup.getId() + " Stats", e); + } + return null; + } + + private Map buildPerformanceMonitorParams(AutoScaleVmGroup asGroup, List vmList){ + Map params = new HashMap<>(); + List asGroupVmVOs = _asGroupVmDao.listByGroup(asGroup.getId()); + params.put("total_vm", String.valueOf(asGroupVmVOs.size())); + for(int i = 0; i < vmList.size() ; i++){ + params.put("vmname" + String.valueOf(i + 1), vmList.get(i).getInstanceName()); + params.put("vmid" + String.valueOf(i + 1), String.valueOf(vmList.get(i).getId())); + } + + // setup parameters phase: duration and counter + // list pair [counter, duration] + List> lstPair = getPairOfCounterNameAndDuration(asGroup); + int total_counter = 0; + String[] lstCounter = new String[lstPair.size()]; + for (int i = 0; i < lstPair.size(); i++) { + Pair pair = lstPair.get(i); + String strCounterNames = pair.first(); + Integer duration = pair.second(); + + lstCounter[i] = strCounterNames.split(",")[0]; + total_counter++; + params.put("duration" + String.valueOf(total_counter), duration.toString()); + params.put("counter" + String.valueOf(total_counter), lstCounter[i]); + params.put("con" + String.valueOf(total_counter), strCounterNames.split(",")[1]); + } + params.put("total_counter", String.valueOf(total_counter)); + + return params; + } +} \ No newline at end of file diff --git a/server/src/com/cloud/network/as/SNMPClient.java b/server/src/com/cloud/network/as/SNMPClient.java new file mode 100644 index 000000000000..8132510336b1 --- /dev/null +++ b/server/src/com/cloud/network/as/SNMPClient.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 com.cloud.network.as; + +import java.util.Map; + +public interface SNMPClient { + + public Map read(String ipAddress, Map counters); +} diff --git a/server/src/com/cloud/network/as/SNMPClientImpl.java b/server/src/com/cloud/network/as/SNMPClientImpl.java new file mode 100644 index 000000000000..770c37376078 --- /dev/null +++ b/server/src/com/cloud/network/as/SNMPClientImpl.java @@ -0,0 +1,204 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; +import org.snmp4j.CommunityTarget; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.smi.Null; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.UdpAddress; +import org.snmp4j.smi.VariableBinding; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +public class SNMPClientImpl implements SNMPClient, Configurable{ + + private final Snmp snmp; + + protected static final String CPU_SYSTEM_OID = "1.3.6.1.4.1.2021.11.10.0"; + protected static final String CPU_USER_OID = "1.3.6.1.4.1.2021.11.9.0"; + protected static final String MEMORY_FREE_OID = "1.3.6.1.4.1.2021.4.6.0"; + protected static final String MEMORY_TOTAL_OID = "1.3.6.1.4.1.2021.4.5.0"; + + private static final ConfigKey SnmpTimeout = new ConfigKey<>("Advanced", Integer.class, "autoscale.snmp.timeout", "500", "Auto scale snmp client max timeout", false, ConfigKey.Scope.Global); + private static final ConfigKey SnmpCommunity = new ConfigKey<>("Advanced", String.class, "autoscale.snmp.community", "DataCenter", "SNMP community", false, ConfigKey.Scope.Global); + private static final ConfigKey SnmpPort = new ConfigKey<>("Advanced", String.class, "autoscale.snmp.port", "161", "SNMP port", false, ConfigKey.Scope.Global); + private static final ConfigKey SnmpVersion = new ConfigKey<>("Advanced", String.class, "autoscale.snmp.version", "2c", "SNMP version (1, 2c or 3)", false, ConfigKey.Scope.Global); + + public static Logger s_logger = Logger.getLogger(SNMPClientImpl.class.getName()); + + public SNMPClientImpl(Snmp snmp) throws IOException { + this.snmp = snmp; + this.snmp.listen(); + } + + @Override + public Map read(String ipAddress, Map counters) { + try { + s_logger.debug("Sending GET SNMP to Server: " + ipAddress); + + ResponseEvent responseEvent = snmp.get(createPDU(counters), createTarget(ipAddress)); + if (responseEvent != null && responseEvent.getResponse() != null) { + if (responseEvent.getResponse().getErrorStatus() == PDU.noError) { + return parseResponse(counters, responseEvent.getResponse().getVariableBindings()); + } else { + s_logger.error("Error Status status/text: " + responseEvent.getResponse().getErrorStatus() + "/" + responseEvent.getResponse().getErrorStatusText()); + } + } else { + s_logger.info("SNMP agent did not respond. Possible causes: vm SNMP agent not ready / request timed out"); + } + } catch (IOException e) { + s_logger.error("Error querying SNMP on: " + ipAddress, e); + } catch (SnmpAgentNotReadyException e){ + s_logger.info("The SNMP agent was not ready on the VM " + ipAddress + ". Error: " + e.getMessage()); + } catch (Exception e) { + s_logger.error("Unexpected error", e); + } + return null; + } + + private Map parseResponse(Map counters, Vector variableBindings) throws SnmpAgentNotReadyException { + Map metrics = new HashMap<>(); + for(String counterName : counters.keySet()){ + if(counterName.equals(Counter.Source.cpu_used.name())){ + metrics.put(counterName, calculateCpuUsed(variableBindings)); + }else if(counterName.equals(Counter.Source.memory_used.name())){ + metrics.put(counterName, calculateMemoryUsed(variableBindings)); + }else if (counterName.equals(Counter.Source.memory_free.name())) { + metrics.put(counterName, calculateMemoryFree(variableBindings)); + }else{ + metrics.put(counterName, getValueByOID(variableBindings, counters.get(counterName))); + } + } + return metrics; + } + + private Double getValueByOID(Vector variableBindings, String oid) throws SnmpAgentNotReadyException { + for(VariableBinding binding : variableBindings){ + if(binding.getOid().toString().equals(oid)){ + if(binding.getVariable() instanceof Null){ + throw new SnmpAgentNotReadyException("Result was " + binding.toString()); + } + return new Double(binding.toValueString()); + } + } + return null; + } + + private Double calculateMemoryUsed(Vector variableBindings) throws SnmpAgentNotReadyException { + Double memoryFree = getValueByOID(variableBindings, MEMORY_FREE_OID); + Double memoryTotal = getValueByOID(variableBindings, MEMORY_TOTAL_OID); + if(memoryFree != null && memoryTotal != null) { + return (1.0 - (memoryFree / memoryTotal)) * 100; + }else{ + return null; + } + } + + private Double calculateMemoryFree(Vector variableBindings) throws SnmpAgentNotReadyException { + Double memoryFree = getValueByOID(variableBindings, MEMORY_FREE_OID); + Double memoryTotal = getValueByOID(variableBindings, MEMORY_TOTAL_OID); + if(memoryFree != null && memoryTotal != null) { + return (memoryFree / memoryTotal) * 100; + }else{ + return null; + } + } + + private Double calculateCpuUsed(Vector variableBindings) throws SnmpAgentNotReadyException { + Double cpuSystem = getValueByOID(variableBindings, CPU_SYSTEM_OID); + Double cpuUser = getValueByOID(variableBindings, CPU_USER_OID); + if(cpuSystem != null && cpuUser != null) { + return cpuSystem + cpuUser; + }else{ + return null; + } + } + + protected PDU createPDU(Map counters) { + PDU pdu = new PDU(); + for (String counterName : counters.keySet()) { + if(counterName.equals(Counter.Source.cpu_used.name())){ + pdu.add(createVariable(CPU_SYSTEM_OID)); + pdu.add(createVariable(CPU_USER_OID)); + }else if(counterName.equals(Counter.Source.memory_used.name())){ + pdu.add(createVariable(MEMORY_FREE_OID)); + pdu.add(createVariable(MEMORY_TOTAL_OID)); + }else if (counterName.equals(Counter.Source.memory_free.name())) { + pdu.add(createVariable(MEMORY_FREE_OID)); + pdu.add(createVariable(MEMORY_TOTAL_OID)); + }else{ + pdu.add(createVariable(counters.get(counterName))); + } + } + pdu.setType(PDU.GET); + return pdu; + } + + private VariableBinding createVariable(String oid){ + return new VariableBinding(new OID(oid)); + } + + protected CommunityTarget createTarget(String ipAddress) { + CommunityTarget target = new CommunityTarget(); + target.setCommunity(new OctetString(SnmpCommunity.value())); + target.setVersion(getSnmpVersion()); + target.setAddress(new UdpAddress(ipAddress + "/" + SnmpPort.value())); + target.setRetries(2); + target.setTimeout(SnmpTimeout.value()); + return target; + } + + private int getSnmpVersion() { + String version = SnmpVersion.value(); + if("1".equals(version)){ + return SnmpConstants.version1; + }else if("2c".equals(version)){ + return SnmpConstants.version2c; + }else if("3".equals(version)){ + return SnmpConstants.version3; + }else{ + return SnmpConstants.version2c; + } + } + + @Override + public String getConfigComponentName() { + return SNMPClientImpl.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ SnmpTimeout, SnmpCommunity, SnmpPort, SnmpVersion}; + } + + protected class SnmpAgentNotReadyException extends Exception { + public SnmpAgentNotReadyException(String message) { + super(message); + } + } +} diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 9b481ed4a648..1456e5fd5f58 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -743,6 +743,23 @@ public boolean saveSSHKey(final Network network, final NicProfile nic, final Vir return result; } +// @Override +// public boolean saveUserVMData(Network network, NicProfile nic, VirtualMachineProfile vm, +// Map vmData) throws ResourceUnavailableException { +// if (!canHandle(network, null)) { +// return false; +// } +// List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); +// if (routers == null || routers.isEmpty()) { +// s_logger.debug("Can't find virtual router element in network " + network.getId()); +// return true; +// } +// +// VirtualMachineProfile uservm = vm; +// +// return _routerMgr.saveVmMetadataToRouter(network, nic, uservm, routers, vmData); +// } + @Override public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException { if (!canHandle(network, null)) { @@ -766,6 +783,23 @@ public boolean saveUserData(final Network network, final NicProfile nic, final V return result; } + +// public boolean saveMetadataToRouter(Network network, NicProfile nic, VirtualMachineProfile vm, Map metadata) throws ResourceUnavailableException { +// if (!canHandle(network, null)) { +// return false; +// } +// List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); +// if (routers == null || routers.isEmpty()) { +// s_logger.debug("Can't find virtual router element in network " + network.getId()); +// return true; +// } +// +// @SuppressWarnings("unchecked") +// VirtualMachineProfile uservm = vm; +// +// return _routerMgr.saveVmMetadataToRouter(network, nic, uservm, routers, metadata); +// } + @Override public List> getCommands() { final List> cmdList = new ArrayList>(); diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index cf72ff28f0cf..e7ee04672e65 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -187,28 +187,28 @@ public FirewallRule createEgressFirewallRule(FirewallRule rule) throws NetworkRu @Override @ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating firewall rule", create = true) public FirewallRule createIngressFirewallRule(FirewallRule rule) throws NetworkRuleConflictException { - Account caller = CallContext.current().getCallingAccount(); + Account caller = CallContext.current().getCallingAccount(); Long sourceIpAddressId = rule.getSourceIpAddressId(); return createFirewallRule(sourceIpAddressId, caller, rule.getXid(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), - rule.getSourceCidrList(), null, rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay()); + rule.getSourceCidrList(), null, rule.getIcmpCode(), rule.getIcmpType(), null, rule.getType(), rule.getNetworkId(), rule.getTrafficType(), rule.isDisplay()); } //Destination CIDR capability is currently implemented for egress rules only. For others, the field is passed as null. @DB protected FirewallRule createFirewallRule(final Long ipAddrId, Account caller, final String xId, final Integer portStart, final Integer portEnd, - final String protocol, final List sourceCidrList, final List destCidrList, final Integer icmpCode, final Integer icmpType, final Long relatedRuleId, - final FirewallRule.FirewallRuleType type, - final Long networkId, final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException { + final String protocol, final List sourceCidrList, final List destCidrList, final Integer icmpCode, final Integer icmpType, final Long relatedRuleId, + final FirewallRule.FirewallRuleType type, + final Long networkId, final FirewallRule.TrafficType trafficType, final Boolean forDisplay) throws NetworkRuleConflictException { IPAddressVO ipAddress = null; if (ipAddrId != null) { // this for ingress firewall rule, for egress id is null - ipAddress = _ipAddressDao.findById(ipAddrId); - // Validate ip address - if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { + ipAddress = _ipAddressDao.findById(ipAddrId); + // Validate ip address + if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { throw new InvalidParameterValueException("Unable to create firewall rule; " + "couldn't locate IP address by id in the system"); - } - _networkModel.checkIpForService(ipAddress, Service.Firewall, null); + } + _networkModel.checkIpForService(ipAddress, Service.Firewall, null); } validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type, networkId, trafficType); @@ -231,9 +231,9 @@ protected FirewallRule createFirewallRule(final Long ipAddrId, Account caller, f domainId = ipAddress.getAllocatedInDomainId(); } else if (networkId != null) { //egress firewall rule - Network network = _networkModel.getNetwork(networkId); - accountId = network.getAccountId(); - domainId = network.getDomainId(); + Network network = _networkModel.getNetwork(networkId); + accountId = network.getAccountId(); + domainId = network.getDomainId(); } final Long accountIdFinal = accountId; @@ -295,7 +295,7 @@ public Pair, Integer> listFirewallRules(IListFirewa sb.and("id", sb.entity().getId(), Op.EQ); sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ); - sb.and("networkId", sb.entity().getNetworkId(), Op.EQ); + sb.and("networkId", sb.entity().getNetworkId(), Op.EQ); sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ); sb.and("purpose", sb.entity().getPurpose(), Op.EQ); sb.and("display", sb.entity().isDisplay(), Op.EQ); @@ -337,9 +337,9 @@ public Pair, Integer> listFirewallRules(IListFirewa sc.setParameters("ip", ipId); } - if (networkId != null) { - sc.setParameters("networkId", networkId); - } + if (networkId != null) { + sc.setParameters("networkId", networkId); + } sc.setParameters("purpose", Purpose.Firewall); sc.setParameters("trafficType", trafficType); @@ -363,9 +363,9 @@ boolean detectConflictingCidrs(List cidrList1, List cidrList2){ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException { List rules; if (newRule.getSourceIpAddressId() != null) { - rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); + rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); assert (rules.size() >= 1) : "For network rules, we now always first persist the rule 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."; } else { // fetches only firewall egress rules. rules = _firewallDao.listByNetworkPurposeTrafficTypeAndNotRevoked(newRule.getNetworkId(), Purpose.Firewall, newRule.getTrafficType()); @@ -378,7 +378,7 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict } boolean oneOfRulesIsFirewall = - ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol() + ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol() .equalsIgnoreCase(rule.getProtocol())))); // if both rules are firewall and their cidrs are different, we can skip port ranges verification @@ -413,7 +413,7 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict //Check for the ICMP protocol. This has to be done separately from other protocols as we need to check the ICMP codes and ICMP type also. if (newRule.getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO) && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) { if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue() && newRule.getIcmpType().longValue() == rule.getIcmpType().longValue() && - newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()) && duplicatedCidrs) { + newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()) && duplicatedCidrs) { throw new InvalidParameterValueException("New rule conflicts with existing rule id=" + rule.getId()); } } @@ -448,7 +448,7 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict rule.getProtocol())) || (rule.getPurpose() == Purpose.Vpn && newRule.getPurpose() == Purpose.PortForwarding && !newRule.getProtocol().equalsIgnoreCase( rule.getProtocol())); boolean allowStaticNat = - (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() == Purpose.StaticNat && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())); + (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() == Purpose.StaticNat && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())); boolean allowVpnPf = (rule.getPurpose() == Purpose.PortForwarding && newRule.getPurpose() == Purpose.Vpn && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())); @@ -458,7 +458,7 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict if (!(allowPf || allowStaticNat || oneOfRulesIsFirewall || allowVpnPf || allowVpnLb)) { throw new NetworkRuleConflictException("The range specified, " + newRule.getSourcePortStart() + "-" + newRule.getSourcePortEnd() + - ", conflicts with rule " + rule.getId() + " which has " + rule.getSourcePortStart() + "-" + rule.getSourcePortEnd()); + ", conflicts with rule " + rule.getId() + " which has " + rule.getSourcePortStart() + "-" + rule.getSourcePortEnd()); } } } @@ -470,7 +470,7 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict @Override public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type, - Long networkId, FirewallRule.TrafficType trafficType) { + Long networkId, FirewallRule.TrafficType trafficType) { if (portStart != null && !NetUtils.isValidPort(portStart)) { throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart); } @@ -489,7 +489,7 @@ public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer if (ipAddress != null) { if (ipAddress.getAssociatedWithNetworkId() == null) { - throw new InvalidParameterValueException("Unable to create firewall rule ; ip with specified id is not associated with any network"); + throw new InvalidParameterValueException("Unable to create firewall rule ; ip with specified id is not associated with any network"); } else { networkId = ipAddress.getAssociatedWithNetworkId(); } @@ -565,7 +565,7 @@ public boolean applyRules(List rules, boolean continueOn FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(rule.getId()); if (relatedRule != null) { s_logger.warn("Can't remove the firewall rule id=" + rule.getId() + " as it has related firewall rule id=" + relatedRule.getId() + - "; leaving it in Revoke state"); + "; leaving it in Revoke state"); success = false; } else { removeRule(rule); @@ -592,31 +592,31 @@ public boolean applyRules(Network network, Purpose purpose, List)rules); - if (handled) - break; - } - break; + if (handled) + break; + } + break; /* case NetworkACL: for (NetworkACLServiceProvider element: _networkAclElements) { Network.Provider provider = element.getProvider(); @@ -629,10 +629,10 @@ public boolean applyRules(Network network, Purpose purpose, List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress); - return applyFirewallRules(rules, false, caller); + List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress); + return applyFirewallRules(rules, false, caller); } @Override @@ -747,8 +747,8 @@ protected boolean revokeFirewallRule(long ruleId, boolean apply, Account caller, // ingress firewall rule if (rule.getSourceIpAddressId() != null) { //feteches ingress firewall, ingress firewall rules associated with the ip - List rules = _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall); - return applyFirewallRules(rules, false, caller); + List rules = _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall); + return applyFirewallRules(rules, false, caller); //egress firewall rule } else if (networkId != null) { List rules = _firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress); @@ -832,24 +832,24 @@ public void revokeRule(final FirewallRuleVO rule, Account caller, long userId, f Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - boolean generateUsageEvent = false; + boolean generateUsageEvent = false; - if (rule.getState() == State.Staged) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found a rule that is still in stage state so just removing it: " + rule); - } - removeRule(rule); - generateUsageEvent = true; - } else if (rule.getState() == State.Add || rule.getState() == State.Active) { - rule.setState(State.Revoke); - _firewallDao.update(rule.getId(), rule); - generateUsageEvent = true; - } + if (rule.getState() == State.Staged) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found a rule that is still in stage state so just removing it: " + rule); + } + removeRule(rule); + generateUsageEvent = true; + } else if (rule.getState() == State.Add || rule.getState() == State.Active) { + rule.setState(State.Revoke); + _firewallDao.update(rule.getId(), rule); + generateUsageEvent = true; + } - if (generateUsageEvent && needUsageEvent) { + if (generateUsageEvent && needUsageEvent) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_DELETE, rule.getAccountId(), 0, rule.getId(), null, rule.getClass().getName(), - rule.getUuid()); - } + rule.getUuid()); + } } }); } @@ -896,7 +896,7 @@ public boolean revokeFirewallRulesForIp(long ipId, long userId, Account caller) @Override @ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating firewall rule", create = true) public FirewallRule createRuleForAllCidrs(long ipAddrId, Account caller, Integer startPort, Integer endPort, String protocol, Integer icmpCode, Integer icmpType, - Long relatedRuleId, long networkId) throws NetworkRuleConflictException { + Long relatedRuleId, long networkId) throws NetworkRuleConflictException { // If firwallRule for this port range already exists, return it List rules = _firewallDao.listByIpPurposeAndProtocolAndNotRevoked(ipAddrId, startPort, endPort, protocol, Purpose.Firewall); diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 9b7cf9b42305..2a81bd733c22 100644 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -16,10 +16,16 @@ // under the License. package com.cloud.network.lb; +import com.cloud.api.ApiDBUtils; +import com.cloud.network.as.AutoScaleService; +import com.cloud.network.dao.LoadBalancerOptionsDao; +import com.cloud.network.dao.LoadBalancerOptionsVO; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -42,6 +48,10 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationVO; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; import org.apache.log4j.Logger; @@ -98,6 +108,10 @@ import com.cloud.network.dao.LoadBalancerCertMapDao; import com.cloud.network.dao.LoadBalancerCertMapVO; import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerNetworkMapDao; +import com.cloud.network.dao.LoadBalancerNetworkMapVO; +import com.cloud.network.dao.LoadBalancerPortMapDao; +import com.cloud.network.dao.LoadBalancerPortMapVO; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.dao.LoadBalancerVO; @@ -128,6 +142,7 @@ import com.cloud.network.rules.StickinessPolicy; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.service.dao.ServiceOfferingDao; @@ -142,6 +157,7 @@ import com.cloud.user.dao.UserDao; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; @@ -214,6 +230,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements DomainService _domainMgr; @Inject ConfigurationManager _configMgr; + @Inject + AutoScaleService _autoScaleMgr; @Inject ExternalDeviceUsageManager _externalDeviceUsageMgr; @@ -256,10 +274,22 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements EntityManager _entityMgr; @Inject LoadBalancerCertMapDao _lbCertMapDao; - + @Inject + LoadBalancerNetworkMapDao _lbNetMapDao; + @Inject + LoadBalancerPortMapDao _lbPortMapDao; + @Inject + LoadBalancerOptionsDao _lbOptionsDao; + @Inject + NetworkOfferingDao _netOffDao; + @Inject + GloboResourceConfigurationDao _globoResourceConfigurationDao; @Inject NicSecondaryIpDao _nicSecondaryIpDao; + @Inject + GloboResourceConfigurationDao globoConfigDao; + // Will return a string. For LB Stickiness this will be a json, for // autoscale this will be "," separated values @Override @@ -337,21 +367,13 @@ private LbAutoScaleVmGroup getLbAutoScaleVmGroup(AutoScaleVmGroupVO vmGroup, Str throw new InvalidParameterValueException("Global setting endpointe.url has to be set to the Management Server's API end point"); } - LbAutoScaleVmProfile lbAutoScaleVmProfile = - new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl, zoneId, domainId, serviceOfferingId, templateId, vmName, lbNetworkUuid); + LbAutoScaleVmProfile lbAutoScaleVmProfile = new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl, zoneId, domainId, serviceOfferingId, templateId, vmName, + lbNetworkUuid); return new LbAutoScaleVmGroup(vmGroup, autoScalePolicies, lbAutoScaleVmProfile, currentState); } private boolean applyAutoScaleConfig(LoadBalancerVO lb, AutoScaleVmGroupVO vmGroup, String currentState) throws ResourceUnavailableException { - LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, currentState, lb); - /* - * Regular config like destinations need not be packed for applying - * autoscale config as of today. - */ - List policyList = getStickinessPolicies(lb.getId()); - Ip sourceIp = getSourceIp(lb); - LoadBalancingRule rule = new LoadBalancingRule(lb, null, policyList, null, sourceIp, null, lb.getLbProtocol()); - rule.setAutoScaleVmGroup(lbAutoScaleVmGroup); + LoadBalancingRule rule = getLoadBalancerRuleToApply(lb); if (!isRollBackAllowedForProvider(lb)) { // this is for Netscaler type of devices. if their is failure the db @@ -359,9 +381,7 @@ private boolean applyAutoScaleConfig(LoadBalancerVO lb, AutoScaleVmGroupVO vmGro return false; } - List rules = Arrays.asList(rule); - - if (!applyLbRules(rules, false)) { + if (!applyLbRules(Arrays.asList(rule), false)) { s_logger.debug("LB rules' autoscale config are not completely applied"); return false; } @@ -369,7 +389,8 @@ private boolean applyAutoScaleConfig(LoadBalancerVO lb, AutoScaleVmGroupVO vmGro return true; } - private Ip getSourceIp(LoadBalancer lb) { + @Override + public Ip getSourceIp(LoadBalancer lb) { Ip sourceIp = null; if (lb.getScheme() == Scheme.Public) { sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress(); @@ -422,9 +443,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) { loadBalancer.setState(FirewallRule.State.Active); s_logger.debug("LB rule " + loadBalancer.getId() + " state is set to Active"); _lbDao.persist(loadBalancer); - vmGroup.setState(AutoScaleVmGroup.State_Enabled); + vmGroup.setState(AutoScaleVmGroup.State_Disabled); _autoScaleVmGroupDao.persist(vmGroup); - s_logger.debug("LB Auto Scale Vm Group with Id: " + vmGroupid + " is set to Enabled state."); + s_logger.debug("LB Auto Scale Vm Group with Id: " + vmGroupid + " is set to Disabled state."); } }); } @@ -433,8 +454,8 @@ public void doInTransactionWithoutResult(TransactionStatus status) { return success; } - private boolean validateHealthCheck(CreateLBHealthCheckPolicyCmd cmd) { - LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); + private boolean validateHealthCheck(Long lbRuleId) { + LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId); String capability = getLBCapability(loadBalancer.getNetworkId(), Capability.HealthCheckPolicy.getName()); if (capability != null) { return true; @@ -442,19 +463,18 @@ private boolean validateHealthCheck(CreateLBHealthCheckPolicyCmd cmd) { return false; } - private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException { - LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); + private boolean genericValidator(Long lbRuleId, String stickinessMethodName, Map apiParamList) throws InvalidParameterValueException { + LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId); /* Validation : check for valid Method name and params */ List stickinessMethodList = getStickinessMethods(loadBalancer.getNetworkId()); boolean methodMatch = false; if (stickinessMethodList == null) { - throw new InvalidParameterValueException("Failed: No Stickiness method available for LB rule:" + cmd.getLbRuleId()); + throw new InvalidParameterValueException("Failed: No Stickiness method available for LB rule:" + lbRuleId); } for (LbStickinessMethod method : stickinessMethodList) { - if (method.getMethodName().equalsIgnoreCase(cmd.getStickinessMethodName())) { + if (method.getMethodName().equalsIgnoreCase(stickinessMethodName)) { methodMatch = true; - Map apiParamList = cmd.getparamList(); List methodParamList = method.getParamList(); Map tempParamList = new HashMap(); @@ -501,13 +521,13 @@ private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws Invalid } } if (methodMatch == false) { - throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule:" + cmd.getLbRuleId()); + throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule:" + lbRuleId); } /* Validation : check for the multiple policies to the rule id */ - List stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false); + List stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lbRuleId, false); if (stickinessPolicies.size() > 1) { - throw new InvalidParameterValueException("Failed to create Stickiness policy: Already two policies attached " + cmd.getLbRuleId()); + throw new InvalidParameterValueException("Failed to create Stickiness policy: Already two policies attached " + lbRuleId); } return true; } @@ -530,30 +550,37 @@ public StickinessPolicy createLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " is in deleting state: "); } + return validateAndPersistLbStickinessPolicy(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription(), cmd.getDisplay()); + } + + @DB + public StickinessPolicy validateAndPersistLbStickinessPolicy(Long lbRuleId, String lbStickinessPolicyName, String lbStickinessMethodName, Map paramList, String description, Boolean forDisplay) { /* Generic validations */ - if (!genericValidator(cmd)) { - throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + cmd.getLbRuleId()); + if (!genericValidator(lbRuleId, lbStickinessMethodName, paramList)) { + throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + lbRuleId); } /* * Specific validations using network element validator for specific * validations */ - LBStickinessPolicyVO lbpolicy = - new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); + LBStickinessPolicyVO lbpolicy = new LBStickinessPolicyVO(lbRuleId, lbStickinessPolicyName, lbStickinessMethodName, paramList, description); List policyList = new ArrayList(); - policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams())); + policyList.add(new LbStickinessPolicy(lbStickinessMethodName, lbpolicy.getParams())); + LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId); Ip sourceIp = getSourceIp(loadBalancer); - LoadBalancingRule lbRule = - new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList, null, sourceIp, null, loadBalancer.getLbProtocol()); + LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList, null, sourceIp, null, loadBalancer.getLbProtocol()); + + GloboResourceConfigurationVO skipDnsErrorCmd = _globoResourceConfigurationDao.getFirst(GloboResourceType.LOAD_BALANCER, loadBalancer.getUuid(), GloboResourceKey.skipDnsError); + boolean skipDnsError = (skipDnsErrorCmd != null && skipDnsErrorCmd.getBooleanValue()); + lbRule.setSkipDnsError(skipDnsError); + if (!validateLbRule(lbRule)) { - throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + cmd.getLbRuleId()); + throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + lbRuleId); } /* Finally Insert into DB */ - LBStickinessPolicyVO policy = - new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); - Boolean forDisplay = cmd.getDisplay(); + LBStickinessPolicyVO policy = new LBStickinessPolicyVO(loadBalancer.getId(), lbStickinessPolicyName, lbStickinessMethodName, paramList, description); if (forDisplay != null) { policy.setDisplay(forDisplay); } @@ -590,38 +617,40 @@ public HealthCheckPolicy createLBHealthCheckPolicy(CreateLBHealthCheckPolicyCmd throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " is in deleting state: "); } + return validateAndPersistLbHealthcheckPolicy(loadBalancer.getId(), cmd.getPingPath(), cmd.getDescription(), cmd.getResponsTimeOut(), cmd.getHealthCheckInterval(), cmd.getHealthyThreshold(), cmd.getUnhealthyThreshold(), cmd.getDisplay()); + } + + @DB + public HealthCheckPolicy validateAndPersistLbHealthcheckPolicy(Long lbRuleId, String pingPath, String description, int timeout, int healthcheckInterval, int healthyThreshold, int unhealthyThreshold, Boolean forDisplay) { /* * Validate Whether LB Provider has the capabilities to support Health * Checks */ - if (!validateHealthCheck(cmd)) { + if (!validateHealthCheck(lbRuleId)) { throw new InvalidParameterValueException( - "Failed to create HealthCheck policy: Validation Failed (HealthCheck Policy is not supported by LB Provider for the LB rule id :" + cmd.getLbRuleId() + ")"); + "Failed to create HealthCheck policy: Validation Failed (HealthCheck Policy is not supported by LB Provider for the LB rule id :" + lbRuleId + ")"); } /* Validation : check for the multiple hc policies to the rule id */ - List hcPolicies = _lb2healthcheckDao.listByLoadBalancerId(cmd.getLbRuleId(), false); + List hcPolicies = _lb2healthcheckDao.listByLoadBalancerId(lbRuleId, false); if (hcPolicies.size() > 0) { - throw new InvalidParameterValueException("Failed to create HealthCheck policy: Already policy attached for the LB Rule id :" + cmd.getLbRuleId()); + throw new InvalidParameterValueException("Failed to create HealthCheck policy: Already policy attached for the LB Rule id :" + lbRuleId); } /* * Specific validations using network element validator for specific * validations */ - LBHealthCheckPolicyVO hcpolicy = - new LBHealthCheckPolicyVO(loadBalancer.getId(), cmd.getPingPath(), cmd.getDescription(), cmd.getResponsTimeOut(), cmd.getHealthCheckInterval(), - cmd.getHealthyThreshold(), cmd.getUnhealthyThreshold()); + LBHealthCheckPolicyVO hcpolicy = new LBHealthCheckPolicyVO(lbRuleId, pingPath, description, timeout, + healthcheckInterval, healthyThreshold, unhealthyThreshold); List hcPolicyList = new ArrayList(); - hcPolicyList.add(new LbHealthCheckPolicy(hcpolicy.getpingpath(), hcpolicy.getDescription(), hcpolicy.getResponseTime(), hcpolicy.getHealthcheckInterval(), - hcpolicy.getHealthcheckThresshold(), hcpolicy.getUnhealthThresshold())); + hcPolicyList.add(new LbHealthCheckPolicy(hcpolicy.getpingpath(), hcpolicy.getDescription(), hcpolicy.getResponseTime(), hcpolicy.getHealthcheckInterval(), hcpolicy + .getHealthcheckThresshold(), hcpolicy.getUnhealthThresshold())); // Finally Insert into DB - LBHealthCheckPolicyVO policy = - new LBHealthCheckPolicyVO(loadBalancer.getId(), cmd.getPingPath(), cmd.getDescription(), cmd.getResponsTimeOut(), cmd.getHealthCheckInterval(), - cmd.getHealthyThreshold(), cmd.getUnhealthyThreshold()); + LBHealthCheckPolicyVO policy = new LBHealthCheckPolicyVO(lbRuleId, pingPath, description, timeout, + healthcheckInterval, healthyThreshold, unhealthyThreshold); - Boolean forDisplay = cmd.getDisplay(); if (forDisplay != null) { policy.setDisplay(forDisplay); } @@ -724,8 +753,11 @@ public boolean applyLBHealthCheckPolicy(CreateLBHealthCheckPolicyCmd cmd) { _lbDao.persist(loadBalancer); s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating healthcheck policy"); } - deleteLBHealthCheckPolicy(cmd.getEntityId(), false); success = false; + throw new CloudRuntimeException(e.getMessage()); + } finally { + if (!success) + deleteLBHealthCheckPolicy(cmd.getEntityId(), false); } return success; } @@ -923,13 +955,107 @@ private boolean isRollBackAllowedForProvider(LoadBalancerVO loadBalancer) { if (provider == null || provider.size() == 0) { return false; } - if (provider.get(0) == Provider.Netscaler || provider.get(0) == Provider.F5BigIp || - provider.get(0) == Provider.VirtualRouter) { + if (provider.get(0) == Provider.Netscaler || provider.get(0) == Provider.F5BigIp || provider.get(0) == Provider.GloboNetwork) { return true; } return false; } + @Override + @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_NETWORK_TO_LOAD_BALANCER_RULE, eventDescription = "assigning networks to load balancer", async = true) + public boolean assignNetworksToLoadBalancer(Long loadBalancerId, List networkIds) { + CallContext ctx = CallContext.current(); + Account caller = ctx.getCallingAccount(); + + final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); + if (loadBalancer == null) { + throw new InvalidParameterValueException("Failed to assign to load balancer " + loadBalancerId + ", the load balancer was not found."); + } + + final Network loadBalancerNetwork = _networkDao.findById(loadBalancer.getNetworkId()); + if (loadBalancerNetwork == null) { + throw new InvalidParameterValueException("Failed to load network of load balancer " + loadBalancerId + ", the network was not found."); + } + + List loadBalancerProviders = _networkMgr.getProvidersForServiceInNetwork(loadBalancerNetwork, Service.Lb); + if (loadBalancerProviders == null || loadBalancerProviders.isEmpty() || loadBalancerProviders.size() > 1) { + throw new InvalidParameterValueException("Invalid network offering. Offering need to have only one Load Balancer provider. Network Offering id " + + loadBalancerNetwork.getNetworkOfferingId()); + } + + List mappedNetworks = _lbNetMapDao.listByLoadBalancerId(loadBalancerId); + Set mappedNetworkIds = new HashSet(); + mappedNetworkIds.add(loadBalancer.getNetworkId()); + for (LoadBalancerNetworkMapVO mappedNetwork : mappedNetworks) { + mappedNetworkIds.add(Long.valueOf(mappedNetwork.getNetworkId())); + } + + if (networkIds == null || networkIds.isEmpty()) { + s_logger.warn("List of networks to assign to the lb, is empty"); + return false; + } + + final List networksToAdd = new ArrayList(); + + for (Long networkId : networkIds) { + if (mappedNetworkIds.contains(networkId)) { + throw new InvalidParameterValueException("Network " + networkId + " is already mapped to load balancer."); + } + + Network network = _networkDao.findById(networkId); + if (network == null || network.getState() == Network.State.Destroy || network.getState() == Network.State.Shutdown) { + InvalidParameterValueException ex = new InvalidParameterValueException("Invalid network id specified"); + if (network == null) { + ex.addProxyObject(networkId.toString(), "networkId"); + } else { + ex.addProxyObject(network.getUuid(), "networkId"); + } + throw ex; + } + + _accountMgr.checkAccess(caller, null, true, loadBalancer, network); + + // Check if additional network have the same provider as load balancer network + List providers = _networkMgr.getProvidersForServiceInNetwork(network, Service.Lb); + if (!loadBalancerProviders.equals(providers)) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Network with id specified cannot be added because it doesn't have the same load balancer provider as network " + loadBalancerNetwork.getId()); + ex.addProxyObject(network.getUuid(), "networkId"); + throw ex; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Adding " + network + " to the load balancer"); + } + networksToAdd.add(network); + } + + LoadBalancingRule rule = getLoadBalancerRuleToApply(loadBalancer); + + // insert additional networks + List allNetworks = rule.getAdditionalNetworks(); + allNetworks.addAll(networkIds); + rule.setAdditionalNetworks(allNetworks); + + if (!validateLbRule(rule)) { + throw new InvalidParameterValueException("LB service provider cannot support some networks"); + } + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (Network network : networksToAdd) { + LoadBalancerNetworkMapVO map = new LoadBalancerNetworkMapVO(loadBalancer.getId(), network.getId()); + map = _lbNetMapDao.persist(map); + } + } + }); + + // No need to apply load balancer rules because nothing was changed. Apply + // only happens when virtual machine is added. + return true; + } + @Override @DB @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true) @@ -1006,8 +1132,11 @@ public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds, throw new PermissionDeniedException("Cannot add virtual machines that do not belong to the same owner."); } + // load additional networks + List listLbnetmap = _lbNetMapDao.listByLoadBalancerId(loadBalancerId); + // Let's check to make sure the vm has a nic in the same network as - // the load balancing rule. + // the load balancing rule or in additional networks. List nics = _networkModel.getNics(vm.getId()); Nic nicInSameNetwork = null; for (Nic nic : nics) { @@ -1015,11 +1144,18 @@ public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds, nicInSameNetwork = nic; break; } + + // try find in additional load balancer networks + for (LoadBalancerNetworkMapVO lbnetmap : listLbnetmap) { + if (nic.getNetworkId() == lbnetmap.getNetworkId()) { + nicInSameNetwork = nic; + break; + } + } } if (nicInSameNetwork == null) { - InvalidParameterValueException ex = - new InvalidParameterValueException("VM with id specified cannot be added because it doesn't belong in the same network."); + InvalidParameterValueException ex = new InvalidParameterValueException("VM with id specified cannot be added because it doesn't belong in the same network."); ex.addProxyObject(vm.getUuid(), "instanceId"); throw ex; } @@ -1043,11 +1179,11 @@ public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds, } } - List vmIpsList = vmIdIpMap.get(instanceId); - String vmLbIp = null; - + List vmIpsList = null; + if(vmIdIpMap.get(instanceId) != null && !vmIdIpMap.get(instanceId).isEmpty()) { + vmIpsList = new ArrayList<>(vmIdIpMap.get(instanceId)); + } if (vmIpsList != null) { - //check if the ips belongs to nic secondary ip for (String ip: vmIpsList) { // skip the primary ip from vm secondary ip comparisions @@ -1097,50 +1233,37 @@ public void doInTransactionWithoutResult(TransactionStatus status) { } }); - if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { - // For autoscaled loadbalancer, the rules need not be applied, - // meaning the call need not reach the resource layer. - // We can consider the job done. - return true; - } - boolean success = false; + FirewallRule.State backupState = loadBalancer.getState(); try { loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); applyLoadBalancerConfig(loadBalancerId); - success = true; - } catch (ResourceUnavailableException e) { - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - success = false; - } finally { - if (!success) { - final List vmInstanceIds = new ArrayList(); - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - for (Long vmId : vmIds) { - vmInstanceIds.add(vmId); - } + return true; + } catch (ResourceUnavailableException | CloudRuntimeException e) { + s_logger.warn("Unable to apply the load balancer config because " + e.getMessage(), e); + final List vmInstanceIds = new ArrayList(); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (Long vmId : vmIds) { + vmInstanceIds.add(vmId); } - }); - if (!vmInstanceIds.isEmpty()) { - _lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null); - s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while attaching VM: " + vmInstanceIds); } - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " - + vmInstanceIds); - ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); - // TBD: Also pack in the instanceIds in the exception using the - // right VO object or table name. - throw ex; + }); + if (!vmInstanceIds.isEmpty()) { + _lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while attaching VM: " + vmInstanceIds); } - + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " + + vmInstanceIds + ". Reason: " + e.getMessage()); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); + // TBD: Also pack in the instanceIds in the exception using the + // right VO object or table name. + throw ex; } - - return success; } @Override @@ -1345,27 +1468,18 @@ private boolean removeFromLoadBalancerInternal(long loadBalancerId, List i s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + instanceId); } else { - for (String vmIp: lbVmIps) { - LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmIdVmIp (loadBalancerId, instanceId, vmIp); + for (String vmIp : lbVmIps) { + LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmIdVmIp(loadBalancerId, instanceId, vmIp); if (map == null) { - throw new InvalidParameterValueException("The instance id: "+ instanceId +" is not configured " - + " for LB rule id " + loadBalancerId); + throw new InvalidParameterValueException("The instance id: " + instanceId + " is not configured " + " for LB rule id " + loadBalancerId); } map.setRevoke(true); _lb2VmMapDao.persist(map); - s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + - instanceId + ", vmip " + vmIp); + s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + instanceId + ", vmip " + vmIp); + } - } - } - if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { - // For autoscaled loadbalancer, the rules need not be applied, - // meaning the call need not reach the resource layer. - // We can consider the job done and only need to remove the - // rules in DB - _lb2VmMapDao.remove(loadBalancer.getId(), instanceIds, null); - return true; + } } if (!applyLoadBalancerConfig(loadBalancerId)) { @@ -1515,8 +1629,8 @@ public List doInTransaction(TransactionStatus status) { if (generateUsageEvent) { // Generate usage event right after all rules were marked for revoke Network network = _networkModel.getNetwork(lb.getNetworkId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), network.getDataCenterId(), lb.getId(), - null, LoadBalancingRule.class.getName(), lb.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), network.getDataCenterId(), lb.getId(), null, + LoadBalancingRule.class.getName(), lb.getUuid()); } return backupMaps; @@ -1533,6 +1647,11 @@ public List doInTransaction(TransactionStatus status) { if (apply) { try { + List autoScaleGroups = _autoScaleVmGroupDao.listByAll(lb.getId(), null); + for(AutoScaleVmGroupVO autoScaleVmGroup : autoScaleGroups){ + _autoScaleMgr.deleteAutoScaleVmGroupWithDependencies(autoScaleVmGroup.getId()); + } + if (!applyLoadBalancerConfig(loadBalancerId)) { s_logger.warn("Unable to apply the load balancer config"); return false; @@ -1555,10 +1674,34 @@ public List doInTransaction(TransactionStatus status) { } } + List lbNetMaps = _lbNetMapDao.listByLoadBalancerId(loadBalancerId); + if (lbNetMaps != null) { + for (LoadBalancerNetworkMapVO lbNetMap : lbNetMaps) { + _lbNetMapDao.remove(lbNetMap.getId()); + } + } + + List lbPortMaps = ApiDBUtils.listLoadBalancerAdditionalPorts(loadBalancerId); + if (lbPortMaps != null) { + for (LoadBalancerPortMapVO lbPortMap : lbPortMaps) { + if (lbPortMap.getLoadBalancerId() == loadBalancerId) { // FIXME Double-check lbID because query doesn't seem to be working + _lbPortMapDao.remove(lbPortMap.getId()); + } + } + } + + List lbOptions = _lbOptionsDao.listByLoadBalancerId(loadBalancerId); + if (lbOptions != null) { + for (LoadBalancerOptionsVO lbOption : lbOptions) { + if (lbOption.getLoadBalancerId() == loadBalancerId) { + _lbOptionsDao.remove(lbOption.getId()); + } + } + } + FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId()); if (relatedRule != null) { - s_logger.warn("Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + - "; leaving it in Revoke state"); + s_logger.warn("Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); return false; } else { _firewallMgr.removeRule(lb); @@ -1577,8 +1720,9 @@ public List doInTransaction(TransactionStatus status) { @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, String lbProtocol, Boolean forDisplay) throws NetworkRuleConflictException, - InsufficientAddressCapacityException { + Long ipAddrId, String protocol, String algorithm, long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol, Boolean forDisplay, List additionalPortMap, String cache, + String serviceDownAction, String healthCheckDestination, String expectedHealthcheck, String healthcheckType, boolean skipDnsError, boolean dsr) + throws NetworkRuleConflictException, InsufficientAddressCapacityException { Account lbOwner = _accountMgr.getAccount(lbOwnerId); if (srcPortStart != srcPortEnd) { @@ -1636,7 +1780,10 @@ public LoadBalancer createPublicLoadBalancerRule(String xId, String name, String } result = createPublicLoadBalancer(xId, name, description, srcPortStart, defPortStart, ipVO.getId(), protocol, algorithm, openFirewall, CallContext.current(), - lbProtocol, forDisplay); + lbProtocol, forDisplay, additionalPortMap, cache, + serviceDownAction, healthCheckDestination, expectedHealthcheck, healthcheckType, skipDnsError, dsr); + } catch (CloudRuntimeException e) { + throw e; } catch (Exception ex) { s_logger.warn("Failed to create load balancer due to ", ex); if (ex instanceof NetworkRuleConflictException) { @@ -1669,9 +1816,9 @@ public LoadBalancer createPublicLoadBalancerRule(String xId, String name, String @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 String lbProtocol, final Boolean forDisplay) + 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 String lbProtocol, final Boolean forDisplay, final List additionalPortMap, final String cache, + final String serviceDownAction, final String healthCheckDestination, final String expectedHealthcheck, final String healthcheckType, final boolean skipDnsError, final boolean dsr) throws NetworkRuleConflictException { if (!NetUtils.isValidPort(destPort)) { @@ -1702,8 +1849,7 @@ public LoadBalancer createPublicLoadBalancer(final String xId, final String name final Long networkId = ipAddr.getAssociatedWithNetworkId(); if (networkId == null) { - InvalidParameterValueException ex = - new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network"); + InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network"); ex.addProxyObject(ipAddr.getUuid(), "sourceIpId"); throw ex; } @@ -1713,25 +1859,29 @@ public LoadBalancer createPublicLoadBalancer(final String xId, final String name _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(), + 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); + LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), new ArrayList(), new ArrayList(), + sourceIp, null, lbProtocol); + loadBalancing.setSkipDnsError(skipDnsError); + loadBalancing.setDsr(dsr); + loadBalancing.setAdditionalPortMap(additionalPortMap); if (!validateLbRule(loadBalancing)) { throw new InvalidParameterValueException("LB service provider cannot support this rule"); } - return Transaction.execute(new TransactionCallbackWithException() { + if(loadBalancing.isDsr()){ + validatePortsForDSRLoadBalancer(loadBalancing); + } + + LoadBalancerVO lb = 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(), lbProtocol); + LoadBalancerVO newRule = new LoadBalancerVO(xId, name, description, sourceIpId, srcPort, destPort, algorithm, networkId, ipAddr.getAllocatedToAccountId(), ipAddr + .getAllocatedInDomainId(), lbProtocol); if (forDisplay != null) { newRule.setDisplay(forDisplay); @@ -1739,15 +1889,28 @@ public LoadBalancerVO doInTransaction(TransactionStatus status) throws NetworkRu // 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); + LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), new ArrayList(), + new ArrayList(), sourceIp, null, lbProtocol); + loadBalancing.setCache(cache); + loadBalancing.setServiceDownAction(serviceDownAction); + loadBalancing.setHealthCheckDestination(healthCheckDestination); + loadBalancing.setAdditionalPortMap(additionalPortMap); + loadBalancing.setExpectedHealthCheck(expectedHealthcheck); + loadBalancing.setHealthCheckType(healthcheckType); + loadBalancing.setSkipDnsError(skipDnsError); + loadBalancing.setDsr(dsr); if (!validateLbRule(loadBalancing)) { throw new InvalidParameterValueException("LB service provider cannot support this rule"); } newRule = _lbDao.persist(newRule); + GloboResourceConfigurationVO config = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, newRule.getUuid(), GloboResourceKey.skipDnsError, Boolean.toString(skipDnsError)); + _globoResourceConfigurationDao.persist(config); + + GloboResourceConfigurationVO dsrConfig = new GloboResourceConfigurationVO(GloboResourceType.LOAD_BALANCER, newRule.getUuid(), GloboResourceKey.dsr, Boolean.toString(dsr)); + _globoResourceConfigurationDao.persist(dsrConfig); + //create rule for all CIDRs if (openFirewall) { _firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCallingAccount(), srcPort, srcPort, protocol, null, null, newRule.getId(), networkId); @@ -1760,17 +1923,18 @@ public LoadBalancerVO doInTransaction(TransactionStatus status) throws NetworkRu if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } - s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPort + ", private port " + destPort + - " is added successfully."); + s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPort + ", private port " + destPort + + " is added successfully."); CallContext.current().setEventDetails("Load balancer Id: " + newRule.getId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), - null, LoadBalancingRule.class.getName(), newRule.getUuid()); + CallContext.current().putContextParameter(LoadBalancer.class.getName(), newRule.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null, + LoadBalancingRule.class.getName(), newRule.getUuid()); return newRule; } catch (Exception e) { success = false; if (e instanceof NetworkRuleConflictException) { - throw (NetworkRuleConflictException)e; + throw (NetworkRuleConflictException) e; } throw new CloudRuntimeException("Unable to add rule for ip address id=" + newRule.getSourceIpAddressId(), e); } finally { @@ -1782,6 +1946,47 @@ public LoadBalancerVO doInTransaction(TransactionStatus status) throws NetworkRu } }); + // If load balancer rule was created successfully, add any extra port mappings + if (lb != null && additionalPortMap != null && !additionalPortMap.isEmpty()) { + for (String portMap : additionalPortMap) { + portMap = portMap.trim(); // Remove any white spaces on either side + String[] publicPrivatePorts = portMap.split(":"); // Mapping is of form '80:8080' or '443:8443' + if (publicPrivatePorts == null || publicPrivatePorts.length != 2 || !NetUtils.isValidPort(publicPrivatePorts[0]) || !NetUtils.isValidPort(publicPrivatePorts[1])) { + throw new CloudRuntimeException("Invalid additional port mappings for load balancer rule: " + name); + } + + LoadBalancerPortMapVO lbPortMapVO = new LoadBalancerPortMapVO(lb.getId(), Integer.parseInt(publicPrivatePorts[0]), Integer.parseInt(publicPrivatePorts[1])); + _lbPortMapDao.persist(lbPortMapVO); + } + } + + // If load balancer rule was created successfully, add cache and service down action and health check port options + if (lb != null && (cache != null || serviceDownAction != null || healthCheckDestination != null)) { + String cacheStr = cache != null ? cache.trim() : null; // Remove any white spaces on either side + String serviceDownActionStr = serviceDownAction != null ? serviceDownAction.trim() : null; + String healthCheckDestinationStr = healthCheckDestination != null ? healthCheckDestination.trim() : null; + + LoadBalancerOptionsVO loadBalancerOptionsVO = new LoadBalancerOptionsVO(lb.getId(), cacheStr, serviceDownActionStr, healthCheckDestinationStr); + loadBalancerOptionsVO.setExpectedHealthCheck(expectedHealthcheck); + loadBalancerOptionsVO.setHealthCheckType(healthcheckType); + _lbOptionsDao.persist(loadBalancerOptionsVO); + } + + return lb; + } + + private void validatePortsForDSRLoadBalancer(LoadBalancingRule loadBalancing) { + String message = "In DSR load balancer the public port must always be the same as private port."; + if(loadBalancing.getDefaultPortStart() != loadBalancing.getSourcePortStart()){ + throw new InvalidParameterValueException(message); + } + for(String portPair : loadBalancing.getAdditionalPortMap()){ + String publicPort = portPair.split(":")[0]; + String privatePort = portPair.split(":")[1]; + if(!publicPort.equals(privatePort)){ + throw new InvalidParameterValueException(message); + } + } } @Override @@ -1859,23 +2064,56 @@ protected boolean applyLbRules(Network network, List rules) t return handled; } - private LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb) { + @Override + public LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb) { List policyList = getStickinessPolicies(lb.getId()); Ip sourceIp = getSourceIp(lb); LbSslCert sslCert = getLbSslCert(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, null, policyList, null, sourceIp, sslCert, lb.getLbProtocol()); + List additionalNetworks = new ArrayList(); + for (LoadBalancerNetworkMapVO lbNetMap : _lbNetMapDao.listByLoadBalancerId(lb.getId())) { + additionalNetworks.add(lbNetMap.getNetworkId()); + } + loadBalancing.setAdditionalNetworks(additionalNetworks); + + List additionalPorts = new ArrayList(); + List lbPortMaps = _lbPortMapDao.listByLoadBalancerId(lb.getId()); + if (lbPortMaps != null) { + for (LoadBalancerPortMapVO lbPortMap : lbPortMaps) { + additionalPorts.add(lbPortMap.getPublicPort() + ":" + lbPortMap.getPrivatePort()); + } + } + loadBalancing.setAdditionalPortMap(additionalPorts); + + List lbOptions = _lbOptionsDao.listByLoadBalancerId(lb.getId()); + if (lbOptions != null) { + for (LoadBalancerOptionsVO lbOption : lbOptions) { + if (lbOption.getLoadBalancerId() == lb.getId()) { + loadBalancing.setCache(lbOption.getCache()); + loadBalancing.setServiceDownAction(lbOption.getServiceDownAction()); + loadBalancing.setHealthCheckDestination(lbOption.getHealthCheckDestination()); + loadBalancing.setExpectedHealthCheck(lbOption.getExpectedHealthCheck()); + loadBalancing.setHealthCheckType(lbOption.getHealthCheckType()); + } + } + } + + GloboResourceConfigurationVO dsrConfig = _globoResourceConfigurationDao.getFirst(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.dsr); + loadBalancing.setDsr(dsrConfig != null && dsrConfig.getBooleanValue()); + + List hcPolicyList = getHealthCheckPolicies(lb.getId()); + loadBalancing.setHealthCheckPolicies(hcPolicyList); + + List dstList = getExistingDestinations(lb.getId()); + loadBalancing.setDestinations(dstList); + if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(lb.getId())) { // Get the associated VmGroup AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.listByAll(lb.getId(), null).get(0); LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, vmGroup.getState(), lb); loadBalancing.setAutoScaleVmGroup(lbAutoScaleVmGroup); - } else { - List dstList = getExistingDestinations(lb.getId()); - loadBalancing.setDestinations(dstList); - List hcPolicyList = getHealthCheckPolicies(lb.getId()); - loadBalancing.setHealthCheckPolicies(hcPolicyList); } return loadBalancing; @@ -2028,6 +2266,13 @@ public boolean removeAllLoadBalanacersForNetwork(long networkId, Account caller, } } } + + // Check if network is in any load balancer from another network (multi-network load balancing) + List lbNetMapList = _lbNetMapDao.listByNetworkId(networkId); + for (LoadBalancerNetworkMapVO lbNetMap : lbNetMapList) { + this.removeNetworksFromLoadBalancer(lbNetMap.getLoadBalancerId(), Arrays.asList(networkId)); + } + return true; } @@ -2050,9 +2295,8 @@ public List getHealthCheckPolicies(long lbId) { for (LBHealthCheckPolicyVO policy : hcDbpolicies) { String pingpath = policy.getpingpath(); - LbHealthCheckPolicy hDbPolicy = - new LbHealthCheckPolicy(pingpath, policy.getDescription(), policy.getResponseTime(), policy.getHealthcheckInterval(), policy.getHealthcheckThresshold(), - policy.getUnhealthThresshold(), policy.isRevoke()); + LbHealthCheckPolicy hDbPolicy = new LbHealthCheckPolicy(pingpath, policy.getDescription(), policy.getResponseTime(), policy.getHealthcheckInterval(), + policy.getHealthcheckThresshold(), policy.getUnhealthThresshold(), policy.isRevoke()); healthCheckPolicies.add(hDbPolicy); } return healthCheckPolicies; @@ -2067,9 +2311,9 @@ public List getExistingDestinations(long lbId) { String dstIp = null; for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); - Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); + Nic nic = getLbInstanceNic(lbId, lbVmMap.getInstanceId()); dstIp = lbVmMap.getInstanceIp() == null ? nic.getIPv4Address(): lbVmMap.getInstanceIp(); - LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke()); + LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, nic.getNetworkId(), lbVmMap.getInstanceId(), lbVmMap.isRevoke()); dstList.add(lbDst); } return dstList; @@ -2165,7 +2409,7 @@ public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) { } @Override - public Pair, List> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException { + public Ternary, List, Integer> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException { Account caller = CallContext.current().getCallingAccount(); Long loadBalancerId = cmd.getId(); Boolean applied = cmd.isApplied(); @@ -2185,7 +2429,7 @@ public Pair, List> listLoadBalancerInstances(List List serviceStates = new ArrayList(); List vmLoadBalancerMappings = null; vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); - if(vmLoadBalancerMappings == null) { + if (vmLoadBalancerMappings == null) { String msg = "no VM Loadbalancer Mapping found"; s_logger.error(msg); throw new CloudRuntimeException(msg); @@ -2200,9 +2444,24 @@ public Pair, List> listLoadBalancerInstances(List } } - List userVms = _vmDao.listVirtualNetworkInstancesByAcctAndNetwork(loadBalancer.getAccountId(), loadBalancer.getNetworkId()); + Set userVms = new HashSet(); + userVms.addAll(_vmDao.listVirtualNetworkInstancesByAcctAndNetwork(loadBalancer.getAccountId(), loadBalancer.getNetworkId())); - for (UserVmVO userVm : userVms) { + // Check for VMs in additional networks + List lbNetMaps = _lbNetMapDao.listByLoadBalancerId(loadBalancer.getId()); + if (lbNetMaps != null && !lbNetMaps.isEmpty()) { + for (LoadBalancerNetworkMapVO lbNetMapVO : lbNetMaps) { + userVms.addAll(_vmDao.listVirtualNetworkInstancesByAcctAndNetwork(loadBalancer.getAccountId(), lbNetMapVO.getNetworkId())); + } + } + ArrayList vms = new ArrayList(userVms); + Collections.sort(vms, new Comparator(){ + @Override + public int compare(UserVmVO o1, UserVmVO o2) { + return o1.getHostName().compareTo(o2.getHostName()); + } + }); + for (UserVmVO userVm : vms) { // if the VM is destroyed, being expunged, in an error state, or in // an unknown state, skip it switch (userVm.getState()) { @@ -2214,12 +2473,23 @@ public Pair, List> listLoadBalancerInstances(List } boolean isApplied = appliedInstanceIdList.contains(userVm.getId()); - if ((isApplied && applied) || (!isApplied && !applied)) { + if ( ((isApplied && applied) || (!isApplied && !applied)) && shouldListVm(cmd.getKeyword(), userVm)) { loadBalancerInstances.add(userVm); serviceStates.add(vmServiceState.get(userVm.getId())); } } - return new Pair, List>(loadBalancerInstances, serviceStates); + Integer count = loadBalancerInstances.size(); + loadBalancerInstances = StringUtils.applyPagination(loadBalancerInstances, cmd.getStartIndex(), cmd.getPageSizeVal()); + serviceStates = StringUtils.applyPagination(serviceStates, cmd.getStartIndex(), cmd.getPageSizeVal()); + return new Ternary, List, Integer>(loadBalancerInstances, serviceStates, count); + } + + private boolean shouldListVm(String keyword, UserVmVO userVm) { + if ( keyword == null ){ + return true; + } + + return userVm.getHostName().toLowerCase().contains(keyword.toLowerCase()); } @Override @@ -2411,6 +2681,11 @@ public LoadBalancerVO findById(long lbId) { return _lbDao.findById(lbId); } + @Override + public LoadBalancerVO findByUuid(String uuid) { + return _lbDao.findByUuid(uuid); + } + @Override public LoadBalancerVO findLbByStickinessId(long stickinessPolicyId) { LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId); @@ -2473,22 +2748,46 @@ public boolean applyLbRules(List rules, boolean continueOnErr public Map getLbInstances(long lbId) { Map dstList = new HashMap(); List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId); - LoadBalancerVO lb = _lbDao.findById(lbId); for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); - Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); + Nic nic = getLbInstanceNic(lbId, lbVmMap.getInstanceId()); Ip ip = new Ip(nic.getIPv4Address()); dstList.put(ip, vm); } return dstList; } + @Override + public Nic getLbInstanceNic(long lbId, long vmId) { + LoadBalancerVO lb = _lbDao.findById(lbId); + if (lb == null) { + throw new InvalidParameterValueException("Failed to load load balancer " + lbId + ". LB not found"); + } + LoadBalancerVMMapVO lbVmMap = _lb2VmMapDao.findByLoadBalancerIdAndVmId(lbId, vmId); + if (lbVmMap == null) { + throw new InvalidParameterValueException("Failed to load vm " + vmId + " in load balancer " + lbId + ". There is no association."); + } + + Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vmId); + if (nic != null) { + return nic; + } + + for (LoadBalancerNetworkMapVO lbNetMap : _lbNetMapDao.listByLoadBalancerId(lbId)) { + nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lbNetMap.getNetworkId(), vmId); + if (nic != null) { + return nic; + } + } + throw new InvalidParameterValueException("Failed to load nic of vm " + vmId + " in load balancer " + lbId + ". VM doesn't have network in load balancer networks"); + } + @Override public boolean isLbRuleMappedToVmGuestIp(String vmSecondaryIp) { List lbVmMap = _lb2VmMapDao.listByInstanceIp(vmSecondaryIp); if (lbVmMap == null || lbVmMap.isEmpty()) { - return false; + return false; } return true; } @@ -2595,4 +2894,109 @@ public Long findLBIdByHealtCheckPolicyId(long lbHealthCheckPolicy) { return null; } + @Override + @ActionEvent(eventType = EventTypes.EVENT_REMOVE_NETWORK_FROM_LOAD_BALANCER_RULE, eventDescription = "removing networks from load balancer", async = true) + public boolean removeNetworksFromLoadBalancer(final Long loadBalancerId, final List networkIds) { + CallContext caller = CallContext.current(); + + LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId)); + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid load balancer value: " + loadBalancerId); + } + + _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer); + + if (networkIds.contains(loadBalancer.getNetworkId())) { + throw new InvalidParameterException("Can't delete network " + loadBalancer.getUuid() + " because it is the load balancer network"); + } + + List vmsInNetworks = new ArrayList(); + for (LoadBalancerVMMapVO lbVmMap : _lb2VmMapDao.listByLoadBalancerId(loadBalancerId)) { + Nic nic = getLbInstanceNic(loadBalancerId, lbVmMap.getInstanceId()); + if (nic != null && networkIds.contains(nic.getNetworkId())) { + vmsInNetworks.add(lbVmMap.getInstanceId()); + } + } + + if (!vmsInNetworks.isEmpty()) { + List vmsUuid = new ArrayList(vmsInNetworks.size()); + for (Long vmId : vmsInNetworks) { + vmsUuid.add(_vmDao.findByIdIncludingRemoved(vmId).getUuid()); + } + throw new InvalidParameterException("There are vms in loadbalancer associated with these networks: " + StringUtils.join(vmsUuid, ",")); + } + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (Long networkId : networkIds) { + LoadBalancerNetworkMapVO lbNetMap = _lbNetMapDao.findByLoadBalancerIdAndNetworkId(loadBalancerId, networkId); + if (lbNetMap == null) { + throw new InvalidParameterException("Network " + networkId + " is not associated with loadbalancer id " + loadBalancerId); + } + _lbNetMapDao.remove(lbNetMap.getId()); + } + } + }); + return true; + } + + + @Override + public boolean isLinkedChildLoadBalancer(String lbuuid) { + List linkedConfig = globoConfigDao.getConfiguration(GloboResourceType.LOAD_BALANCER, lbuuid, GloboResourceKey.linkedLoadBalancer); + + if (linkedConfig != null) { + return linkedConfig.size() > 0; + } + return false; + } + + @Override + public boolean isLinkedParentLoadBalancer(String lbuuid) { + List linkedConfig = globoConfigDao.getConfigsByValue(GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer, lbuuid); + + if (linkedConfig != null) { + return linkedConfig.size() > 0; + } + return false; + } + + @Override + public void throwExceptionIfIsChildLoadBalancer(Long id, String operation) throws CloudRuntimeException{ + LoadBalancer lb = findById(id); + List linkedConfig = globoConfigDao.getConfiguration(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.linkedLoadBalancer); + + if (linkedConfig != null && linkedConfig.size() > 0) { + GloboResourceConfigurationVO globoResourceConfigurationVO = linkedConfig.get(0); + LoadBalancerVO parent = findByUuid(globoResourceConfigurationVO.getValue()); + throw new CloudRuntimeException("Can not execute '" + operation + "', Load balancer '" +lb.getName() + "' is linked with parent '" + parent.getName() + "'. Unlink lb before try to execute " + operation + "!" ); + } + + } + + @Override + public void throwExceptionIfIsParentLoadBalancer(Long id, String operation) { + LoadBalancer lb = findById(id); + List linkedConfigs = globoConfigDao.getConfigsByValue(GloboResourceType.LOAD_BALANCER, GloboResourceKey.linkedLoadBalancer, lb.getUuid()); + + if (linkedConfigs != null && linkedConfigs.size() > 0) { + List childs = new ArrayList<>(); + for (GloboResourceConfigurationVO config : linkedConfigs) { + LoadBalancerVO child = findByUuid(config.getValue()); + if ( child != null) { + childs.add(child.getName()); + } + } + + throw new CloudRuntimeException("Can not execute '" + operation + "', Load balancer '" +lb.getName() + "' is linked with child(s)'" + childs + "'. Unlink childs(s) before try to execute " + operation + "!" ); + } + } + + @Override + public void throwExceptionIfIsParentLoadBalancer(List ids, String operation) { + for (Long id : ids) { + throwExceptionIfIsParentLoadBalancer(id, operation); + } + } } diff --git a/server/src/com/cloud/network/router/CommandSetupHelper.java b/server/src/com/cloud/network/router/CommandSetupHelper.java index c8d85fe26446..8fb4858532fc 100644 --- a/server/src/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/com/cloud/network/router/CommandSetupHelper.java @@ -26,6 +26,9 @@ import javax.inject.Inject; +import com.cloud.server.ResourceTag; +import com.cloud.tags.TagKeysBuilder; +import com.cloud.tags.dao.ResourceTagDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -173,6 +176,9 @@ public class CommandSetupHelper { private VlanDao _vlanDao; @Inject private IPAddressDao _ipAddressDao; + @Inject + private ResourceTagDao _resourceTagDao; + @Inject private RouterControlHelper _routerControlHelper; @@ -1004,6 +1010,23 @@ public SetupGuestNetworkCommand createSetupGuestNetworkCommand(final DomainRoute return setupCmd; } + public static void buildTagMetadata(VmDataCommand cmd, long vmId,ResourceTagDao resourceTagDao) { + List resourceTags = resourceTagDao.listBy(vmId, ResourceTag.ResourceObjectType.UserVm); + + TagKeysBuilder tagKeysBuilder = new TagKeysBuilder(); + for (ResourceTag resourceTag : resourceTags) { + String key = resourceTag.getKey(); + if (key != null && !key.isEmpty()){ + String value = resourceTag.getValue() != null ? resourceTag.getValue() : ""; + String metadataKey = TagKeysBuilder.getKeyMetadata(key); + cmd.addVmData("metadata", metadataKey, value); + + } + } + + cmd.addVmData("metadata", TagKeysBuilder.TAGKEYS_METADATA_KEY, tagKeysBuilder.buildTagKeys((List)resourceTags)); + } + private VmDataCommand generateVmDataCommand(final VirtualRouter router, final String vmPrivateIpAddress, final String userData, final String serviceOffering, final String zoneName, final String guestIpAddress, final String vmName, final String vmInstanceName, final long vmId, final String vmUuid, final String publicKey, final long guestNetworkId) { @@ -1021,6 +1044,7 @@ private VmDataCommand generateVmDataCommand(final VirtualRouter router, final St cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName)); cmd.addVmData("metadata", "local-ipv4", guestIpAddress); cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vmName)); + buildTagMetadata(cmd,vmId,_resourceTagDao); if (dcVo.getNetworkType() == NetworkType.Basic) { cmd.addVmData("metadata", "public-ipv4", guestIpAddress); cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vmName)); diff --git a/server/src/com/cloud/network/router/NetworkHelper.java b/server/src/com/cloud/network/router/NetworkHelper.java index 04604e19d783..80986526b2ea 100644 --- a/server/src/com/cloud/network/router/NetworkHelper.java +++ b/server/src/com/cloud/network/router/NetworkHelper.java @@ -92,4 +92,6 @@ public abstract LinkedHashMap> configureGues throws ConcurrentOperationException, InsufficientAddressCapacityException; public boolean validateHAProxyLBRule(final LoadBalancingRule rule); + + public boolean updateVMMetadaInVrouter(long userVmId, Map vmData) throws InsufficientCapacityException, ResourceUnavailableException; } diff --git a/server/src/com/cloud/network/router/NetworkHelperImpl.java b/server/src/com/cloud/network/router/NetworkHelperImpl.java index fa17e7bc3159..ea0d67a728a4 100644 --- a/server/src/com/cloud/network/router/NetworkHelperImpl.java +++ b/server/src/com/cloud/network/router/NetworkHelperImpl.java @@ -16,6 +16,16 @@ // under the License. package com.cloud.network.router; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfileImpl; +import com.cloud.vm.dao.UserVmDao; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -154,6 +164,15 @@ public class NetworkHelperImpl implements NetworkHelper { @Inject ConfigurationDao _configDao; + @Inject + private RouterControlHelper _routerControlHelper; + + @Inject + private UserVmDao _vmDao; + + @Inject + private DataCenterDao _dcDao; + protected final Map> hypervisorsMap = new HashMap<>(); @PostConstruct @@ -163,7 +182,6 @@ protected void setupHypervisorsMap() { hypervisorsMap.put(HypervisorType.VMware, VirtualNetworkApplianceManager.RouterTemplateVmware); hypervisorsMap.put(HypervisorType.Hyperv, VirtualNetworkApplianceManager.RouterTemplateHyperV); hypervisorsMap.put(HypervisorType.LXC, VirtualNetworkApplianceManager.RouterTemplateLxc); - hypervisorsMap.put(HypervisorType.Ovm3, VirtualNetworkApplianceManager.RouterTemplateOvm3); } @Override @@ -841,6 +859,68 @@ public boolean validateHAProxyLBRule(final LoadBalancingRule rule) { return true; } + + @Override + public boolean updateVMMetadaInVrouter(long userVmId, Map vmData) throws InsufficientCapacityException, ResourceUnavailableException { + UserVmVO vm = _vmDao.findById(userVmId); + + if (vm == null) { + throw new CloudRuntimeException("UserVm " + userVmId + " do not exists!"); + } + + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + + List nics = _nicDao.listByVmId(vm.getId()); + if (nics == null || nics.isEmpty()) { + s_logger.error("unable to find any nics for vm " + vm.getUuid()); + throw new CloudRuntimeException("unable to find any nics for vm " + vm.getUuid()); + } + boolean result = true; + 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)); + + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); + + UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network); + if (element == null) { + throw new CloudRuntimeException("Can't find network element for " + Network.Service.UserData.getName() + " provider needed for UserData update"); + } + + List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + if (routers == null || routers.isEmpty()) { + s_logger.debug("Can't find virtual router element in network " + network.getId()); + return false; + } + + for (DomainRouterVO router : routers) { + final VmDataCommand cmd = new VmDataCommand(nic.getIPv4Address(), vm.getInstanceName(), _networkModel.getExecuteInSeqNtwkElmtCmd()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(nic.getNetworkId(), router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); + cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + + for (String key : vmData.keySet()) { + String value = vmData.get(key); + cmd.addVmData("metadata", key, value); + } + final Commands cmds = new Commands(Command.OnError.Stop); + cmds.addCommand("vmdata", cmd); + boolean resultRouter = sendCommandsToRouter(router, cmds); + + if (!resultRouter) { + s_logger.error("Failed to update userdata for vm " + vm.getUuid() + " and nic " + nic.getUuid() + " vr " + router.getUuid()); + } + result = resultRouter && result; + } + + } + return result; + } + /* * This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number * like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index a291b3590b25..36027a2fa2ec 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -18,8 +18,10 @@ import java.util.List; + import org.apache.cloudstack.framework.config.ConfigKey; + import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceUnavailableException; @@ -33,49 +35,45 @@ /** * NetworkManager manages the network for the different end users. + * */ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkApplianceService { - static final String RouterTemplateXenCK = "router.template.xenserver"; static final String RouterTemplateKvmCK = "router.template.kvm"; static final String RouterTemplateVmwareCK = "router.template.vmware"; static final String RouterTemplateHyperVCK = "router.template.hyperv"; static final String RouterTemplateLxcCK = "router.template.lxc"; - static final String RouterTemplateOvm3CK = "router.template.ovm3"; static final String SetServiceMonitorCK = "network.router.EnableServiceMonitoring"; static final String RouterAlertsCheckIntervalCK = "router.alerts.check.interval"; + static final String RouterReprovisionOnOutOfBandMigrationCK = "router.reboot.when.outofband.migrated"; static final ConfigKey RouterTemplateXen = new ConfigKey(String.class, RouterTemplateXenCK, "Advanced", "SystemVM Template (XenServer)", - "Name of the default router template on Xenserver.", true, ConfigKey.Scope.Zone, null); + "Name of the default router template on Xenserver.", true, ConfigKey.Scope.Zone, null); static final ConfigKey RouterTemplateKvm = new ConfigKey(String.class, RouterTemplateKvmCK, "Advanced", "SystemVM Template (KVM)", - "Name of the default router template on KVM.", true, ConfigKey.Scope.Zone, null); + "Name of the default router template on KVM.", true, ConfigKey.Scope.Zone, null); static final ConfigKey RouterTemplateVmware = new ConfigKey(String.class, RouterTemplateVmwareCK, "Advanced", "SystemVM Template (vSphere)", - "Name of the default router template on Vmware.", true, ConfigKey.Scope.Zone, null); + "Name of the default router template on Vmware.", true, ConfigKey.Scope.Zone, null); static final ConfigKey RouterTemplateHyperV = new ConfigKey(String.class, RouterTemplateHyperVCK, "Advanced", "SystemVM Template (HyperV)", - "Name of the default router template on Hyperv.", true, ConfigKey.Scope.Zone, null); + "Name of the default router template on Hyperv.", true, ConfigKey.Scope.Zone, null); static final ConfigKey RouterTemplateLxc = new ConfigKey(String.class, RouterTemplateLxcCK, "Advanced", "SystemVM Template (LXC)", - "Name of the default router template on LXC.", true, ConfigKey.Scope.Zone, null); - static final ConfigKey RouterTemplateOvm3 = new ConfigKey(String.class, RouterTemplateOvm3CK, "Advanced", "SystemVM Template (Ovm3)", - "Name of the default router template on Ovm3.", true, ConfigKey.Scope.Zone, null); - + "Name of the default router template on LXC.", true, ConfigKey.Scope.Zone, null); static final ConfigKey SetServiceMonitor = new ConfigKey(String.class, SetServiceMonitorCK, "Advanced", "true", "service monitoring in router enable/disable option, default true", true, ConfigKey.Scope.Zone, null); - static final ConfigKey RouterAlertsCheckInterval = new ConfigKey(Integer.class, RouterAlertsCheckIntervalCK, "Advanced", "1800", "Interval (in seconds) to check for alerts in Virtual Router.", false, ConfigKey.Scope.Global, null); static final ConfigKey routerVersionCheckEnabled = new ConfigKey("Advanced", Boolean.class, "router.version.check", "true", "If true, router minimum required version is checked before sending command", false); static final ConfigKey UseExternalDnsServers = new ConfigKey(Boolean.class, "use.external.dns", "Advanced", "false", "Bypass internal dns, use external dns1 and dns2", true, ConfigKey.Scope.Zone, null); + static final ConfigKey RouterReprovisionOnOutOfBandMigration = new ConfigKey(Boolean.class, RouterReprovisionOnOutOfBandMigrationCK, "Advanced", "false", + "Reboot routers when they are migrated out of band in order to reprovision", true, ConfigKey.Scope.Zone, null); public static final int DEFAULT_ROUTER_VM_RAMSIZE = 256; // 256M public static final int DEFAULT_ROUTER_CPU_MHZ = 500; // 500 MHz public static final boolean USE_POD_VLAN = false; - public static final int DEFAULT_PRIORITY = 100; - public static final int DEFAULT_DELTA = 2; /** - /* + /* * Send ssh public/private key pair to specified host * @param hostId * @param pubKey @@ -101,4 +99,5 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA public boolean prepareAggregatedExecution(Network network, List routers) throws AgentUnavailableException, ResourceUnavailableException; public boolean completeAggregatedExecution(Network network, List routers) throws AgentUnavailableException, ResourceUnavailableException; + } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 1985deaefa80..5fa310f282a8 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -17,6 +17,9 @@ package com.cloud.network.router; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.StringUtils; import java.math.BigInteger; import java.nio.charset.Charset; import java.security.MessageDigest; @@ -99,6 +102,7 @@ import com.cloud.cluster.dao.ManagementServerHostDao; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; + import com.cloud.configuration.ZoneConfig; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -195,6 +199,7 @@ import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; + import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.User; @@ -243,13 +248,13 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; - +import com.cloud.vm.VirtualMachine.State; /** * VirtualNetworkApplianceManagerImpl manages the different types of virtual * network appliances available in the Cloud Stack. */ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements VirtualNetworkApplianceManager, VirtualNetworkApplianceService, VirtualMachineGuru, Listener, -Configurable, StateListener { + Configurable, StateListener { private static final Logger s_logger = Logger.getLogger(VirtualNetworkApplianceManagerImpl.class); @Inject private EntityManager _entityMgr; @@ -308,6 +313,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V @Inject protected VpcDao _vpcDao; @Inject protected ApiAsyncJobDispatcher _asyncDispatcher; @Inject private OpRouterMonitorServiceDao _opRouterMonitorServiceDao; + @Inject + ResourceTagDao _resourceTagDao; @Inject protected NetworkTopologyContext _networkTopologyContext; @@ -346,6 +353,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V private BlockingQueue _vrUpdateQueue; @Override + @ActionEvent(eventType = EventTypes.EVENT_ROUTER_DESTROY, eventDescription = "destroying router Vm", async = true) public VirtualRouter destroyRouter(final long routerId, final Account caller, final Long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException { return _nwHelper.destroyRouter(routerId, caller, callerUserId); } @@ -672,6 +680,67 @@ public boolean stop() { protected VirtualNetworkApplianceManagerImpl() { } + + private VmDataCommand generateVmDataCommand(final VirtualRouter router, final String vmPrivateIpAddress, final String userData, final String serviceOffering, final String zoneName, + final String guestIpAddress, final String vmName, final String vmInstanceName, final long vmId, final String vmUuid, final String publicKey, final long guestNetworkId) { + final VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress, vmName, _networkModel.getExecuteInSeqNtwkElmtCmd()); + + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, getRouterIpInNetwork(guestNetworkId, router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); + cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + // if you add new metadata files, also edit systemvm/patches/debian/config/var/www/html/latest/.htaccess + cmd.addVmData("userdata", "user-data", userData); + cmd.addVmData("metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering)); + cmd.addVmData("metadata", "availability-zone", StringUtils.unicodeEscape(zoneName)); + cmd.addVmData("metadata", "local-ipv4", guestIpAddress); + cmd.addVmData("metadata", "local-hostname", StringUtils.unicodeEscape(vmName)); + if (dcVo.getNetworkType() == NetworkType.Basic) { + cmd.addVmData("metadata", "public-ipv4", guestIpAddress); + cmd.addVmData("metadata", "public-hostname", StringUtils.unicodeEscape(vmName)); + } else { + if (router.getPublicIpAddress() == null) { + cmd.addVmData("metadata", "public-ipv4", guestIpAddress); + } else { + cmd.addVmData("metadata", "public-ipv4", router.getPublicIpAddress()); + } + cmd.addVmData("metadata", "public-hostname", router.getPublicIpAddress()); + } + if (vmUuid == null) { + setVmInstanceId(vmInstanceName, vmId, cmd); + } else { + setVmInstanceId(vmUuid, cmd); + } + + CommandSetupHelper.buildTagMetadata(cmd, vmId,_resourceTagDao); + cmd.addVmData("metadata", "public-keys", publicKey); + + String cloudIdentifier = _configDao.getValue("cloud.identifier"); + if (cloudIdentifier == null) { + cloudIdentifier = ""; + } else { + cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}"; + } + cmd.addVmData("metadata", "cloud-identifier", cloudIdentifier); + + return cmd; + } + + + + private void setVmInstanceId(final String vmUuid, final VmDataCommand cmd) { + cmd.addVmData("metadata", "instance-id", vmUuid); + cmd.addVmData("metadata", "vm-id", vmUuid); + } + + private void setVmInstanceId(final String vmInstanceName, final long vmId, final VmDataCommand cmd) { + cmd.addVmData("metadata", "instance-id", vmInstanceName); + cmd.addVmData("metadata", "vm-id", String.valueOf(vmId)); + } + + protected class NetworkUsageTask extends ManagedContextRunnable { public NetworkUsageTask() { @@ -1496,6 +1565,32 @@ public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile return true; } + private DomainRouterVO waitRouter(DomainRouterVO router) { + DomainRouterVO vm = _routerDao.findById(router.getId()); + + if (s_logger.isDebugEnabled()) + s_logger.debug("Router " + router.getInstanceName() + " is not fully up yet, we will wait"); + while (vm.getState() == State.Starting) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + // reload to get the latest state info + vm = _routerDao.findById(router.getId()); + } + + if (vm.getState() == State.Running) { + if (s_logger.isDebugEnabled()) + s_logger.debug("Router " + router.getInstanceName() + " is now fully up"); + + return router; + } + + s_logger.warn("Router " + router.getInstanceName() + " failed to start. current state: " + vm.getState()); + return null; + } + protected StringBuilder createGuestBootLoadArgs(final NicProfile guestNic, final String defaultDns1, final String defaultDns2, final DomainRouterVO router) { final long guestNetworkId = guestNic.getNetworkId(); final NetworkVO guestNetwork = _networkDao.findById(guestNetworkId); @@ -2248,7 +2343,7 @@ public VirtualRouter startRouter(final long id) throws ResourceUnavailableExcept @Override public VirtualRouter startRouter(final long routerId, final boolean reprogramNetwork) throws ResourceUnavailableException, InsufficientCapacityException, - ConcurrentOperationException { + ConcurrentOperationException { final Account caller = CallContext.current().getCallingAccount(); final User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId()); @@ -2674,4 +2769,27 @@ public boolean prepareAggregatedExecution(final Network network, final List routers) throws AgentUnavailableException, ResourceUnavailableException { return aggregationExecution(Action.Finish, network, routers); } + protected String getRouterControlIp(final long routerId) { + String routerControlIpAddress = null; + final List nics = _nicDao.listByVmId(routerId); + for (final NicVO n : nics) { + final NetworkVO nc = _networkDao.findById(n.getNetworkId()); + if (nc != null && nc.getTrafficType() == TrafficType.Control) { + routerControlIpAddress = n.getIPv4Address(); + // router will have only one control ip + break; + } + } + + if (routerControlIpAddress == null) { + s_logger.warn("Unable to find router's control ip in its attached NICs!. routerId: " + routerId); + final DomainRouterVO router = _routerDao.findById(routerId); + return router.getPrivateIpAddress(); + } + + return routerControlIpAddress; + } + protected String getRouterIpInNetwork(final long networkId, final long instanceId) { + return _nicDao.getIpAddress(networkId, instanceId); + } } diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java index a8dd225c54c6..ac1910cb4a7f 100644 --- a/server/src/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -38,6 +38,7 @@ import javax.mail.internet.InternetAddress; import javax.naming.ConfigurationException; +import com.cloud.globodictionary.GloboDictionaryService; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -124,6 +125,8 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager { private ProjectInvitationJoinDao _projectInvitationJoinDao; @Inject protected ResourceTagDao _resourceTagDao; + @Inject + protected GloboDictionaryService _globoDictionaryManager; protected boolean _invitationRequired = false; protected long _invitationTimeOut = 86400000; @@ -176,7 +179,7 @@ public boolean stop() { @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_CREATE, eventDescription = "creating project", create = true) @DB - public Project createProject(final String name, final String displayText, String accountName, final Long domainId) throws ResourceAllocationException { + public Project createProject(final String name, final String displayText, String accountName, final Long domainId, final String businessServiceId, final String clientId, final String componentId, final String subComponentId, final String productId, final Boolean detailedUsage) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); Account owner = caller; @@ -194,6 +197,36 @@ public Project createProject(final String name, final String displayText, String owner = _accountMgr.finalizeOwner(caller, accountName, domainId, null); } + if(businessServiceId != null){ + if(!businessServiceId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, businessServiceId) == null){ + throw new InvalidParameterValueException("Business Service with ID "+ businessServiceId +" does not exist"); + } + } + + if(clientId != null){ + if(!clientId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.CLIENT, clientId) == null){ + throw new InvalidParameterValueException("Client with ID "+ clientId +" does not exist"); + } + } + + if(componentId != null){ + if(!componentId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.COMPONENT, componentId) == null){ + throw new InvalidParameterValueException("Component with ID "+ componentId +" does not exist"); + } + } + + if(subComponentId != null){ + if(!subComponentId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.SUB_COMPONENT, subComponentId) == null){ + throw new InvalidParameterValueException("Sub-component with ID "+ subComponentId +" does not exist"); + } + } + + if(productId != null){ + if(!productId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.PRODUCT, productId) == null){ + throw new InvalidParameterValueException("Product with ID "+ productId +" does not exist"); + } + } + //don't allow 2 projects with the same name inside the same domain if (_projectDao.findByNameAndDomain(name, owner.getDomainId()) != null) { throw new InvalidParameterValueException("Project with name " + name + " already exists in domain id=" + owner.getDomainId()); @@ -213,7 +246,7 @@ public Project doInTransaction(TransactionStatus status) { Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.ACCOUNT_TYPE_PROJECT, null, domainId, null, null, UUID.randomUUID().toString()); - Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId())); + Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId(), businessServiceId, clientId, componentId, subComponentId, productId, detailedUsage)); //assign owner to the project assignAccountToProject(project, ownerFinal.getId(), ProjectAccount.Role.Admin); @@ -451,7 +484,7 @@ public boolean canModifyProjectAccount(Account caller, long accountId) { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PROJECT_UPDATE, eventDescription = "updating project", async = true) - public Project updateProject(final long projectId, final String displayText, final String newOwnerName) throws ResourceAllocationException { + public Project updateProject(final long projectId, final String displayText, final String newOwnerName, final String businessServiceId, final String clientId, final String componentId, final String subComponentId, final String productId, final Boolean detailedUsage) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); //check that the project exists @@ -467,43 +500,83 @@ public Project updateProject(final long projectId, final String displayText, fin Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException { - if (displayText != null) { - project.setDisplayText(displayText); - _projectDao.update(projectId, project); - } + if (displayText != null) { + project.setDisplayText(displayText); + } - if (newOwnerName != null) { - //check that the new owner exists - Account futureOwnerAccount = _accountMgr.getActiveAccountByName(newOwnerName, project.getDomainId()); - if (futureOwnerAccount == null) { - throw new InvalidParameterValueException("Unable to find account name=" + newOwnerName + " in domain id=" + project.getDomainId()); - } - Account currentOwnerAccount = getProjectOwner(projectId); - if (currentOwnerAccount.getId() != futureOwnerAccount.getId()) { - ProjectAccountVO futureOwner = _projectAccountDao.findByProjectIdAccountId(projectId, futureOwnerAccount.getAccountId()); - if (futureOwner == null) { - throw new InvalidParameterValueException("Account " + newOwnerName + - " doesn't belong to the project. Add it to the project first and then change the project's ownership"); + if (detailedUsage != null) { + project.setDetailedUsage(detailedUsage); + } + + if(businessServiceId != null){ + if(!businessServiceId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, businessServiceId) == null){ + throw new InvalidParameterValueException("Business Service with ID "+ businessServiceId +" does not exist"); + } + project.setBusinessServiceId(businessServiceId); } - //do resource limit check - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project); + if(clientId != null) { + if(!clientId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.CLIENT, clientId) == null){ + throw new InvalidParameterValueException("Client with ID "+ clientId +" does not exist"); + } + project.setClientId(clientId); + } - //unset the role for the old owner - ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); - currentOwner.setAccountRole(Role.Regular); - _projectAccountDao.update(currentOwner.getId(), currentOwner); - _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); + if(componentId != null) { + if(!componentId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.COMPONENT, componentId) == null){ + throw new InvalidParameterValueException("Component with ID "+ componentId +" does not exist"); + } + project.setComponentId(componentId); + } - //set new owner - futureOwner.setAccountRole(Role.Admin); - _projectAccountDao.update(futureOwner.getId(), futureOwner); - _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); + if(subComponentId != null) { + if(!subComponentId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.SUB_COMPONENT, subComponentId) == null){ + throw new InvalidParameterValueException("Sub-component with ID "+ subComponentId +" does not exist"); + } + project.setSubComponentId(subComponentId); + } - } else { - s_logger.trace("Future owner " + newOwnerName + "is already the owner of the project id=" + projectId); - } - } + if(productId != null) { + if(!productId.trim().equals("") && _globoDictionaryManager.get(GloboDictionaryService.GloboDictionaryEntityType.PRODUCT, productId) == null){ + throw new InvalidParameterValueException("Product with ID "+ productId +" does not exist"); + } + project.setProductId(productId); + } + + _projectDao.update(projectId, project); + + if (newOwnerName != null) { + //check that the new owner exists + Account futureOwnerAccount = _accountMgr.getActiveAccountByName(newOwnerName, project.getDomainId()); + if (futureOwnerAccount == null) { + throw new InvalidParameterValueException("Unable to find account name=" + newOwnerName + " in domain id=" + project.getDomainId()); + } + Account currentOwnerAccount = getProjectOwner(projectId); + if (currentOwnerAccount.getId() != futureOwnerAccount.getId()) { + ProjectAccountVO futureOwner = _projectAccountDao.findByProjectIdAccountId(projectId, futureOwnerAccount.getAccountId()); + if (futureOwner == null) { + throw new InvalidParameterValueException("Account " + newOwnerName + + " doesn't belong to the project. Add it to the project first and then change the project's ownership"); + } + + //do resource limit check + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project); + + //unset the role for the old owner + ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); + currentOwner.setAccountRole(Role.Regular); + _projectAccountDao.update(currentOwner.getId(), currentOwner); + _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); + + //set new owner + futureOwner.setAccountRole(Role.Admin); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); + + } else { + s_logger.trace("Future owner " + newOwnerName + "is already the owner of the project id=" + projectId); + } + } } }); @@ -705,7 +778,7 @@ public ProjectInvitation generateTokenBasedInvitation(Project project, String em ProjectInvitation projectInvitation = _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), null, project.getDomainId(), email, token)); try { - _emailInvite.sendInvite(token, email, project.getId()); + _emailInvite.sendInvite(token, email, project); } catch (Exception ex) { s_logger.warn("Failed to send project id=" + project + " invitation to the email " + email + "; removing the invitation record from the db", ex); _projectInvitationDao.remove(projectInvitation.getId()); @@ -959,7 +1032,7 @@ protected PasswordAuthentication getPasswordAuthentication() { } } - public void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException { + public void sendInvite(String token, String email, Project project) throws MessagingException, UnsupportedEncodingException { if (_smtpSession != null) { InternetAddress address = null; if (email != null) { @@ -970,13 +1043,13 @@ public void sendInvite(String token, String email, long projectId) throws Messag } } - String content = "You've been invited to join the CloudStack project id=" + projectId + ". Please use token " + token + " to complete registration"; + String content = "You've been invited to join the CloudStack project '" + project.getName() + "'(ID: " + project.getUuid() +") . Please use token " + token + " to complete registration"; SMTPMessage msg = new SMTPMessage(_smtpSession); msg.setSender(new InternetAddress(_emailSender, _emailSender)); msg.setFrom(new InternetAddress(_emailSender, _emailSender)); msg.addRecipient(RecipientType.TO, address); - msg.setSubject("You are invited to join the cloud stack project id=" + projectId); + msg.setSubject("You are invited to join the cloud stack project " + project.getName()); msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10)); msg.setContent(content, "text/plain"); msg.saveChanges(); diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index ba3c157cf9fa..aca07d43d05c 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.resource; +import com.cloud.utils.component.ComponentContext; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; @@ -47,6 +48,9 @@ import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.identity.ManagementServerNode; @@ -271,6 +275,9 @@ public void setDiscoverers(final List discoverers) { private SearchBuilder _gpuAvailability; + @Inject + MessageBus _messageBus; + private void insertListener(final Integer event, final ResourceListener listener) { List lst = _lifeCycleListeners.get(event); if (lst == null) { @@ -826,6 +833,7 @@ public Host getHost(final long hostId) { } @DB + @ActionEvent(eventType = EventTypes.EVENT_HOST_DELETE, eventDescription = "deleting host") protected boolean doDeleteHost(final long hostId, final boolean isForced, final boolean isForceDeleteStorage) { _accountMgr.getActiveUser(CallContext.current().getCallingUserId()); // Verify that host exists @@ -945,6 +953,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { _agentMgr.notifyMonitorsOfRemovedHost(host.getId(), clusterId); } + publishHostEvent(host, EventTypes.EVENT_HOST_DELETE); + return true; } @@ -1374,6 +1384,7 @@ public boolean checkAndMaintain(final long hostId) { } @Override + @ActionEvent(eventType = EventTypes.EVENT_HOST_UPDATE, eventDescription = "updating host") public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException { final Long hostId = cmd.getId(); final Long guestOSCategoryId = cmd.getOsCategoryId(); @@ -1434,9 +1445,53 @@ public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException { } final HostVO updatedHost = _hostDao.findById(hostId); + + + publishHostEvent(host, EventTypes.EVENT_HOST_UPDATE); + return updatedHost; } + private void publishHostEvent(HostVO host, String eventType) { + try { + EventBus eventBus = ComponentContext.getComponent(EventBus.class); + + + Map eventDescription = new HashMap(); + + eventDescription.put("hostId", host.getUuid()); + eventDescription.put("hostName", host.getName()); + eventDescription.put("hostResourceState", host.getResourceState().toString()); + eventDescription.put("hostStatus", host.getStatus().toString()); + eventDescription.put("hostState", host.getState().toString()); + eventDescription.put("hostPrivateIp", host.getPrivateIpAddress()); + eventDescription.put("hostPublicIp", host.getPublicIpAddress()); + + org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event( + "management-server", + eventType, + null, + Host.class.getSimpleName(), + host.getUuid()); + + event.setDescription(eventDescription); + + try { + eventBus.publish(event); + } catch (EventBusException evx) { + String errMsg = "Failed to publish async job event on the the event bus."; + s_logger.warn(errMsg, evx); + } + + + + } catch (Exception e) { + s_logger.error("Error trying to send evento to message bus", e); + return; // no provider is configured to provide events bus, so just return + } + + } + @Override public Cluster getCluster(final Long clusterId) { return _clusterDao.findById(clusterId); @@ -1837,6 +1892,8 @@ protected HostVO createHostVO(final StartupCommand[] cmds, final ServerResource } } + publishHostEvent(host, EventTypes.EVENT_HOST_CREATE); + return host; } @@ -2003,7 +2060,7 @@ private Host createHostAndAgent(final ServerResource resource, final Map details, final boolean old, final List hostTags, final boolean forRebalance) { HostVO host = null; StartupCommand[] cmds = null; @@ -2112,6 +2169,7 @@ private Host createHostAndAgentDeferred(final ServerResource resource, final Map } @Override + @ActionEvent(eventType = EventTypes.EVENT_HOST_CREATE, eventDescription = "host creating", async = false) public Host createHostAndAgent(final Long hostId, final ServerResource resource, final Map details, final boolean old, final List hostTags, final boolean forRebalance) { final Host host = createHostAndAgent(resource, details, old, hostTags, forRebalance); return host; diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 56c912d29c12..5e36323ee27a 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -344,8 +344,10 @@ import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; import org.apache.cloudstack.api.command.user.loadbalancer.AssignCertToLoadBalancerCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.AssignNetworksToLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.loadbalancer.AssignToLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.loadbalancer.CreateApplicationLoadBalancerCmd; + import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerRuleCmd; @@ -362,6 +364,7 @@ import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; import org.apache.cloudstack.api.command.user.loadbalancer.RemoveCertFromLoadBalancerCmd; import org.apache.cloudstack.api.command.user.loadbalancer.RemoveFromLoadBalancerRuleCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.RemoveNetworksFromLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.loadbalancer.UpdateApplicationLoadBalancerCmd; import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLBHealthCheckPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLBStickinessPolicyCmd; @@ -449,6 +452,7 @@ import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; +import org.apache.cloudstack.api.command.user.vm.ListGloboVirtualMachinesCmd; import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; import org.apache.cloudstack.api.command.user.vm.RemoveIpFromVmNicCmd; @@ -2783,6 +2787,7 @@ public List> getCommands() { cmdList.add(ListAsyncJobsCmd.class); cmdList.add(QueryAsyncJobResultCmd.class); cmdList.add(AssignToLoadBalancerRuleCmd.class); + cmdList.add(AssignNetworksToLoadBalancerRuleCmd.class); cmdList.add(CreateLBStickinessPolicyCmd.class); cmdList.add(CreateLBHealthCheckPolicyCmd.class); cmdList.add(CreateLoadBalancerRuleCmd.class); @@ -2794,6 +2799,7 @@ public List> getCommands() { cmdList.add(ListLoadBalancerRuleInstancesCmd.class); cmdList.add(ListLoadBalancerRulesCmd.class); cmdList.add(RemoveFromLoadBalancerRuleCmd.class); + cmdList.add(RemoveNetworksFromLoadBalancerRuleCmd.class); cmdList.add(UpdateLoadBalancerRuleCmd.class); cmdList.add(CreateIpForwardingRuleCmd.class); cmdList.add(DeleteIpForwardingRuleCmd.class); @@ -2802,6 +2808,7 @@ public List> getCommands() { cmdList.add(ListIpForwardingRulesCmd.class); cmdList.add(CreateNetworkACLCmd.class); cmdList.add(CreateNetworkCmd.class); + cmdList.add(CreateNetworkCmdByAdmin.class); cmdList.add(DeleteNetworkACLCmd.class); cmdList.add(DeleteNetworkCmd.class); cmdList.add(ListNetworkACLsCmd.class); @@ -2864,6 +2871,7 @@ public List> getCommands() { cmdList.add(ExpungeVMCmd.class); cmdList.add(GetVMPasswordCmd.class); cmdList.add(ListVMsCmd.class); + cmdList.add(ListGloboVirtualMachinesCmd.class); cmdList.add(ScaleVMCmd.class); cmdList.add(RebootVMCmd.class); cmdList.add(RemoveNicFromVMCmd.class); diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java index b66fa5f0600d..4522836dde32 100644 --- a/server/src/com/cloud/server/StatsCollector.java +++ b/server/src/com/cloud/server/StatsCollector.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.server; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.VMInstanceDao; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -33,6 +36,12 @@ import javax.inject.Inject; +import com.cloud.server.as.AutoScaleMonitor; +import com.cloud.server.as.AutoScaleCounterCollector; +import org.apache.cloudstack.utils.usage.UsageUtils; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -45,16 +54,13 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.graphite.GraphiteClient; import org.apache.cloudstack.utils.graphite.GraphiteException; -import org.apache.cloudstack.utils.usage.UsageUtils; + import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.HostStatsEntry; -import com.cloud.agent.api.PerformanceMonitorCommand; import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.agent.api.VmDiskStatsEntry; import com.cloud.agent.api.VmNetworkStatsEntry; @@ -74,17 +80,11 @@ import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; + +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceState; + import com.cloud.network.as.AutoScaleManager; -import com.cloud.network.as.AutoScalePolicyConditionMapVO; -import com.cloud.network.as.AutoScalePolicyVO; -import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; -import com.cloud.network.as.AutoScaleVmGroupVO; -import com.cloud.network.as.AutoScaleVmGroupVmMapVO; -import com.cloud.network.as.AutoScaleVmProfileVO; -import com.cloud.network.as.Condition.Operator; -import com.cloud.network.as.ConditionVO; -import com.cloud.network.as.Counter; -import com.cloud.network.as.CounterVO; import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; import com.cloud.network.as.dao.AutoScalePolicyDao; import com.cloud.network.as.dao.AutoScaleVmGroupDao; @@ -94,13 +94,11 @@ import com.cloud.network.as.dao.ConditionDao; import com.cloud.network.as.dao.CounterDao; import com.cloud.org.Cluster; -import com.cloud.resource.ResourceManager; -import com.cloud.resource.ResourceState; -import com.cloud.service.ServiceOfferingVO; -import com.cloud.service.dao.ServiceOfferingDao; + import com.cloud.storage.ImageStoreDetailsUtil; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage.ImageFormat; + import com.cloud.storage.StorageManager; import com.cloud.storage.StorageStats; import com.cloud.storage.VolumeStats; @@ -111,7 +109,6 @@ import com.cloud.user.dao.UserStatisticsDao; import com.cloud.user.dao.VmDiskStatisticsDao; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentMethodInterceptable; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; @@ -125,12 +122,10 @@ import com.cloud.vm.NicVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; -import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VmStats; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDao; /** * Provides real time stats for various agent resources up to x seconds @@ -212,6 +207,8 @@ public String toString() { @Inject private VMInstanceDao _vmInstance; @Inject + private ServiceOfferingDao _serviceOfferingDao; + @Inject private AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao; @Inject private AutoScalePolicyDao _asPolicyDao; @@ -223,13 +220,17 @@ public String toString() { private CounterDao _asCounterDao; @Inject private AutoScaleVmProfileDao _asProfileDao; + @Inject - private ServiceOfferingDao _serviceOfferingDao; + private AutoScaleMonitor _autoScaleMonitor; + @Inject + private AutoScaleCounterCollector _autoScaleCounterCollector; @Inject private HostGpuGroupsDao _hostGpuGroupsDao; @Inject private ImageStoreDetailsUtil imageStoreDetailsUtil; + private ConcurrentHashMap _hostStats = new ConcurrentHashMap(); private final ConcurrentHashMap _VmStats = new ConcurrentHashMap(); private final Map _volumeStats = new ConcurrentHashMap(); @@ -242,6 +243,8 @@ public String toString() { long volumeStatsInterval = -1L; long autoScaleStatsInterval = -1L; + long autoScaleCounterCollectorInterval = -1L; + List hostIds = null; private double _imageStoreCapacityThreshold = 0.90; @@ -287,6 +290,8 @@ private void init(Map configs) { storageStatsInterval = NumbersUtil.parseLong(configs.get("storage.stats.interval"), 60000L); volumeStatsInterval = NumbersUtil.parseLong(configs.get("volume.stats.interval"), 600000L); autoScaleStatsInterval = NumbersUtil.parseLong(configs.get("autoscale.stats.interval"), 60000L); + autoScaleCounterCollectorInterval = NumbersUtil.parseLong(configs.get("autoscale.reading.interval"), 10000L); + /* URI to send statistics to. Currently only Graphite is supported */ String externalStatsUri = configs.get("stats.output.uri"); @@ -337,7 +342,11 @@ private void init(Map configs) { } if (autoScaleStatsInterval > 0) { - _executor.scheduleWithFixedDelay(new AutoScaleMonitor(), 15000L, autoScaleStatsInterval, TimeUnit.MILLISECONDS); + _executor.scheduleWithFixedDelay(_autoScaleMonitor, 15000L, autoScaleStatsInterval, TimeUnit.MILLISECONDS); + } + + if (autoScaleCounterCollectorInterval > 0) { + _executor.scheduleWithFixedDelay(_autoScaleCounterCollector, 15000L, autoScaleCounterCollectorInterval, TimeUnit.MILLISECONDS); } if (vmDiskStatsInterval.value() > 0) { @@ -1025,288 +1034,6 @@ protected void runInContext() { } - class AutoScaleMonitor extends ManagedContextRunnable { - @Override - protected void runInContext() { - try { - if (s_logger.isDebugEnabled()) { - s_logger.debug("AutoScaling Monitor is running..."); - } - // list all AS VMGroups - List asGroups = _asGroupDao.listAll(); - for (AutoScaleVmGroupVO asGroup : asGroups) { - // check group state - if ((asGroup.getState().equals("enabled")) && (is_native(asGroup.getId()))) { - // check minimum vm of group - Integer currentVM = _asGroupVmDao.countByGroup(asGroup.getId()); - if (currentVM < asGroup.getMinMembers()) { - _asManager.doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM); - continue; - } - - //check interval - long now = (new Date()).getTime(); - if (asGroup.getLastInterval() != null) - if ((now - asGroup.getLastInterval().getTime()) < asGroup - .getInterval()) { - continue; - } - - // update last_interval - asGroup.setLastInterval(new Date()); - _asGroupDao.persist(asGroup); - - // collect RRDs data for this group - if (s_logger.isDebugEnabled()) { - s_logger.debug("[AutoScale] Collecting RRDs data..."); - } - Map params = new HashMap(); - List asGroupVmVOs = _asGroupVmDao.listByGroup(asGroup.getId()); - params.put("total_vm", String.valueOf(asGroupVmVOs.size())); - for (int i = 0; i < asGroupVmVOs.size(); i++) { - long vmId = asGroupVmVOs.get(i).getInstanceId(); - VMInstanceVO vmVO = _vmInstance.findById(vmId); - //xe vm-list | grep vmname -B 1 | head -n 1 | awk -F':' '{print $2}' - params.put("vmname" + String.valueOf(i + 1), vmVO.getInstanceName()); - params.put("vmid" + String.valueOf(i + 1), String.valueOf(vmVO.getId())); - - } - // get random hostid because all vms are in a cluster - long vmId = asGroupVmVOs.get(0).getInstanceId(); - VMInstanceVO vmVO = _vmInstance.findById(vmId); - Long receiveHost = vmVO.getHostId(); - - // setup parameters phase: duration and counter - // list pair [counter, duration] - List> lstPair = getPairofCounternameAndDuration(asGroup.getId()); - int total_counter = 0; - String[] lstCounter = new String[lstPair.size()]; - for (int i = 0; i < lstPair.size(); i++) { - Pair pair = lstPair.get(i); - String strCounterNames = pair.first(); - Integer duration = pair.second(); - - lstCounter[i] = strCounterNames.split(",")[0]; - total_counter++; - params.put("duration" + String.valueOf(total_counter), duration.toString()); - params.put("counter" + String.valueOf(total_counter), lstCounter[i]); - params.put("con" + String.valueOf(total_counter), strCounterNames.split(",")[1]); - } - params.put("total_counter", String.valueOf(total_counter)); - - PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20); - - try { - Answer answer = _agentMgr.send(receiveHost, perfMon); - if (answer == null || !answer.getResult()) { - s_logger.debug("Failed to send data to node !"); - } else { - String result = answer.getDetails(); - s_logger.debug("[AutoScale] RRDs collection answer: " + result); - HashMap avgCounter = new HashMap(); - - // extract data - String[] counterElements = result.split(","); - if ((counterElements != null) && (counterElements.length > 0)) { - for (String string : counterElements) { - try { - String[] counterVals = string.split(":"); - String[] counter_vm = counterVals[0].split("\\."); - - Long counterId = Long.parseLong(counter_vm[1]); - Long conditionId = Long.parseLong(params.get("con" + counter_vm[1])); - Double coVal = Double.parseDouble(counterVals[1]); - - // Summary of all counter by counterId key - if (avgCounter.get(counterId) == null) { - /* initialize if data is not set */ - avgCounter.put(counterId, new Double(0)); - } - - String counterName = getCounternamebyCondition(conditionId.longValue()); - if (Counter.Source.memory.toString().equals(counterName)) { - // calculate memory in percent - Long profileId = asGroup.getProfileId(); - AutoScaleVmProfileVO profileVo = _asProfileDao.findById(profileId); - ServiceOfferingVO serviceOff = _serviceOfferingDao.findById(profileVo.getServiceOfferingId()); - int maxRAM = serviceOff.getRamSize(); - - // get current RAM percent - coVal = coVal / maxRAM; - } else { - // cpu - coVal = coVal * 100; - } - - // update data entry - avgCounter.put(counterId, avgCounter.get(counterId) + coVal); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - String scaleAction = getAutoscaleAction(avgCounter, asGroup.getId(), currentVM, params); - if (scaleAction != null) { - s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId()); - if (scaleAction.equals("scaleup")) { - _asManager.doScaleUp(asGroup.getId(), 1); - } else { - _asManager.doScaleDown(asGroup.getId()); - } - } - } - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - } - - } catch (Throwable t) { - s_logger.error("Error trying to monitor autoscaling", t); - } - - } - - private boolean is_native(long groupId) { - List vos = _asGroupPolicyDao.listByVmGroupId(groupId); - for (AutoScaleVmGroupPolicyMapVO vo : vos) { - List ConditionPolicies = _asConditionMapDao.findByPolicyId(vo.getPolicyId()); - for (AutoScalePolicyConditionMapVO ConditionPolicy : ConditionPolicies) { - ConditionVO condition = _asConditionDao.findById(ConditionPolicy.getConditionId()); - CounterVO counter = _asCounterDao.findById(condition.getCounterid()); - if (counter.getSource() == Counter.Source.cpu || counter.getSource() == Counter.Source.memory) - return true; - } - } - return false; - } - - private String getAutoscaleAction(HashMap avgCounter, long groupId, long currentVM, Map params) { - - List listMap = _asGroupPolicyDao.listByVmGroupId(groupId); - if ((listMap == null) || (listMap.size() == 0)) - return null; - for (AutoScaleVmGroupPolicyMapVO asVmgPmap : listMap) { - AutoScalePolicyVO policyVO = _asPolicyDao.findById(asVmgPmap.getPolicyId()); - if (policyVO != null) { - Integer quitetime = policyVO.getQuietTime(); - Date quitetimeDate = policyVO.getLastQuiteTime(); - long last_quitetime = 0L; - if (quitetimeDate != null) { - last_quitetime = policyVO.getLastQuiteTime().getTime(); - } - long current_time = (new Date()).getTime(); - - // check quite time for this policy - if ((current_time - last_quitetime) >= (long)quitetime) { - - // list all condition of this policy - boolean bValid = true; - List lstConditions = getConditionsbyPolicyId(policyVO.getId()); - if ((lstConditions != null) && (lstConditions.size() > 0)) { - // check whole conditions of this policy - for (ConditionVO conditionVO : lstConditions) { - long thresholdValue = conditionVO.getThreshold(); - Double thresholdPercent = (double)thresholdValue / 100; - CounterVO counterVO = _asCounterDao.findById(conditionVO.getCounterid()); -//Double sum = avgCounter.get(conditionVO.getCounterid()); - long counter_count = 1; - do { - String counter_param = params.get("counter" + String.valueOf(counter_count)); - Counter.Source counter_source = counterVO.getSource(); - if (counter_param.equals(counter_source.toString())) - break; - counter_count++; - } while (1 == 1); - - Double sum = avgCounter.get(counter_count); - Double avg = sum / currentVM; - Operator op = conditionVO.getRelationalOperator(); - boolean bConditionCheck = ((op == com.cloud.network.as.Condition.Operator.EQ) && (thresholdPercent.equals(avg))) - || ((op == com.cloud.network.as.Condition.Operator.GE) && (avg.doubleValue() >= thresholdPercent.doubleValue())) - || ((op == com.cloud.network.as.Condition.Operator.GT) && (avg.doubleValue() > thresholdPercent.doubleValue())) - || ((op == com.cloud.network.as.Condition.Operator.LE) && (avg.doubleValue() <= thresholdPercent.doubleValue())) - || ((op == com.cloud.network.as.Condition.Operator.LT) && (avg.doubleValue() < thresholdPercent.doubleValue())); - - if (!bConditionCheck) { - bValid = false; - break; - } - } - if (bValid) { - return policyVO.getAction(); - } - } - } - } - } - return null; - } - - private List getConditionsbyPolicyId(long policyId) { - List conditionMap = _asConditionMapDao.findByPolicyId(policyId); - if ((conditionMap == null) || (conditionMap.size() == 0)) - return null; - - List lstResult = new ArrayList(); - for (AutoScalePolicyConditionMapVO asPCmap : conditionMap) { - lstResult.add(_asConditionDao.findById(asPCmap.getConditionId())); - } - - return lstResult; - } - - public List> getPairofCounternameAndDuration( - long groupId) { - AutoScaleVmGroupVO groupVo = _asGroupDao.findById(groupId); - if (groupVo == null) - return null; - List> result = new ArrayList>(); - //list policy map - List groupPolicymap = _asGroupPolicyDao.listByVmGroupId(groupVo.getId()); - if (groupPolicymap == null) - return null; - for (AutoScaleVmGroupPolicyMapVO gpMap : groupPolicymap) { - //get duration - AutoScalePolicyVO policyVo = _asPolicyDao.findById(gpMap.getPolicyId()); - Integer duration = policyVo.getDuration(); - //get collection of counter name - - StringBuffer buff = new StringBuffer(); - List lstPCmap = _asConditionMapDao.findByPolicyId(policyVo.getId()); - for (AutoScalePolicyConditionMapVO pcMap : lstPCmap) { - String counterName = getCounternamebyCondition(pcMap.getConditionId()); - buff.append(counterName); - buff.append(","); - buff.append(pcMap.getConditionId()); - } - // add to result - Pair pair = new Pair(buff.toString(), duration); - result.add(pair); - } - - return result; - } - - public String getCounternamebyCondition(long conditionId) { - - ConditionVO condition = _asConditionDao.findById(conditionId); - if (condition == null) - return ""; - - long counterId = condition.getCounterid(); - CounterVO counter = _asCounterDao.findById(counterId); - if (counter == null) - return ""; - - return counter.getSource().toString(); - } - } - public boolean imageStoreHasEnoughCapacity(DataStore imageStore) { StorageStats imageStoreStats = _storageStats.get(imageStore.getId()); if (imageStoreStats != null && (imageStoreStats.getByteUsed()/(imageStoreStats.getCapacityBytes()*1.0)) <= _imageStoreCapacityThreshold) { diff --git a/server/src/com/cloud/server/as/AutoScaleCounterCollector.java b/server/src/com/cloud/server/as/AutoScaleCounterCollector.java new file mode 100644 index 000000000000..fe9de05ff4b0 --- /dev/null +++ b/server/src/com/cloud/server/as/AutoScaleCounterCollector.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 com.cloud.server.as; + +import com.cloud.network.as.AutoScalePolicyConditionMapVO; +import com.cloud.network.as.AutoScalePolicyVO; +import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.CounterVO; +import com.cloud.network.as.AutoScaleCounterProcessor; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.utils.net.MacAddress; +import com.cloud.vm.NicVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; +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.managed.context.ManagedContextRunnable; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class AutoScaleCounterCollector extends ManagedContextRunnable implements Configurable { + + @Inject + protected AutoScaleVmGroupDao autoScaleVmGroupDao; + @Inject + protected AutoScaleVmGroupVmMapDao autoScaleVmMapDao; + @Inject + protected VMInstanceDao vmInstanceDao; + @Inject + protected NicDao nicDao; + @Inject + protected AutoScaleVmGroupPolicyMapDao asPolicyMapDao; + @Inject + protected AutoScalePolicyDao asPolicyDao; + @Inject + protected AutoScalePolicyConditionMapDao asPolicyConditionMapDao; + @Inject + protected ConditionDao conditionDao; + @Inject + protected CounterDao counterDao; + @Inject + protected AutoScaleCounterProcessor counterProcessor; + @Inject + protected ConfigurationDao configurationDao; + + private static final String ELASTIC_SEARCH_DATA_SOURCE = "elasticsearch"; + protected static ConfigKey managementServer = new ConfigKey<>("Advanced", Long.class, "autoscale.counter_agent.mgmtserverid", "0", "ID of the management server in which the SNMP agent will run", true, ConfigKey.Scope.Global); + protected static final long currentManagementServerId = MacAddress.getMacAddress().toLong(); + + public static Logger s_logger = Logger.getLogger(AutoScaleCounterCollector.class.getName()); + + @Override + protected void runInContext() { + try { + if (elasticSearchDataSourceEnabled() && isConfiguredManagementServer()) { + List autoScaleVmGroups = this.listEnabledAutoScaleGroups(); + for (AutoScaleVmGroupVO asGroup : autoScaleVmGroups) { + s_logger.debug("[AutoScale] Reading VM stats from AutoScaleGroup #" + asGroup.getId()); + try { + List virtualMachines = this.getVirtualMachinesFrom(asGroup); + if (!virtualMachines.isEmpty()) { + counterProcessor.process(asGroup, virtualMachines, this.getCountersFrom(asGroup)); + } + } catch (Exception ex) { + s_logger.error("[AutoScale] Error while reading AutoScaleGroup #" + asGroup.getId(), ex); + } + } + } else { + s_logger.debug("[AutoScale] Elasticsearch stats datasource not enabled or management server not configured"); + } + }catch(Exception ex){ + s_logger.error("[AutoScale] Error processing VM counters, skipping to next execution"); + } + } + + private boolean isConfiguredManagementServer() { + return currentManagementServerId == managementServer.value(); + } + + private boolean elasticSearchDataSourceEnabled() { + return ELASTIC_SEARCH_DATA_SOURCE.equals(configurationDao.findByName("autoscale.stats.datasource").getValue()); + } + + protected List listEnabledAutoScaleGroups(){ + return autoScaleVmGroupDao.listAllEnabled(); + } + + protected List getVirtualMachinesFrom(AutoScaleVmGroupVO asGroup) { + List vmList = new ArrayList<>(); + List autoScaleVmGroupVmMapVOs = autoScaleVmMapDao.listByGroup(asGroup.getId()); + if(autoScaleVmGroupVmMapVOs != null) { + for (AutoScaleVmGroupVmMapVO asGroupVmVO : autoScaleVmGroupVmMapVOs) { + VMInstanceVO vmInstanceVO = vmInstanceDao.findById(asGroupVmVO.getInstanceId()); + String ipAddress = getIpAddressesFrom(vmInstanceVO); + if(ipAddress != null) { + vmList.add(new VirtualMachineAddress(ipAddress, vmInstanceVO.getHostName())); + } + } + } + return vmList; + } + + protected String getIpAddressesFrom(VMInstanceVO vm){ + NicVO nic; + List nicList = nicDao.listByVmId(vm.getId()); + + if(nicList.size() == 1){ + nic = nicList.get(0); + }else{ + nic = getNonDefaultNic(nicList); + } + + if(nic == null) + return null; + + return nic.getIPv4Address() != null ? nic.getIPv4Address() : nic.getIPv6Address(); + } + + private NicVO getNonDefaultNic(List nicList) { + for(NicVO currentNic : nicList){ + if(!currentNic.isDefaultNic()){ + return currentNic; + } + } + return null; + } + + protected Map getCountersFrom(AutoScaleVmGroupVO asGroup){ + Map counters = new HashMap<>(); + for(AutoScaleVmGroupPolicyMapVO autoScaleVmGroupPolicyMapVO : asPolicyMapDao.listByVmGroupId(asGroup.getId())){ + for(AutoScalePolicyConditionMapVO asPolicyCondition : findPolicyConditionMap(autoScaleVmGroupPolicyMapVO)){ + CounterVO counter = findCounter(asPolicyCondition); + counters.put(counter.getSource().name(), counter.getValue()); + } + } + return counters; + } + + private List findPolicyConditionMap(AutoScaleVmGroupPolicyMapVO autoScaleVmGroupPolicyMapVO) { + AutoScalePolicyVO autoScalePolicy = asPolicyDao.findById(autoScaleVmGroupPolicyMapVO.getPolicyId()); + return asPolicyConditionMapDao.findByPolicyId(autoScalePolicy.getId()); + } + + private CounterVO findCounter(AutoScalePolicyConditionMapVO asPolicyCondition) { + return counterDao.findById(conditionDao.findById(asPolicyCondition.getConditionId()).getCounterid()); + } + + @Override + public String getConfigComponentName() { + return AutoScaleCounterCollector.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ managementServer }; + } +} + diff --git a/server/src/com/cloud/server/as/AutoScaleMonitor.java b/server/src/com/cloud/server/as/AutoScaleMonitor.java new file mode 100644 index 000000000000..c8aa0e5b187e --- /dev/null +++ b/server/src/com/cloud/server/as/AutoScaleMonitor.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 com.cloud.server.as; + +import com.cloud.network.as.AutoScaleManager; +import com.cloud.network.as.AutoScalePolicy; +import com.cloud.network.as.AutoScalePolicyConditionMapVO; +import com.cloud.network.as.AutoScalePolicyVO; +import com.cloud.network.as.AutoScaleStatsCollector; +import com.cloud.network.as.AutoScaleStatsCollectorFactory; +import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.Condition; +import com.cloud.network.as.ConditionVO; +import com.cloud.network.as.Counter; +import com.cloud.network.as.CounterVO; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Component +public class AutoScaleMonitor extends ManagedContextRunnable implements Configurable{ + + @Inject + protected AutoScaleVmGroupDao _asGroupDao; + @Inject + protected AutoScaleVmGroupVmMapDao _asGroupVmDao; + @Inject + protected AutoScaleManager _asManager; + @Inject + protected VMInstanceDao _vmInstance; + @Inject + protected AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao; + @Inject + protected AutoScalePolicyDao _asPolicyDao; + @Inject + protected AutoScalePolicyConditionMapDao _asConditionMapDao; + @Inject + protected ConditionDao _asConditionDao; + @Inject + protected CounterDao _asCounterDao; + @Inject + protected AutoScaleStatsCollectorFactory autoScaleStatsCollectorFactory; + + protected ExecutorService threadExecutor; + + private static final String SCALE_UP_ACTION = "scaleup"; + private static final String AUTO_SCALE_ENABLED = "enabled"; + private static final Logger s_logger = Logger.getLogger(AutoScaleMonitor.class.getName()); + + private static final ConfigKey ThreadPoolSize = new ConfigKey<>("Advanced", Integer.class, "autoscale.monitor.threadpoolsize", "10", "Auto scale monitor thread pool size", true, ConfigKey.Scope.Global); + + public AutoScaleMonitor(){ + threadExecutor = Executors.newFixedThreadPool(ThreadPoolSize.value()); + } + + @Override + protected void runInContext() { + try { + if (s_logger.isDebugEnabled()) { + s_logger.debug("[AutoScale] AutoScaling Monitor is running"); + } + + for (final AutoScaleVmGroupVO asGroup : _asGroupDao.listAllNotLocked()) { + threadExecutor.execute(new Runnable() { + public void run() { + if (s_logger.isDebugEnabled()) { + s_logger.debug("[AutoScale] Processing AutoScaleGroup id: " + asGroup.getId()); + } + processAutoScaleGroup(asGroup); + } + }); + } + } catch (Throwable t) { + s_logger.error("[AutoScale] Error trying to monitor auto scale", t); + } + } + + protected void processAutoScaleGroup(AutoScaleVmGroupVO asGroup) { + try { + //refresh to have the most updated version of asGroup, + //as it can become outdated while the list is iterated + if (asGroup.getUuid() != null) + s_logger.debug("[AutoScale] Started processing group " + asGroup.getUuid()); + asGroup = _asGroupDao.findById(asGroup.getId()); + + if(asGroup.isLocked() || !asGroup.getState().equals(AUTO_SCALE_ENABLED) || !isNative(asGroup.getId())) + return; + + lockAutoScaleGroup(asGroup); + + // check minimum vm of group + Integer currentVmCount = _asGroupVmDao.countByGroup(asGroup.getId()); + if (currentVmCount < asGroup.getMinMembers()) { + _asManager.doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVmCount); + return; + } + + // check maximum vm of group + if (currentVmCount > asGroup.getMaxMembers()) { + _asManager.doScaleDown(asGroup.getId(), currentVmCount - asGroup.getMaxMembers()); + return; + } + + if (minimumIntervalNotMet(asGroup)) return; + + updateLasIntervalFor(asGroup); + + AutoScaleStatsCollector statsCollector = autoScaleStatsCollectorFactory.getStatsCollector(); + Map counterSummary = statsCollector.retrieveMetrics(asGroup, this.getVirtualMachinesFor(asGroup)); + + if (counterSummaryNotEmpty(counterSummary)) { + AutoScalePolicy policy = this.getAutoScalePolicy(counterSummary, asGroup); + if (policy != null) { + s_logger.debug("[AutoScale] Doing scale action: " + policy.getAction() + " for group " + asGroup.getId()); + + if (policy.getAction().equals(SCALE_UP_ACTION)) { + _asManager.doScaleUp(asGroup.getId(), policy.getStep()); + } else { + _asManager.doScaleDown(asGroup.getId(), policy.getStep()); + } + } + } + }catch(Exception ex){ + s_logger.error("[AutoScale] Error while processing AutoScale group id" + asGroup.getId(), ex); + }finally{ + unlockAutoScaleGroup(asGroup); + } + } + + private boolean minimumIntervalNotMet(AutoScaleVmGroupVO asGroup) { + long now = new Date().getTime(); + Date lastInterval = asGroup.getLastInterval(); + return (lastInterval != null && (now - lastInterval.getTime()) < asGroup.getInterval() * 1000); + } + + + private boolean counterSummaryNotEmpty(Map counterSummary) { + return counterSummary != null && !counterSummary.keySet().isEmpty(); + } + + private void updateLasIntervalFor(AutoScaleVmGroupVO asGroup) { + asGroup.setLastInterval(new Date()); + _asGroupDao.persist(asGroup); + } + + private List getVirtualMachinesFor(AutoScaleVmGroupVO asGroup) { + List vmList = new ArrayList<>(); + List asGroupVmVOs = _asGroupVmDao.listByGroup(asGroup.getId()); + for(AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs){ + vmList.add(_vmInstance.findById(asGroupVmVO.getInstanceId())); + } + return vmList; + } + + private void lockAutoScaleGroup(AutoScaleVmGroupVO autoScaleVmGroup){ + autoScaleVmGroup.setLocked(true); + _asGroupDao.persist(autoScaleVmGroup); + } + + private void unlockAutoScaleGroup(AutoScaleVmGroupVO autoScaleVmGroup){ + autoScaleVmGroup.setLocked(false); + _asGroupDao.persist(autoScaleVmGroup); + } + + private boolean isNative(long groupId) { + List vos = _asGroupPolicyDao.listByVmGroupId(groupId); + for (AutoScaleVmGroupPolicyMapVO vo : vos) { + List ConditionPolicies = _asConditionMapDao.findByPolicyId(vo.getPolicyId()); + for (AutoScalePolicyConditionMapVO ConditionPolicy : ConditionPolicies) { + ConditionVO condition = _asConditionDao.findById(ConditionPolicy.getConditionId()); + CounterVO counter = _asCounterDao.findById(condition.getCounterid()); + List notNativesSources = Arrays.asList(Counter.Source.snmp, Counter.Source.netscaler); + if (notNativesSources.contains(counter.getSource())) + return false; + } + } + return true; + } + + private AutoScalePolicy getAutoScalePolicy(Map counterSummary, AutoScaleVmGroupVO asGroup) { + List asGroupPolicyMap = _asGroupPolicyDao.listByVmGroupId(asGroup.getId()); + if (asGroupPolicyMap == null || asGroupPolicyMap.size() == 0) + return null; + + for (AutoScaleVmGroupPolicyMapVO asGroupPolicy : asGroupPolicyMap) { + AutoScalePolicyVO policy = _asPolicyDao.findById(asGroupPolicy.getPolicyId()); + if (policy != null) { + long quietTime = (long) policy.getQuietTime() * 1000; + Date quietTimeDate = policy.getLastQuiteTime(); + long lastQuietTime = 0L; + if (quietTimeDate != null) { + lastQuietTime = policy.getLastQuiteTime().getTime(); + } + long now = (new Date()).getTime(); + + // check quite time for this policy + if (now - lastQuietTime >= quietTime) { + List conditions = getConditionsByPolicyId(policy.getId()); + + if (conditions != null && !conditions.isEmpty()) { + if (isPolicyValid(counterSummary, conditions, policy.getLogicalOperator())) { + return policy; + } + } + } + } + } + return null; + } + + protected boolean isPolicyValid(Map counterSummary, List conditions, AutoScalePolicy.LogicalOperator logicalOperator) { + Boolean isPolicyValid = null; + for (ConditionVO conditionVO : conditions) { + long counterThresholdValue = conditionVO.getThreshold(); + CounterVO counter = _asCounterDao.findById(conditionVO.getCounterid()); + + Double counterAverageValue = counterSummary.get(counter.getSource().name()); + if(counterAverageValue == null){ + return false; + } + + boolean isConditionValid = isConditionValid(counterThresholdValue, counterAverageValue, conditionVO.getRelationalOperator()); + + if(isPolicyValid == null){ + isPolicyValid = isConditionValid; + } + + if(logicalOperator == AutoScalePolicy.LogicalOperator.OR){ + isPolicyValid = isPolicyValid || isConditionValid; + }else { + isPolicyValid = isPolicyValid && isConditionValid; + } + } + return isPolicyValid; + } + + protected boolean isConditionValid(long counterThresholdValue, Double counterAverageValue, Condition.Operator operator) { + if(operator == Condition.Operator.EQ) + return counterAverageValue == counterThresholdValue; + if(operator == Condition.Operator.GE) + return counterAverageValue >= counterThresholdValue; + if(operator == Condition.Operator.GT) + return counterAverageValue > counterThresholdValue; + if(operator == Condition.Operator.LE) + return counterAverageValue <= counterThresholdValue; + if(operator == Condition.Operator.LT) + return counterAverageValue < counterThresholdValue; + return false; + } + + private List getConditionsByPolicyId(long policyId) { + List conditionMap = _asConditionMapDao.findByPolicyId(policyId); + if (conditionMap == null || conditionMap.isEmpty()) + return null; + + List conditions = new ArrayList<>(); + for (AutoScalePolicyConditionMapVO policyConditionMap : conditionMap) { + conditions.add(_asConditionDao.findById(policyConditionMap.getConditionId())); + } + + return conditions; + } + + @Override + public String getConfigComponentName() { + return AutoScaleMonitor.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ ThreadPoolSize }; + } +} diff --git a/server/src/com/cloud/server/as/VirtualMachineAddress.java b/server/src/com/cloud/server/as/VirtualMachineAddress.java new file mode 100644 index 000000000000..ed4aee844538 --- /dev/null +++ b/server/src/com/cloud/server/as/VirtualMachineAddress.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.server.as; + +public class VirtualMachineAddress { + + private String ipAddress; + private String hostName; + + public VirtualMachineAddress(String ipAddress, String hostName) { + this.ipAddress = ipAddress; + this.hostName = hostName; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getHostName() { + return hostName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VirtualMachineAddress)) return false; + + VirtualMachineAddress vmAddress = (VirtualMachineAddress) o; + + if (hostName != null ? !hostName.equals(vmAddress.hostName) : vmAddress.hostName != null) return false; + if (ipAddress != null ? !ipAddress.equals(vmAddress.ipAddress) : vmAddress.ipAddress != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = ipAddress != null ? ipAddress.hashCode() : 0; + result = 31 * result + (hostName != null ? hostName.hashCode() : 0); + return result; + } +} diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 610789f5de26..f81df2a2b7e0 100644 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -285,6 +285,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C @Inject private TemplateService _imageSrv; @Inject + private VolumeService _volumeSrv; + @Inject EndPointSelector _epSelector; @Inject private DiskOfferingDao _diskOfferingDao; @@ -565,6 +567,7 @@ public DataStore createLocalStorage(Host host, StoragePoolInfo pInfo) throws Con if (!(dc.isLocalStorageEnabled() || useLocalStorageForSystemVM)) { return null; } + DataStore store; try { String hostAddress = pInfo.getHost(); @@ -1974,6 +1977,9 @@ public String getName() { return null; } + @Inject + private DataStoreManager _storeMgr; + @Override public ImageStore discoverImageStore(String name, String url, String providerName, Long zoneId, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException { DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(providerName); @@ -2066,8 +2072,11 @@ public ImageStore discoverImageStore(String name, String url, String providerNam if (scopeType == ScopeType.REGION) { duplicateCacheStoreRecordsToRegionStore(store.getId()); } + ImageStore storeImg = (ImageStore)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Image); - return (ImageStore)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Image); + _volumeSrv.handleVolumeSync(store); + _imageSrv.handleTemplateSync(store); + return storeImg; } @Override diff --git a/server/src/com/cloud/tags/TagKeysBuilder.java b/server/src/com/cloud/tags/TagKeysBuilder.java new file mode 100644 index 000000000000..634d52690f70 --- /dev/null +++ b/server/src/com/cloud/tags/TagKeysBuilder.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.tags; + +import com.cloud.server.ResourceTag; +import java.util.List; + +public class TagKeysBuilder { + private StringBuilder tagKeys = new StringBuilder(""); + private static final String SEPARATOR = ","; + public static final String TAGKEYS_METADATA_KEY = "TAGKEYS"; + + public void add(String key) { + tagKeys.append(key.trim() + " "); + } + + public String value() { + return tagKeys.toString().trim().replaceAll(" ", SEPARATOR); + } + + public static String getKeyMetadata(String key) { + return "TAG_" + key.trim().replaceAll(" ","_"); + } + + public static String buildTagKeys(List tags) { + TagKeysBuilder builder = new TagKeysBuilder(); + if ( tags != null ){ + for(ResourceTag tag : tags) { + builder.add(tag.getKey().replaceAll(" ","_")); + } + } + return builder.value(); + } +} \ No newline at end of file diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java index 9803ce753a34..1eabc3e91f60 100644 --- a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java @@ -16,6 +16,27 @@ // under the License. package com.cloud.tags; +import com.cloud.network.as.AutoScaleManager; +import com.cloud.network.router.NetworkHelper; +import com.cloud.vm.UserVmManager; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.cloud.storage.SnapshotPolicyVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + import com.cloud.dc.DataCenterVO; import com.cloud.domain.PartOf; import com.cloud.event.ActionEvent; @@ -49,7 +70,6 @@ import com.cloud.server.TaggedResourceService; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.SnapshotVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; @@ -60,7 +80,6 @@ import com.cloud.user.DomainManager; import com.cloud.user.OwnedBy; import com.cloud.user.UserVO; -import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; @@ -70,27 +89,16 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.snapshot.VMSnapshotVO; -import org.apache.cloudstack.api.Identity; -import org.apache.cloudstack.api.InternalIdentity; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; import org.apache.commons.collections.MapUtils; -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; public class TaggedResourceManagerImpl extends ManagerBase implements TaggedResourceService { public static final Logger s_logger = Logger.getLogger(TaggedResourceManagerImpl.class); @@ -146,6 +154,15 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso DomainManager _domainMgr; @Inject AccountDao _accountDao; + @Inject + UserVmManager _userVmManager; + @Inject + AutoScaleManager _autoscaleManager; + + @Autowired + @Qualifier("networkHelper") + NetworkHelper _networkHelper; + @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -284,6 +301,8 @@ public List createTags(final List resourceIds, final Resour final List resourceTags = new ArrayList<>(tags.size()); + final Map> vmsIdResourceTagToCreate = new HashMap>(); + Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -309,17 +328,35 @@ public void doInTransactionWithoutResult(TransactionStatus status) { throw new InvalidParameterValueException("Value for the key " + key + " is either null or empty"); } + ResourceTag tag = _resourceTagDao.findByResourceIdAndResourceTypeAndKey(id, resourceType, key); + if (tag != null){ + throw new CloudRuntimeException("The key '" + key + "' already exist for '" + resourceType + "' with resourceId " + id ); + } + ResourceTagVO resourceTag = new ResourceTagVO(key, value, accountDomainPair.first(), accountDomainPair.second(), id, resourceType, customer, resourceUuid); resourceTag = _resourceTagDao.persist(resourceTag); resourceTags.add(resourceTag); + + if (resourceType == ResourceObjectType.AutoScaleVmGroup){ + _autoscaleManager.applyTagToAutoScaleGroupVm(id, resourceTag); + } + + addTagToVMMetadata(vmsIdResourceTagToCreate, + resourceTag.getResourceId(), + resourceTag.getKey(), + resourceTag.getValue(), + resourceType); } } } }); + updateVMMetaData(vmsIdResourceTagToCreate); + return resourceTags; } + private List searchResourceTags(List resourceIds, ResourceObjectType resourceType) { List resourceUuids = resourceIds.stream().map(resourceId -> getUuid(resourceId, resourceType)).collect(Collectors.toList()); SearchBuilder sb = _resourceTagDao.createSearchBuilder(); @@ -337,6 +374,10 @@ private List searchResourceTags(List resourceIds, @ActionEvent(eventType = EventTypes.EVENT_TAGS_DELETE, eventDescription = "deleting resource tags") public boolean deleteTags(List resourceIds, ResourceObjectType resourceType, Map tags) { Account caller = CallContext.current().getCallingAccount(); + + if(resourceType == ResourceObjectType.AutoScaleVmGroup){ + _autoscaleManager.deleteTagsFromAutoScaleGroupVms(resourceIds, tags); + } if(s_logger.isDebugEnabled()) { s_logger.debug("ResourceIds to Find " + String.join(", ", resourceIds)); } @@ -383,23 +424,84 @@ public boolean deleteTags(List resourceIds, ResourceObjectType resourceT throw new InvalidParameterValueException("Unable to find any tags which conform to specified delete parameters."); } + + final Map> vmsIdResourceTagToRemove = new HashMap>(); + final Map vmsIdsTagKeysBuilder = new HashMap(); + + //Remove the tags Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { for (ResourceTag tagToRemove : tagsToDelete) { _resourceTagDao.remove(tagToRemove.getId()); + s_logger.debug("Removed the tag " + tagToRemove); + + addTagToVMMetadata(vmsIdResourceTagToRemove, + tagToRemove.getResourceId(), + tagToRemove.getKey(), + "", + tagToRemove.getResourceType()); + s_logger.debug("Removed the tag '" + tagToRemove + "' for resources (" + String.join(", ", resourceIds) + ")"); } } }); + updateVMMetaData(vmsIdResourceTagToRemove); + return true; } + protected void updateVMMetaData(Map> vmsIdResourceTagToRemove) { + boolean success = true; + List userVmsIdsErros = new ArrayList(); + + for (Long userVmId : vmsIdResourceTagToRemove.keySet() ) { + try { + Map tagsR = vmsIdResourceTagToRemove.get(userVmId); + + List tags = (List)_resourceTagDao.listBy(userVmId, ResourceObjectType.UserVm); + tagsR.put(TagKeysBuilder.TAGKEYS_METADATA_KEY, TagKeysBuilder.buildTagKeys(tags)); + + s_logger.debug("[TAG_METADATA] Update userVm metadata with tags and values: " + tagsR.toString()); + _networkHelper.updateVMMetadaInVrouter(userVmId, tagsR); + }catch (Exception e ) { + //catch exception because it should try to update other resources + s_logger.error("[TAG_METADATA] Error try to update VmMetaData removing tags. userVmId: " + userVmId, e); + userVmsIdsErros.add(userVmId); + success = false; + } + } + + if (!success) { + throw new CloudRuntimeException("Error when try to remove userVm tags. userVmsIds: "+ userVmsIdsErros); + } + } + + + + protected void addTagToVMMetadata(Map> vmsIdResourceTagToRemove, + Long vmUserId, String key, + String value, ResourceObjectType type) { + if (ResourceObjectType.UserVm == type){ + Map tags = vmsIdResourceTagToRemove.get(vmUserId); + if ( tags == null ){ + tags = new HashMap(); + vmsIdResourceTagToRemove.put(vmUserId, tags); + } + + String keyMetadata = TagKeysBuilder.getKeyMetadata(key); + tags.put(keyMetadata, value != null ? value : ""); + + } + } + + @Override public List listByResourceTypeAndId(ResourceObjectType resourceType, long resourceId) { return _resourceTagDao.listBy(resourceId, resourceType); } + } diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 9129976900d3..5d669ef49d21 100644 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -586,6 +586,27 @@ public void checkAccess(Account caller, AccessType accessType, boolean sameOwner } + private Domain getEntityDomain(ControlledEntity entity) { + Domain entityDomain = null; + long domainId = entity.getDomainId(); + + if (domainId != -1) { + entityDomain = _domainMgr.getDomain(domainId); + } else { + if (entity.getAccountId() != -1) { + // If account exists domainId should too so + // calculate + // it. This condition might be hit for templates or + // entities which miss domainId in their tables + Account account = getAccount(entity.getAccountId()); + domainId = account != null ? account.getDomainId() : -1; + entityDomain = _domainMgr.getDomain(domainId); + } + } + + return entityDomain; + } + @Override public Long checkAccessAndSpecifyAuthority(Account caller, Long zoneId) { // We just care for resource domain admin for now. He should be permitted to see only his zone. diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index 60c48fa574e8..2590e027b310 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -258,7 +258,11 @@ public Set getDomainParentIds(long domainId) { @Override public boolean removeDomain(long domainId) { - return _domainDao.remove(domainId); + boolean removed = _domainDao.remove(domainId); + if (removed) { + _messageBus.publish(_name, MESSAGE_REMOVE_DOMAIN_EVENT, PublishScope.LOCAL, domainId); + } + return removed; } @Override @@ -508,11 +512,16 @@ protected boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOp ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(ctx.getCallingUserId()), ctx.getCallingAccount()); for (Long networkId : networkIds) { s_logger.debug("Deleting network id=" + networkId + " as a part of domain id=" + domainId + " cleanup"); - if (!_networkMgr.destroyNetwork(networkId, context, false)) { - s_logger.warn("Unable to destroy network id=" + networkId + " as a part of domain id=" + domainId + " cleanup."); + try { + if (!_networkMgr.destroyNetwork(networkId, context, false)) { + s_logger.warn("Unable to destroy network id=" + networkId + " as a part of domain id=" + domainId + " cleanup."); + networksDeleted = false; + } else { + s_logger.debug("Network " + networkId + " successfully deleted as a part of domain id=" + domainId + " cleanup."); + } + } catch (CloudRuntimeException e) { + s_logger.warn(e.getMessage(), e); networksDeleted = false; - } else { - s_logger.debug("Network " + networkId + " successfully deleted as a part of domain id=" + domainId + " cleanup."); } } diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 6ffc28e44032..3d62b2ec3c78 100644 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -118,6 +118,10 @@ UserVm updateVirtualMachine(long id, String displayName, String group, Boolean h void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType); + public UserVm updateVMdata(long userVmId) throws InsufficientCapacityException, ResourceUnavailableException; + + public void updateVMData(long userVmId, Map vmData) throws InsufficientCapacityException, ResourceUnavailableException; + void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString); HashMap> getVmNetworkStatistics(long hostId, String hostName, List vmIds); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index d3eed38cc0e4..ec2effae1709 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -39,6 +39,15 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +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; @@ -89,12 +98,7 @@ import org.apache.cloudstack.storage.command.DettachCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -474,11 +478,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject private ConfigurationDao _configDao; - private ScheduledExecutorService _executor = null; - private ScheduledExecutorService _vmIpFetchExecutor = null; - private int _expungeInterval; - private int _expungeDelay; - private boolean _dailyOrHourly = false; + @Inject + protected AutoScaleVmGroupVmMapDao _asGroupVmMapDao; + + protected ScheduledExecutorService _executor = null; + protected ScheduledExecutorService _vmIpFetchExecutor = null; + protected int _expungeInterval; + protected int _expungeDelay; + protected boolean _dailyOrHourly = false; + private int capacityReleaseInterval; private ExecutorService _vmIpFetchThreadExecutor; @@ -1061,6 +1069,7 @@ private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map currentCpu) { - _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, newCpu - currentCpu); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, newCpu - currentCpu); } if (newMemory > currentMemory) { - _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, newMemory - currentMemory); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, newMemory - currentMemory); } // Check that the specified service offering ID is valid @@ -1103,7 +1112,6 @@ private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map currentCpu) { - _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu)); + _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu)); } else if (currentCpu > newCpu) { - _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(currentCpu - newCpu)); + _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.cpu, new Long(currentCpu - newCpu)); } if (newMemory > currentMemory) { - _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory)); + _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory)); } else if (currentMemory > newMemory) { - _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory)); + _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory)); } return _vmDao.findById(vmInstance.getId()); @@ -2139,7 +2147,7 @@ public boolean expunge(UserVmVO vm, long callerUserId, Account caller) { } // Only if vm is not expunged already, cleanup it's resources - if (vm.getRemoved() == null) { + if (vm != null && vm.getRemoved() == null) { // Cleanup vm resources - all the PF/LB/StaticNat rules // associated with vm s_logger.debug("Starting cleaning up vm " + vm + " resources..."); @@ -2633,17 +2641,51 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo _vmDao.updateVM(id, displayName, ha, osTypeId, userData, isDisplayVmEnabled, isDynamicallyScalable, customId, hostName, instanceName); if (updateUserdata) { - boolean result = updateUserDataInternal(_vmDao.findById(id)); - if (result) { - s_logger.debug("User data successfully updated for vm id=" + id); - } else { - throw new CloudRuntimeException("Failed to reset userdata for the virtual machine "); - } + updateVMdata(id); } return _vmDao.findById(id); } + @Override + public UserVm updateVMdata(long userVMId) throws InsufficientCapacityException, ResourceUnavailableException { + UserVmVO userVM = _vmDao.findById(userVMId); + + if (userVM == null) { + throw new CloudRuntimeException("UserVm " + userVMId + " do not exists!"); + } + + boolean result = updateUserDataInternal(userVM); + if (result) { + s_logger.debug("User data successfully updated for vm id=" + userVMId); + } else { + throw new CloudRuntimeException("Failed to reset userdata for the virtual machine "); + } + + return userVM; + } + + @Override + public void updateVMData(long userVmId, final Map vmData) throws InsufficientCapacityException, ResourceUnavailableException { + UserVmVO vm = _vmDao.findById(userVmId); + + if (vm == null) { + throw new CloudRuntimeException("UserVm " + userVmId + " do not exists!"); + } + + //TODO fix find out how to send vmdata to virtual router +// VmDataUpdater vmDataUpdater = new VmDataUpdater(){ +// +// @Override +// public boolean execute(Network network, NicProfile nic, VirtualMachineProfile vmProfile, UserDataServiceProvider element) throws ResourceUnavailableException { +// return element.saveUserVMData(network, nic, vmProfile, vmData); +// } +// }; +// vmDataUpdater.run(vm); + } + + + private boolean updateUserDataInternal(UserVm vm) throws ResourceUnavailableException, InsufficientCapacityException { VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); @@ -2758,7 +2800,9 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C } s_logger.debug("Found no ongoing snapshots on volume of type ROOT, for the vm with id " + vmId); + removeVmFromAutoScaleGroup(vmId); UserVm destroyedVm = destroyVm(vmId, expunge); + if (expunge) { if (!expunge(vm, ctx.getCallingUserId(), ctx.getCallingAccount())) { throw new CloudRuntimeException("Failed to expunge vm " + destroyedVm); @@ -2768,6 +2812,13 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C return destroyedVm; } + private void removeVmFromAutoScaleGroup(long vmId) { + AutoScaleVmGroupVmMapVO autoScaleVmMap = _asGroupVmMapDao.findByVmId(vmId); + if(autoScaleVmMap != null){ + _asGroupVmMapDao.remove(autoScaleVmMap.getVmGroupId(), vmId); + } + } + @Override @DB public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { @@ -6441,4 +6492,42 @@ private boolean checkStatusOfVolumeSnapshots(long vmId, Volume.Type type) { } return false; } -} \ No newline at end of file + + abstract class VmDataUpdater { + public boolean run(UserVm vm) throws ResourceUnavailableException { + + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + + List nics = _nicDao.listByVmId(vm.getId()); + if (nics == null || nics.isEmpty()) { + s_logger.error("unable to find any nics for vm " + vm.getUuid()); + throw new CloudRuntimeException("unable to find any nics for vm " + vm.getUuid()); + } + boolean success = true; + 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)); + + 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 = execute(network, nicProfile, vmProfile, element); + if (!result) { + s_logger.error("Failed to update userdata for vm " + vm + " and nic " + nic); + } + success = result & success; + + } + return success; + } + + public abstract boolean execute(Network network, NicProfile nic, VirtualMachineProfile vmProfile, UserDataServiceProvider element) throws ResourceUnavailableException; + } +} + + + diff --git a/server/test/com/cloud/api/query/dao/UserVmJoinDaoImplTest.java b/server/test/com/cloud/api/query/dao/UserVmJoinDaoImplTest.java old mode 100755 new mode 100644 diff --git a/server/test/com/cloud/configuration/ConfigurationManagerImplTest.java b/server/test/com/cloud/configuration/ConfigurationManagerImplTest.java new file mode 100644 index 000000000000..147a099b5225 --- /dev/null +++ b/server/test/com/cloud/configuration/ConfigurationManagerImplTest.java @@ -0,0 +1,23 @@ +package com.cloud.configuration; + +import junit.framework.TestCase; + +public class ConfigurationManagerImplTest extends TestCase { + + + public void testShouldHaveRemovedInQuery() { + ConfigurationManagerImpl manager = new ConfigurationManagerImpl(); + + assertTrue(manager.shouldHaveRemovedQuery("host_pod_ref")); + assertTrue(manager.shouldHaveRemovedQuery("host")); + assertTrue(manager.shouldHaveRemovedQuery("volumes")); + assertTrue(manager.shouldHaveRemovedQuery("physical_network")); + assertTrue(manager.shouldHaveRemovedQuery("vm_instance")); + + + assertFalse(manager.shouldHaveRemovedQuery("hos")); + assertFalse(manager.shouldHaveRemovedQuery("volu")); + assertFalse(manager.shouldHaveRemovedQuery("instance")); + + } +} \ No newline at end of file diff --git a/server/test/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImplTest.java b/server/test/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImplTest.java new file mode 100644 index 000000000000..b29b2288c68f --- /dev/null +++ b/server/test/com/cloud/globodictionary/apiclient/GloboDictionaryAPIClientImplTest.java @@ -0,0 +1,163 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package com.cloud.globodictionary.apiclient; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GloboDictionaryAPIClientImplTest { + + public static final String BUSINESS_SERVICE_ID = "31cf5f75db9f43409fc15458dc96198b"; + public static final String BUSINESS_SERVICE_NAME = "Assinatura - Vendas"; + @Mock + private HttpClient httpClient; + + @Spy + private GloboDictionaryAPIClientImpl apiClient; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + apiClient = spy(new GloboDictionaryAPIClientImpl(httpClient)); + } + + @Test + public void testGet() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + when(getMethodMock.getResponseBodyAsString()).thenReturn(getBusinessServiceJSON()); + mockCreateRequest(getMethodMock); + mockExecuteMethod(200); + + GloboDictionaryEntity entity = apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + + assertEquals(BUSINESS_SERVICE_NAME, entity.getName()); + assertEquals(BUSINESS_SERVICE_ID, entity.getId()); + verify(httpClient).executeMethod(getMethodMock); + } + + @Test + public void testGetGivenObjectNotFound() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + when(getMethodMock.getResponseBodyAsString()).thenReturn("[]"); + mockCreateRequest(getMethodMock); + mockExecuteMethod(200); + + GloboDictionaryEntity entity = apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + + assertNull(entity); + verify(httpClient).executeMethod(getMethodMock); + } + + @Test(expected = CloudRuntimeException.class) + public void testGetGivenStatusCodeIsNot200() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + mockCreateRequest(getMethodMock); + mockExecuteMethod(500); + + apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test(expected = CloudRuntimeException.class) + public void testGetGivenIOError() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + mockCreateRequest(getMethodMock); + doThrow(new IOException()).when(httpClient).executeMethod(any(GetMethod.class)); + + apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test + public void testList() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + when(getMethodMock.getResponseBodyAsString()).thenReturn(getBusinessServiceJSON()); + mockCreateRequest(getMethodMock); + mockExecuteMethod(200); + + List entities = apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + + GloboDictionaryEntity entity = entities.get(0); + assertEquals(1, entities.size()); + assertEquals(BUSINESS_SERVICE_NAME, entity.getName()); + assertEquals(BUSINESS_SERVICE_ID, entity.getId()); + verify(httpClient).executeMethod(getMethodMock); + } + + @Test + public void testListGivenObjectNotFound() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + when(getMethodMock.getResponseBodyAsString()).thenReturn("[]"); + mockCreateRequest(getMethodMock); + mockExecuteMethod(200); + + List entities = apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + + assertEquals(0, entities.size()); + verify(httpClient).executeMethod(getMethodMock); + } + + @Test(expected = CloudRuntimeException.class) + public void testListGivenStatusCodeIsNot200() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + mockCreateRequest(getMethodMock); + mockExecuteMethod(500); + + apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + @Test(expected = CloudRuntimeException.class) + public void testListGivenIOError() throws Exception { + GetMethod getMethodMock = mock(GetMethod.class); + mockCreateRequest(getMethodMock); + doThrow(new IOException()).when(httpClient).executeMethod(any(GetMethod.class)); + + apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + private void mockExecuteMethod(int status) throws IOException { + when(httpClient.executeMethod(any(GetMethod.class))).thenReturn(status); + } + + private void mockCreateRequest(GetMethod getMethodMock) { + doReturn(getMethodMock).when(apiClient).createRequest(any(GloboDictionaryService.GloboDictionaryEntityType.class), anyString()); + } + + private String getBusinessServiceJSON(){ + return "[{\"macro_servico\": \"Assinatura\", \"detalhe_servico\": \"Vendas\", \"nome\": \"Assinatura - Vendas\", \"descricao\": \"infraestrutura para o sistema de vendas\", \"driver_custeio\": \"N\\u00famero de Assinantes\", \"status\": \"Ativo\", \"coorporativo\": true, \"servico_negocio_armazenamento_id\": \"31cf5f75db9f43409fc15458dc96198b\", \"id_service_now\": \"31cf5f75db9f43409fc15458dc96198b\"}]"; + } +} \ No newline at end of file diff --git a/server/test/com/cloud/globodictionary/manager/GloboDictionaryManagerTest.java b/server/test/com/cloud/globodictionary/manager/GloboDictionaryManagerTest.java new file mode 100644 index 000000000000..09ea1f69f576 --- /dev/null +++ b/server/test/com/cloud/globodictionary/manager/GloboDictionaryManagerTest.java @@ -0,0 +1,109 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES 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.globodictionary.manager; + +import com.cloud.globodictionary.GloboDictionaryEntity; +import com.cloud.globodictionary.GloboDictionaryService; +import com.cloud.globodictionary.apiclient.GloboDictionaryAPIClient; +import com.cloud.globodictionary.apiclient.model.GloboDictionaryEntityVO; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GloboDictionaryManagerTest { + + public static final String BUSINESS_SERVICE_ID = "31cf5f75db9f43409fc15458dc96198b"; + public static final String BUSINESS_SERVICE_NAME = "Assinatura - Vendas"; + + @Mock + private GloboDictionaryAPIClient apiClient; + + private GloboDictionaryManager manager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + manager = new GloboDictionaryManager(apiClient); + } + + @Test + public void testGet(){ + GloboDictionaryEntityVO apiResponse = createEntity(BUSINESS_SERVICE_ID, BUSINESS_SERVICE_NAME, "Em uso"); + when(apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID)).thenReturn(apiResponse); + GloboDictionaryEntity entity = manager.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + + assertNotNull(entity); + assertEquals(apiResponse, entity); + verify(apiClient).get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test + public void testGetGivenObjectIsNotActive(){ + GloboDictionaryEntityVO apiResponse = createEntity(BUSINESS_SERVICE_ID, BUSINESS_SERVICE_NAME, "Desativado"); + when(apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID)).thenReturn(apiResponse); + GloboDictionaryEntity entity = manager.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + + assertNull(entity); + verify(apiClient).get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test + public void testGetGivenObjectNotFound(){ + when(apiClient.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID)).thenReturn(null); + GloboDictionaryEntity entity = manager.get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + + assertNull(entity); + verify(apiClient).get(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE, BUSINESS_SERVICE_ID); + } + + @Test + public void testList(){ + GloboDictionaryEntity apiResponse = createEntity(BUSINESS_SERVICE_ID, BUSINESS_SERVICE_NAME, "Em uso"); + List responseList = Collections.singletonList(apiResponse); + when(apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE)).thenReturn(responseList); + List entities = manager.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + + assertEquals(1, entities.size()); + verify(apiClient).list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + @Test + public void testListGivenObjectIsNotActive(){ + GloboDictionaryEntity apiResponse = createEntity(BUSINESS_SERVICE_ID, BUSINESS_SERVICE_NAME, "Desativado"); + List responseList = Collections.singletonList(apiResponse); + when(apiClient.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE)).thenReturn(responseList); + List entities = manager.list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + + assertEquals(0, entities.size()); + verify(apiClient).list(GloboDictionaryService.GloboDictionaryEntityType.BUSINESS_SERVICE); + } + + + private GloboDictionaryEntityVO createEntity(String businessServiceId, String businessServiceName, String status) { + return new GloboDictionaryEntityVO(businessServiceId, businessServiceName, status); + } +} \ No newline at end of file diff --git a/server/test/com/cloud/network/MockFirewallManagerImpl.java b/server/test/com/cloud/network/MockFirewallManagerImpl.java index 4c9879890b34..d2e7f42ad3a2 100644 --- a/server/test/com/cloud/network/MockFirewallManagerImpl.java +++ b/server/test/com/cloud/network/MockFirewallManagerImpl.java @@ -141,7 +141,7 @@ public boolean revokeFirewallRulesForIp(long ipId, long userId, Account caller) @Override public FirewallRule createRuleForAllCidrs(long ipAddrId, Account caller, Integer startPort, Integer endPort, String protocol, Integer icmpCode, Integer icmpType, - Long relatedRuleId, long networkId) throws NetworkRuleConflictException { + Long relatedRuleId, long networkId) throws NetworkRuleConflictException { // TODO Auto-generated method stub return null; } @@ -177,7 +177,7 @@ public boolean applyDefaultEgressFirewallRule(Long networkId, boolean defaultPol @Override public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type, - Long networkid, TrafficType trafficType) { + Long networkid, TrafficType trafficType) { // TODO Auto-generated method stub } diff --git a/server/test/com/cloud/network/as/AutoScaleCounterProcessorTest.java b/server/test/com/cloud/network/as/AutoScaleCounterProcessorTest.java new file mode 100644 index 000000000000..a47d348cc543 --- /dev/null +++ b/server/test/com/cloud/network/as/AutoScaleCounterProcessorTest.java @@ -0,0 +1,160 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import com.cloud.server.as.VirtualMachineAddress; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadPoolExecutor; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class AutoScaleCounterProcessorTest { + + AutoScaleCounterProcessorImpl autoScaleCounterProcessor; + + @Mock + SNMPClient snmpClient; + @Mock + LogStashClient logStashClient; + @Mock + ThreadPoolExecutor threadPoolExecutor; + @Mock + Logger logger; + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + AutoScaleCounterProcessorImpl.s_logger = logger; + autoScaleCounterProcessor = new AutoScaleCounterProcessorImpl(); + autoScaleCounterProcessor.snmpClient = snmpClient; + autoScaleCounterProcessor.logStashClient = logStashClient; + autoScaleCounterProcessor.threadExecutorPool = threadPoolExecutor; + } + + @Test + public void testProcessOneIpAddress(){ + List virtualMachines = Arrays.asList(new VirtualMachineAddress("172.168.10.10","host1")); + Map counters = new HashMap<>(); + counters.put("cpu", "1.1.1.1.1.1.1.1"); + + autoScaleCounterProcessor.process(createAutoScaleGroup(), virtualMachines, counters); + verify(threadPoolExecutor, times(1)).execute(any(Runnable.class)); + } + + @Test + public void testProcessWithMoreThanOneIpAddress(){ + List virtualMachines = Arrays.asList(new VirtualMachineAddress("172.168.10.10","host1"), new VirtualMachineAddress("172.168.10.9","host2")); + Map counters = createCountersInput(Arrays.asList("cpu"), Arrays.asList("1.1.1.1.1.1.1.1")); + + autoScaleCounterProcessor.process(createAutoScaleGroup(), virtualMachines, counters); + verify(threadPoolExecutor, times(2)).execute(any(Runnable.class)); + } + + @Test + public void testProcessCountersWithOneCounter(){ + Map counters = createCountersInput(Arrays.asList("cpu"), Arrays.asList("1.1.1.1.1.1.1.1")); + Map metricsResult = createMetricsResult(Arrays.asList("cpu"),Arrays.asList("0.1")); + + when(snmpClient.read("172.168.2.10", counters)).thenReturn(metricsResult); + + autoScaleCounterProcessor.processCounters(createAutoScaleGroup(), new VirtualMachineAddress("172.168.2.10", "host"), counters); + verify(snmpClient, times(1)).read("172.168.2.10", counters); + verify(logStashClient, times(1)).send(anyString()); + } + + @Test + public void testProcessCountersWithMoreThanOneCounter(){ + Map counters = createCountersInput(Arrays.asList("cpu", "memory"), Arrays.asList("1.1.1.1.1.1.1.1", "1.1.1.1.1.1.1.2")); + Map metricsResult = createMetricsResult(Arrays.asList("cpu", "memory"),Arrays.asList("0.1", "0.4")); + + when(snmpClient.read("172.168.2.10", counters)).thenReturn(metricsResult); + + autoScaleCounterProcessor.processCounters(createAutoScaleGroup(), new VirtualMachineAddress("172.168.2.10","host1"), counters); + verify(snmpClient, times(1)).read("172.168.2.10", counters); + verify(logStashClient, times(2)).send(anyString()); + } + + @Test + public void testProcessCountersGivenLogStashError(){ + Map counters = createCountersInput(Arrays.asList("cpu"), Arrays.asList("1.1.1.1.1.1.1.1")); + Map metricsResult = createMetricsResult(Arrays.asList("cpu"), Arrays.asList("0.1")); + + when(snmpClient.read("172.168.2.10", counters)).thenReturn(metricsResult); + when(logStashClient.send(anyString())).thenReturn(false); + + AutoScaleVmGroupVO autoScaleGroup = createAutoScaleGroup(); + autoScaleCounterProcessor.processCounters(autoScaleGroup, new VirtualMachineAddress("172.168.2.10","host1"), counters); + verify(logger).error("Error sending message to LogStash: {\"client\":\"cloudstack\",\"autoScaleGroupUuid\":\""+ autoScaleGroup.getUuid() +"\",\"hostname\":\"host1\",\"metric\":\"cpu\",\"value\":0.1,\"count\":1}"); + } + + @Test + public void testCreateLogStashMessageWithOneMetric(){ + Map metricsResult = createMetricsResult(Arrays.asList("cpu"),Arrays.asList("0.1")); + AutoScaleVmGroupVO autoScaleGroup = createAutoScaleGroup(); + List messages = autoScaleCounterProcessor.createLogStashMessage(autoScaleGroup, metricsResult, new VirtualMachineAddress("172.168.10.10","host1")); + + assertEquals(1, messages.size()); + assertEquals("{\"client\":\"cloudstack\",\"autoScaleGroupUuid\":\""+ autoScaleGroup.getUuid() +"\",\"hostname\":\"host1\",\"metric\":\"cpu\",\"value\":0.1,\"count\":1}", messages.get(0)); + } + + @Test + public void testCreateLogStashMessageWithMoreThanOneMetric(){ + Map metricsResult = createMetricsResult(Arrays.asList("cpu", "memory"),Arrays.asList("0.1", "0.5")); + AutoScaleVmGroupVO autoScaleGroup = createAutoScaleGroup(); + List messages = autoScaleCounterProcessor.createLogStashMessage(autoScaleGroup, metricsResult, new VirtualMachineAddress("172.168.10.10","host1")); + + assertEquals(2, messages.size()); + assertEquals("{\"client\":\"cloudstack\",\"autoScaleGroupUuid\":\""+ autoScaleGroup.getUuid() +"\",\"hostname\":\"host1\",\"metric\":\"cpu\",\"value\":0.1,\"count\":1}", messages.get(0)); + assertEquals("{\"client\":\"cloudstack\",\"autoScaleGroupUuid\":\""+ autoScaleGroup.getUuid() +"\",\"hostname\":\"host1\",\"metric\":\"memory\",\"value\":0.5,\"count\":1}", messages.get(1)); + } + + private Map createMetricsResult(List metricNames, List metricValues){ + Map metrics = new LinkedHashMap<>(); + for (int i = 0; i < metricNames.size(); i++) { + metrics.put(metricNames.get(i), Double.parseDouble(metricValues.get(i))); + } + return metrics; + } + + private Map createCountersInput(List counterNames, List snmpCodes){ + Map metrics = new HashMap<>(); + for (int i = 0; i < counterNames.size(); i++) { + metrics.put(counterNames.get(i), snmpCodes.get(i)); + } + return metrics; + } + + private AutoScaleVmGroupVO createAutoScaleGroup() { + return new AutoScaleVmGroupVO(1L,1l, 1L, 1L, 1, 3, 80, 30, new Date(), 1, "enabled", "as-group"); + } +} diff --git a/server/test/com/cloud/network/as/AutoScaleManagerImplTest.java b/server/test/com/cloud/network/as/AutoScaleManagerImplTest.java new file mode 100644 index 000000000000..5a3802527427 --- /dev/null +++ b/server/test/com/cloud/network/as/AutoScaleManagerImplTest.java @@ -0,0 +1,861 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.AutoScaleVmProfileDao; +import com.cloud.network.as.dao.AutoScaleVmProfileNetworkMapDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.lb.LoadBalancingRulesManagerImpl; +import com.cloud.offering.ServiceOffering; +import com.cloud.server.ResourceTag; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.storage.Storage; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.user.Account; +import com.cloud.user.AccountManagerImpl; +import com.cloud.user.AccountService; +import com.cloud.user.AccountVO; +import com.cloud.user.UserVO; +import com.cloud.uservm.UserVm; +import com.cloud.utils.db.EntityManager; +import com.cloud.vm.UserVmService; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class AutoScaleManagerImplTest { + + private AutoScaleManagerImpl autoScaleManager; + + @Mock + LoadBalancingRulesManagerImpl loadBalancingRulesManager; + @Mock + AutoScaleVmGroupDao autoScaleVmGroupDao; + @Mock + AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao; + @Mock + AutoScaleVmGroupPolicyMapDao autoScaleVmGroupPolicyMapDao; + @Mock + AccountManagerImpl accountManager; + @Mock + AutoScaleVmProfileDao autoScaleVmProfileDao; + @Mock + AutoScalePolicyDao autoScalePolicyDao; + @Mock + ScheduledExecutorService threadPool; + @Mock + ResourceTagDao resourceTagDao; + + private static final long AS_GROUP_ID = 10L; + private static final String USER_DATA = "IA=="; + private static String groupUuid; + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDeleteEmptyAutoScaleGroup() throws ResourceUnavailableException { + autoScaleManager = createMockedScaleDownAutoScaleManager(); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + mockAutoScaleGroupFindById(asGroup); + mockLbRulesManagerConfigure(asGroup, true); + mockAutoScaleVmMapCountBy(0); + mockRemoveAutoScaleGroup(); + mockRemoveAutoScalePolicyByGroup(); + + assertTrue(autoScaleManager.deleteAutoScaleVmGroup(AS_GROUP_ID)); + verify(autoScaleManager, times(0)).doScaleDown(anyLong(), anyInt()); + verify(autoScaleVmGroupDao, times(1)).remove(AS_GROUP_ID); + } + + @Test + public void testDeleteAutoScaleGroupWithThreeVms() throws ResourceUnavailableException { + autoScaleManager = createMockedScaleDownAutoScaleManager(); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + mockAutoScaleGroupFindById(asGroup); + mockLbRulesManagerConfigure(asGroup, true); + mockAutoScaleVmMapCountBy(3); + mockRemoveAutoScaleGroup(); + mockRemoveAutoScalePolicyByGroup(); + + assertTrue(autoScaleManager.deleteAutoScaleVmGroup(AS_GROUP_ID)); + verify(autoScaleManager, times(1)).destroyVmGroupVMs(anyLong()); + verify(autoScaleVmGroupDao, times(1)).remove(AS_GROUP_ID); + } + + @Test + public void testDeleteAutoScaleGroupGivenErrorInLbConfiguration() throws ResourceUnavailableException { + autoScaleManager = createMockedScaleDownAutoScaleManager(); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + mockAutoScaleGroupFindById(asGroup); + mockLbRulesManagerConfigure(asGroup, false); + + assertFalse(autoScaleManager.deleteAutoScaleVmGroup(AS_GROUP_ID)); + } + + @Test + public void testDeleteAutoScaleGroupWithNullLbId() throws ResourceUnavailableException { + autoScaleManager = createMockedScaleDownAutoScaleManager(); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + asGroup.setLoadBalancerId(null); + mockAutoScaleGroupFindById(asGroup); + mockLbRulesManagerConfigure(asGroup, false); + + assertFalse(autoScaleManager.deleteAutoScaleVmGroup(AS_GROUP_ID)); + } + + @Test + public void testScaleUp() throws InsufficientCapacityException, ResourceUnavailableException { + testScaleUpWith(1, true, true); + verify(autoScaleVmGroupVmMapDao, times(1)).persist(any(AutoScaleVmGroupVmMapVO.class)); + verify(autoScaleManager, times(1)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP), anyString()); + verify(autoScaleManager, times(1)).updateLastQuietTime(anyLong(), eq("scaleup")); + } + + @Test + public void testScaleUpThreeVms() throws InsufficientCapacityException, ResourceUnavailableException { + testScaleUpWith(3, true, true); + verify(autoScaleVmGroupVmMapDao, times(3)).persist(any(AutoScaleVmGroupVmMapVO.class)); + verify(autoScaleManager, times(3)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP), anyString()); + verify(autoScaleManager, times(3)).updateLastQuietTime(anyLong(), eq("scaleup")); + } + + @Test + public void testScaleUpFailedToAssignToLB(){ + testScaleUpWith(1, true, false); + verify(autoScaleVmGroupVmMapDao, times(0)).persist(any(AutoScaleVmGroupVmMapVO.class)); + verify(autoScaleManager, times(1)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED), anyString()); + } + + @Test + public void testScaleUpFailedToStartVm() throws InsufficientCapacityException, ResourceUnavailableException { + testScaleUpWith(1, false, false); + verify(autoScaleVmGroupVmMapDao, times(0)).persist(any(AutoScaleVmGroupVmMapVO.class)); + } + + private void testScaleUpWith(int vmCount, boolean startVmResult, boolean assignToLbResult){ + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + autoScaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(autoScaleManager); + + List resourceTags = new ArrayList<>(); + when(resourceTagDao.listBy(asGroup.getId(), ResourceTag.ResourceObjectType.AutoScaleVmGroup)).thenReturn(new ArrayList()); + + when(autoScaleVmGroupDao.findById(10L)).thenReturn(asGroup); + //mocking createVm, assignLBRuleToNewVm and startVM so they can be tested in isolation + doReturn(createUserVm(1L)).when(autoScaleManager).createNewVM(asGroup); + doNothing().when(autoScaleManager).startNewVM(1L); + doReturn(assignToLbResult).when(autoScaleManager).assignLBruleToNewVm(any(UserVm.class), eq(asGroup)); + if(startVmResult && assignToLbResult){ + doNothing().when(autoScaleManager).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP), anyString()); + }else { + doNothing().when(autoScaleManager).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED), anyString()); + } + autoScaleManager.doScaleUp(AS_GROUP_ID, vmCount); + } + + @Test + public void testScaleUpGivenServerApiException(){ + testScaleUpException(new ServerApiException(ApiErrorCode.ACCOUNT_RESOURCE_LIMIT_ERROR, "Account limit exceeded")); + } + + @Test + public void testScaleUpGivenGenericException(){ + testScaleUpException(new ConcurrentOperationException("Error")); + } + + private void testScaleUpException(Exception exception){ + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + autoScaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(autoScaleManager); + + when(autoScaleVmGroupDao.findById(10L)).thenReturn(asGroup); + //mocking createVm, assignLBRuleToNewVm and startVM so they can be tested in isolation + doReturn(createUserVm(1L)).when(autoScaleManager).createNewVM(asGroup); + doThrow(exception).when(autoScaleManager).startNewVM(1L); + doNothing().when(autoScaleManager).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED), anyString()); + + try { + autoScaleManager.doScaleUp(AS_GROUP_ID, 1); + fail(); + }catch(Exception ex){ + verify(autoScaleVmGroupVmMapDao, times(0)).persist(any(AutoScaleVmGroupVmMapVO.class)); + verify(autoScaleManager, times(1)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP_FAILED), anyString()); + } + } + + @Test + public void testScaleDown(){ + testScaleDownWith(1L, null); + + verify(autoScaleVmGroupVmMapDao, times(1)).remove(AS_GROUP_ID, 1L); + verify(threadPool, times(1)).schedule(any(Runnable.class), eq(120L), eq(TimeUnit.SECONDS)); + } + + @Test + public void testScaleDownGivenErrorRemovingFromLB(){ + testScaleDownWith(-1L, null); + + verify(autoScaleVmGroupVmMapDao, times(0)).remove(AS_GROUP_ID, 1L); + verify(autoScaleManager, times(1)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED), anyString()); + } + + @Test + public void testScaleDownGivenError(){ + try { + testScaleDownWith(1L, new RuntimeException()); + fail(); + }catch(Exception ex){ + verify(autoScaleVmGroupVmMapDao, times(0)).remove(AS_GROUP_ID, 1L); + verify(autoScaleManager, times(1)).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED), anyString()); + } + } + + @Test + public void testUpdateLastQuietTimeScaleUp(){ + autoScaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(autoScaleManager); + + AutoScaleVmGroupPolicyMapVO asPolicyMap1 = new AutoScaleVmGroupPolicyMapVO(1L, 1L, false); + AutoScaleVmGroupPolicyMapVO asPolicyMap2 = new AutoScaleVmGroupPolicyMapVO(1L, 2L, false); + AutoScalePolicyVO policy1 = new AutoScalePolicyVO(1L, 1L, 60, 120, null, "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + policy1.id = 1L; + AutoScalePolicyVO policy2 = new AutoScalePolicyVO(1L, 1L, 60, 120, null, "scaledown", 1, AutoScalePolicy.LogicalOperator.AND); + policy2.id = 2L; + + when(autoScaleVmGroupPolicyMapDao.listByVmGroupId(anyLong())).thenReturn(Arrays.asList(asPolicyMap1, asPolicyMap2)); + when(autoScalePolicyDao.findById(1L)).thenReturn(policy1); + + autoScaleManager.updateLastQuietTime(1L, "scaleup"); + + assertNotNull(policy1.getLastQuiteTime()); + assertNull(policy2.getLastQuiteTime()); + verify(autoScalePolicyDao, times(1)).persist(policy1); + } + + @Test + public void testUpdateLastQuietTimeScaleDown(){ + autoScaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(autoScaleManager); + + AutoScaleVmGroupPolicyMapVO asPolicyMap1 = new AutoScaleVmGroupPolicyMapVO(1L, 1L, false); + AutoScaleVmGroupPolicyMapVO asPolicyMap2 = new AutoScaleVmGroupPolicyMapVO(1L, 2L, false); + AutoScalePolicyVO policy1 = new AutoScalePolicyVO(1L, 1L, 60, 120, null, "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + policy1.id = 1L; + AutoScalePolicyVO policy2 = new AutoScalePolicyVO(1L, 1L, 60, 120, null, "scaledown", 1, AutoScalePolicy.LogicalOperator.AND); + policy2.id = 2L; + + when(autoScaleVmGroupPolicyMapDao.listByVmGroupId(anyLong())).thenReturn(Arrays.asList(asPolicyMap1, asPolicyMap2)); + when(autoScalePolicyDao.findById(1L)).thenReturn(policy2); + + autoScaleManager.updateLastQuietTime(1L, "scaledown"); + + assertNull(policy1.getLastQuiteTime()); + assertNotNull(policy2.getLastQuiteTime()); + verify(autoScalePolicyDao, times(1)).persist(policy2); + } + + @Test + public void testCreateNewVMWithAdvancedNetworkAndNoSecurityGroupAndUserData() throws ResourceUnavailableException, ResourceAllocationException, InsufficientCapacityException { + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + + AccountVO owner = new AccountVO(); + mockGetActiveAccount(owner); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + DataCenterVO zone = createZone(); + mockFindDataCenter(entityManager, zone); + ServiceOfferingVO serviceOffering = createServiceOffering(); + mockFindSystemOffering(entityManager, serviceOffering); + FakeTemplate template = new FakeTemplate(); + mockFindTemplate(entityManager, template); + + mockListProfileNetworkMap(autoScaleVmProfile, new ArrayList()); + mockFindNetworkById(); + + doReturn(1L).when(autoScaleManager).getDestinationNetworkId(asGroup); + doReturn("instanceName").when(autoScaleManager).createInstanceName(asGroup); + + UserVmVO userVm = createVM(); + + mockCreateUserVm(owner, zone, serviceOffering, template, userVm, Arrays.asList(1L)); + + assertEquals(userVm, autoScaleManager.createNewVM(asGroup)); + } + + @Test + public void testCreateNewVMWithTwoNICs() throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException { + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + + AccountVO owner = new AccountVO(); + mockGetActiveAccount(owner); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + DataCenterVO zone = createZone(); + mockFindDataCenter(entityManager, zone); + ServiceOfferingVO serviceOffering = createServiceOffering(); + mockFindSystemOffering(entityManager, serviceOffering); + FakeTemplate template = new FakeTemplate(); + mockFindTemplate(entityManager, template); + + mockListProfileNetworkMap(autoScaleVmProfile, Arrays.asList(2L)); + mockFindNetworkById(); + + doReturn(1L).when(autoScaleManager).getDestinationNetworkId(asGroup); + doReturn("instanceName").when(autoScaleManager).createInstanceName(asGroup); + + UserVmVO userVm = createVM(); + + mockCreateUserVm(owner, zone, serviceOffering, template, userVm, Arrays.asList(1L, 2L)); + + assertEquals(userVm, autoScaleManager.createNewVM(asGroup)); + } + + @Test + public void testCreateNewVMWithInvalidServiceOffering() throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException { + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + mockGetActiveAccount(new AccountVO()); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + mockFindDataCenter(entityManager, createZone()); + mockFindSystemOffering(entityManager, null); + + try { + autoScaleManager.createNewVM(asGroup); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("Unable to find service offering: 1", e.getMessage()); + } + } + + @Test + public void testCreateNewVMWithInvalidZone(){ + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + mockGetActiveAccount(new AccountVO()); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + mockFindDataCenter(entityManager, null); + + try { + autoScaleManager.createNewVM(asGroup); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("Unable to find zone by id=1", e.getMessage()); + } + } + + @Test + public void testCreateNewVMWithInvalidTemplate(){ + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + mockGetActiveAccount(new AccountVO()); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + mockFindDataCenter(entityManager, createZone()); + mockFindSystemOffering(entityManager, createServiceOffering()); + mockFindTemplate(entityManager, null); + + try { + autoScaleManager.createNewVM(asGroup); + fail(); + }catch(InvalidParameterValueException e){ + assertEquals("Unable to use template 1", e.getMessage()); + } + } + + @Test + public void testCreateNewVMWithEmptyTemplate(){ + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + autoScaleVmProfile.setTemplateId(-1L); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + mockGetActiveAccount(new AccountVO()); + + assertNull(autoScaleManager.createNewVM(asGroup)); + } + + @Test + public void testCreateNewVMGivenInsufficientCapacity() throws ResourceUnavailableException, ResourceAllocationException, InsufficientCapacityException { + autoScaleManager = spy(new AutoScaleManagerImpl()); + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + AutoScaleVmProfileVO autoScaleVmProfile = new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, USER_DATA); + + mockFindAutoScaleVmProfile(autoScaleVmProfile); + + AccountVO owner = new AccountVO(); + mockGetActiveAccount(owner); + + EntityManager entityManager = mock(EntityManager.class); + autoScaleManager._entityMgr = entityManager; + + DataCenterVO zone = createZone(); + mockFindDataCenter(entityManager, zone); + ServiceOfferingVO serviceOffering = createServiceOffering(); + mockFindSystemOffering(entityManager, serviceOffering); + FakeTemplate template = new FakeTemplate(); + mockFindTemplate(entityManager, template); + + mockListProfileNetworkMap(autoScaleVmProfile, new ArrayList()); + mockFindNetworkById(); + + doReturn(1L).when(autoScaleManager).getDestinationNetworkId(asGroup); + doReturn("instanceName").when(autoScaleManager).createInstanceName(asGroup); + + UserVmService userVmService = mock(UserVmService.class); + when(userVmService.createAdvancedVirtualMachine(eq(zone), eq(serviceOffering), eq(template), + eq(Arrays.asList(1L)), eq(owner), eq("instanceName"), eq("instanceName"), isNull(Long.class), + isNull(Long.class), isNull(String.class), eq(Hypervisor.HypervisorType.XenServer), + eq(BaseCmd.HTTPMethod.POST), eq(USER_DATA), isNull(String.class), isNull(Map.class), + any(Network.IpAddresses.class), eq(true), isNull(String.class), isNull(List.class), any(Map.class), isNull(String.class), isNull(Map.class), isNull(Map.class))). + thenThrow(new InsufficientNetworkCapacityException("Network error", Networks.class, 1L)); + autoScaleManager._userVmService = userVmService; + + try { + autoScaleManager.createNewVM(asGroup); + fail(); + }catch(ServerApiException ex){ + assertEquals(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getErrorCode()); + } + } + + @Test + public void testValidateUserDataGivenValidData(){ + new AutoScaleManagerImpl().validateUserData("dGVzdGU="); + } + + @Test + public void testValidateUserDataGivenEmptyData(){ + new AutoScaleManagerImpl().validateUserData(null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateUserDataGivenShortUserData(){ + new AutoScaleManagerImpl().validateUserData("a"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateUserDataGivenLongUserData(){ + new AutoScaleManagerImpl().validateUserData(new String(new char[35000])); + } + + private AccountService mockGetActiveAccount(AccountVO owner) { + AccountService accountService = mock(AccountService.class); + when(accountService.getActiveAccountById(1L)).thenReturn(owner); + autoScaleManager._accountService = accountService; + return accountService; + } + + private UserVmVO createVM() { + return new UserVmVO(1L, "instanceName", "instanceName", 1L, Hypervisor.HypervisorType.XenServer, 1L, true, true, 1L, 1L, 1L, 1,null, "instanceName", 1L); + } + + private DataCenterVO createZone() { + return new DataCenterVO(1L, "zone1", "", null, null, null, null, null, "domain", 1L, DataCenter.NetworkType.Advanced, "", ""); + } + + private UserVmService mockCreateUserVm(AccountVO owner, DataCenterVO zone, ServiceOfferingVO serviceOffering, VirtualMachineTemplate template, UserVmVO userVm, List networkIds) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException { + UserVmService userVmService = mock(UserVmService.class); + when(userVmService.createAdvancedVirtualMachine(eq(zone), eq(serviceOffering), eq(template), eq(networkIds), eq(owner), eq("instanceName"), eq("instanceName"), isNull(Long.class), isNull(Long.class), isNull(String.class), eq(Hypervisor.HypervisorType.XenServer), eq(BaseCmd.HTTPMethod.POST), eq(USER_DATA), isNull(String.class), isNull(Map.class), any(Network.IpAddresses.class), eq(true), isNull(String.class), isNull(List.class), any(Map.class), isNull(String.class), isNull(Map.class), isNull(Map.class))).thenReturn(userVm); + autoScaleManager._userVmService = userVmService; + return userVmService; + } + + private AutoScaleVmProfileDao mockFindAutoScaleVmProfile(AutoScaleVmProfileVO autoScaleVmProfile) { + AutoScaleVmProfileDao autoScaleVmProfileDao = mock(AutoScaleVmProfileDao.class); + when(autoScaleVmProfileDao.findById(anyLong())).thenReturn(autoScaleVmProfile); + autoScaleManager._autoScaleVmProfileDao = autoScaleVmProfileDao; + return autoScaleVmProfileDao; + } + + private NetworkDao mockFindNetworkById() { + NetworkDao networkDao = mock(NetworkDao.class); + when(networkDao.findById(1L)).thenReturn(createNetwork()); + when(networkDao.findById(2L)).thenReturn(createNetwork()); + autoScaleManager._networkDao = networkDao; + return networkDao; + } + + private NetworkVO createNetwork() { + return new NetworkVO(1L, + Networks.TrafficType.Guest, Networks.Mode.Dhcp, Networks.BroadcastDomainType.LinkLocal, 1L, 1L, 1L, 1L, "network", "", "", Network.GuestType.Shared, 1L, 1L, ControlledEntity.ACLType.Domain, false, 1L, false); + } + + private AutoScaleVmProfileNetworkMapDao mockListProfileNetworkMap(AutoScaleVmProfileVO autoScaleVmProfile, List networkIds) { + AutoScaleVmProfileNetworkMapDao autoScaleVmProfileNetworkMapDao = mock(AutoScaleVmProfileNetworkMapDao.class); + List autoScaleVmProfileNetworkMapVOs = new ArrayList<>(); + for(Long networkId : networkIds){ + autoScaleVmProfileNetworkMapVOs.add(new AutoScaleVmProfileNetworkMapVO(autoScaleVmProfile.getId(), networkId)); + } + when(autoScaleVmProfileNetworkMapDao.listByVmProfileId(autoScaleVmProfile.getId())).thenReturn(autoScaleVmProfileNetworkMapVOs); + autoScaleManager._autoScaleVmProfileNetworkMapDao = autoScaleVmProfileNetworkMapDao; + return autoScaleVmProfileNetworkMapDao; + } + + private void mockFindTemplate(EntityManager entityManager, VirtualMachineTemplate template) { + when(entityManager.findById(VirtualMachineTemplate.class, 1L)).thenReturn(template); + } + + private void mockFindSystemOffering(EntityManager entityManager, ServiceOfferingVO serviceOffering) { + when(entityManager.findById(ServiceOffering.class, 1L)).thenReturn(serviceOffering); + } + + private ServiceOfferingVO createServiceOffering(){ + return new ServiceOfferingVO("Small instance", 1, 256, 100, 100, 100, true, "display", null, false, true, null, false, VirtualMachine.Type.Instance, false); + } + + private void mockFindDataCenter(EntityManager entityManager, DataCenter zone) { + when(entityManager.findById(DataCenter.class, 1L)).thenReturn(zone); + } + + private void testScaleDownWith(Long removeLbResult, Exception exception) { + AutoScaleVmGroupVO asGroup = createAutoScaleGroup(); + autoScaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(autoScaleManager); + + when(autoScaleVmGroupDao.findById(10L)).thenReturn(asGroup); + doReturn(true).when(autoScaleManager).checkConditionDown(asGroup); + if(exception != null){ + doThrow(exception).when(autoScaleManager).removeLBrule(asGroup); + }else{ + doReturn(removeLbResult).when(autoScaleManager).removeLBrule(asGroup); + } + if(removeLbResult == -1 || exception != null){ + doNothing().when(autoScaleManager).createEvent(eq(groupUuid), eq(EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN_FAILED), anyString()); + } + when(autoScaleVmProfileDao.findById(asGroup.getProfileId())).thenReturn(new AutoScaleVmProfileVO()); + autoScaleManager.doScaleDown(AS_GROUP_ID, 1); + } + + private void mockAutoScaleGroupFindById(AutoScaleVmGroupVO asGroup){ + when(autoScaleVmGroupDao.findById(asGroup.getId())).thenReturn(asGroup); + } + + private void mockLbRulesManagerConfigure(AutoScaleVmGroupVO asGroup, boolean result) throws ResourceUnavailableException { + when(loadBalancingRulesManager.configureLbAutoScaleVmGroup(asGroup.getId(), AutoScaleVmGroup.State_Enabled)).thenReturn(result); + } + + private void mockAutoScaleVmMapCountBy(int vmCount){ + when(autoScaleVmGroupVmMapDao.countByGroup(AutoScaleManagerImplTest.AS_GROUP_ID)).thenReturn(vmCount); + } + + private void mockRemoveAutoScaleGroup(){ + when(autoScaleVmGroupDao.remove(AutoScaleManagerImplTest.AS_GROUP_ID)).thenReturn(true); + } + + private void mockRemoveAutoScalePolicyByGroup(){ + when(autoScaleVmGroupPolicyMapDao.removeByGroupId(AutoScaleManagerImplTest.AS_GROUP_ID)).thenReturn(true); + } + + private AutoScaleVmGroupVO createAutoScaleGroup(){ + AutoScaleVmGroupVO asGroup = new AutoScaleVmGroupVO(1L,1l, 1L, 1L, 1, 3, 80, 30, new Date(), 1, "enabled", "as-group"); + asGroup.id = AutoScaleManagerImplTest.AS_GROUP_ID; + groupUuid = asGroup.getUuid(); + return asGroup; + } + + private UserVmVO createUserVm(Long id) { + return new UserVmVO(id, "test", "test", 1L, Hypervisor.HypervisorType.Any, 1L, false, false, 1L, 1L, 5L, 1,"test", "test", 1L); + } + + private AutoScaleManagerImpl createMockedScaleDownAutoScaleManager() { + AutoScaleManagerImpl scaleManager = spy(new AutoScaleManagerImpl()); + configureMocks(scaleManager); + doNothing().when(scaleManager).doScaleDown(anyLong(), anyInt()); + return scaleManager; + } + + private void configureMocks(AutoScaleManagerImpl autoScaleManager) { + autoScaleManager._lbRulesMgr = loadBalancingRulesManager; + autoScaleManager._autoScaleVmGroupDao = autoScaleVmGroupDao; + autoScaleManager._autoScaleVmGroupVmMapDao = autoScaleVmGroupVmMapDao; + autoScaleManager._accountMgr = accountManager; + autoScaleManager._autoScaleVmGroupPolicyMapDao = autoScaleVmGroupPolicyMapDao; + autoScaleManager._autoScaleVmProfileDao = autoScaleVmProfileDao; + autoScaleManager._autoScalePolicyDao = autoScalePolicyDao; + autoScaleManager._executor = threadPool; + autoScaleManager._resourceTagDao = resourceTagDao; + + AccountVO acct = new AccountVO(200L); + acct.setType(Account.ACCOUNT_TYPE_NORMAL); + acct.setAccountName("user"); + acct.setDomainId(1L); + + UserVO user = new UserVO(); + user.setUsername("user"); + user.setAccountId(acct.getAccountId()); + + CallContext.register(user, acct); + when(accountManager.getSystemAccount()).thenReturn(acct); + when(accountManager.getSystemUser()).thenReturn(user); + } + + class FakeTemplate implements VirtualMachineTemplate{ + + @Override + public State getState() { + return null; + } + + @Override + public boolean isFeatured() { + return false; + } + + @Override + public boolean isPublicTemplate() { + return false; + } + + @Override + public boolean isExtractable() { + return false; + } + + @Override + public String getName() { + return null; + } + + @Override + public Storage.ImageFormat getFormat() { + return null; + } + + @Override + public boolean isRequiresHvm() { + return false; + } + + @Override + public String getDisplayText() { + return null; + } + + @Override + public boolean getEnablePassword() { + return false; + } + + @Override + public boolean getEnableSshKey() { + return false; + } + + @Override + public boolean isCrossZones() { + return false; + } + + @Override + public Date getCreated() { + return null; + } + + @Override + public long getGuestOSId() { + return 0; + } + + @Override + public boolean isBootable() { + return false; + } + + @Override + public Storage.TemplateType getTemplateType() { + return null; + } + + @Override + public Hypervisor.HypervisorType getHypervisorType() { + return Hypervisor.HypervisorType.XenServer; + } + + @Override + public int getBits() { + return 0; + } + + @Override + public String getUniqueName() { + return null; + } + + @Override + public String getUrl() { + return null; + } + + @Override + public String getChecksum() { + return null; + } + + @Override + public Long getSourceTemplateId() { + return null; + } + + @Override + public String getTemplateTag() { + return null; + } + + @Override + public Map getDetails() { + return null; + } + + @Override + public boolean isDynamicallyScalable() { + return false; + } + + @Override + public Long getParentTemplateId() { + return null; + } + + @Override + public long getUpdatedCount() { + return 0; + } + + @Override + public void incrUpdatedCount() { + + } + + @Override + public Date getUpdated() { + return null; + } + + @Override + public Class getEntityType() { + return null; + } + + @Override + public String getUuid() { + return null; + } + + @Override + public long getId() { + return 0; + } + + @Override + public long getAccountId() { + return 0; + } + + @Override + public long getDomainId() { + return 0; + } + } +} diff --git a/server/test/com/cloud/network/as/AutoScaleStatsCollectorTest.java b/server/test/com/cloud/network/as/AutoScaleStatsCollectorTest.java new file mode 100644 index 000000000000..a03c25f9c3a6 --- /dev/null +++ b/server/test/com/cloud/network/as/AutoScaleStatsCollectorTest.java @@ -0,0 +1,269 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PerformanceMonitorAnswer; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.AutoScaleVmProfileDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.utils.Pair; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public abstract class AutoScaleStatsCollectorTest { + + protected AutoScaleStatsCollector autoScaleStatsCollector; + protected AutoScaleVmGroup asGroup; + protected List vmList = new ArrayList<>(); + + public void setUp(){ + asGroup = new AutoScaleVmGroupVO(1L,1l, 1L, 1L, 1, 3, 80, 30, new Date(), 1, "enabled", "as-group"); + VMInstanceVO vm = new VMInstanceVO(1, 1, "vm-01", "vm-01", VirtualMachine.Type.Instance, 1L, Hypervisor.HypervisorType.Simulator, 1, 1, 1, 1l, false, true, 1L); + vm.setHostId(1L); + vmList.add(vm); + } + + @Test + public void testReadVmStatsWithCpuCounter(){ + mockAutoScaleVmGroupVmMapDao(); + mockAutoScaleGroupPolicyMapDao(); + mockAutoScalePolicyDao(); + mockAutoScalePolicyConditionMapDao(); + mockConditionDao(); + mockCounterDao("cpu"); + mockAgenManager("0.1"); + + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary.get("cpu") == 0.1D; + } + + @Test + public void testReadVmStatsWithMemoryCounter(){ + mockAutoScaleVmGroupVmMapDao(); + mockAutoScaleGroupPolicyMapDao(); + mockAutoScalePolicyDao(); + mockAutoScalePolicyConditionMapDao(); + mockConditionDao(); + mockCounterDao("memory"); + mockAgenManager("128"); + mockAutoScaleProfileDao(); + mockServiceOfferingDao(512); + + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary.get("memory") == 0.25D; //fifty percent of 256 RAM + } + + @Test + public void testReadVmStatsGivenVmListEmpty(){ + vmList = new ArrayList<>(); + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary == null; + } + + @Test + public void testReadVmStatsGivenVmListIsNull(){ + vmList = null; + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary == null; + } + + @Test + public void testReadVmStatsWithError(){ + AutoScaleVmGroupVmMapDao _asGroupVmDao = mock(AutoScaleVmGroupVmMapDao.class); + when(_asGroupVmDao.listByGroup(anyLong())).thenThrow(new RuntimeException()); + autoScaleStatsCollector._asGroupVmDao = _asGroupVmDao; + + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary == null; + } + + @Test + public void testGetPairOfCounterNameAndDuration(){ + AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao = mock(AutoScaleVmGroupPolicyMapDao.class); + List groupPolicymap = new ArrayList<>(); + groupPolicymap.add(new AutoScaleVmGroupPolicyMapVO(1L, 1L)); + when(_asGroupPolicyDao.listByVmGroupId(anyLong())).thenReturn(groupPolicymap); + autoScaleStatsCollector._asGroupPolicyDao = _asGroupPolicyDao; + + AutoScalePolicyDao _asPolicyDao = mock(AutoScalePolicyDao.class); + AutoScalePolicyVO policyUp = new AutoScalePolicyVO(1L, 1L, 60, 120, new Date(), "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + when(_asPolicyDao.findById(1L)).thenReturn(policyUp); + autoScaleStatsCollector._asPolicyDao = _asPolicyDao; + + AutoScalePolicyConditionMapDao _asConditionMapDao = mock(AutoScalePolicyConditionMapDao.class); + List policiesMap = new ArrayList<>(); + policiesMap.add(new AutoScalePolicyConditionMapVO(1L, 1L)); + when(_asConditionMapDao.findByPolicyId(anyLong())).thenReturn(policiesMap); + autoScaleStatsCollector._asConditionMapDao = _asConditionMapDao; + + ConditionDao _asConditionDao = mock(ConditionDao.class); + ConditionVO conditionUp = new ConditionVO(1L, 90L, 1L, 1L, Condition.Operator.GT); + when(_asConditionDao.findById(1L)).thenReturn(conditionUp); + autoScaleStatsCollector._asConditionDao = _asConditionDao; + + CounterDao _asCounterDao = mock(CounterDao.class); + CounterVO counter = new CounterVO(Counter.Source.cpu, "cpu", ""); + when(_asCounterDao.findById(1L)).thenReturn(counter); + autoScaleStatsCollector._asCounterDao = _asCounterDao; + + List> counterAndDurations = autoScaleStatsCollector.getPairOfCounterNameAndDuration(asGroup); + + assert 1 == counterAndDurations.size(); + assert "cpu".equals(counterAndDurations.get(0).first().split(",")[0]); + assert 60 == counterAndDurations.get(0).second(); + } + + @Test + public void testGetPairOfCounterNameAndDurationGivenPolicyWithTwoConditions(){ + AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao = mock(AutoScaleVmGroupPolicyMapDao.class); + List groupPolicymap = new ArrayList<>(); + groupPolicymap.add(new AutoScaleVmGroupPolicyMapVO(1L, 1L)); + when(_asGroupPolicyDao.listByVmGroupId(anyLong())).thenReturn(groupPolicymap); + autoScaleStatsCollector._asGroupPolicyDao = _asGroupPolicyDao; + + AutoScalePolicyDao _asPolicyDao = mock(AutoScalePolicyDao.class); + AutoScalePolicyVO policyUp = new AutoScalePolicyVO(1L, 1L, 60, 120, new Date(), "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + when(_asPolicyDao.findById(1L)).thenReturn(policyUp); + autoScaleStatsCollector._asPolicyDao = _asPolicyDao; + + AutoScalePolicyConditionMapDao _asConditionMapDao = mock(AutoScalePolicyConditionMapDao.class); + List policiesMap = new ArrayList<>(); + policiesMap.add(new AutoScalePolicyConditionMapVO(1L, 1L)); + policiesMap.add(new AutoScalePolicyConditionMapVO(1L, 2L)); + when(_asConditionMapDao.findByPolicyId(anyLong())).thenReturn(policiesMap); + autoScaleStatsCollector._asConditionMapDao = _asConditionMapDao; + + ConditionDao _asConditionDao = mock(ConditionDao.class); + ConditionVO condition1 = new ConditionVO(1L, 90L, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 90L, 1L, 1L, Condition.Operator.GT); + when(_asConditionDao.findById(1L)).thenReturn(condition1); + when(_asConditionDao.findById(2L)).thenReturn(condition2); + autoScaleStatsCollector._asConditionDao = _asConditionDao; + + CounterDao _asCounterDao = mock(CounterDao.class); + CounterVO counter1 = new CounterVO(Counter.Source.cpu, "cpu", ""); + CounterVO counter2 = new CounterVO(Counter.Source.memory, "memory", ""); + when(_asCounterDao.findById(1L)).thenReturn(counter1); + when(_asCounterDao.findById(2L)).thenReturn(counter2); + autoScaleStatsCollector._asCounterDao = _asCounterDao; + + List> counterAndDurations = autoScaleStatsCollector.getPairOfCounterNameAndDuration(asGroup); + + assert 2 == counterAndDurations.size(); + assert "cpu".equals(counterAndDurations.get(0).first().split(",")[0]); + assert "memory".equals(counterAndDurations.get(1).first().split(",")[0]); + assert 60 == counterAndDurations.get(0).second(); + } + + protected void mockAutoScaleVmGroupVmMapDao(){ + AutoScaleVmGroupVmMapDao _asGroupVmDao = mock(AutoScaleVmGroupVmMapDao.class); + List asGroupVmVOs = new ArrayList<>(); + asGroupVmVOs.add(new AutoScaleVmGroupVmMapVO(1L, 1L)); + when(_asGroupVmDao.listByGroup(anyLong())).thenReturn(asGroupVmVOs); + autoScaleStatsCollector._asGroupVmDao = _asGroupVmDao; + } + + protected void mockAgenManager(String counterValue) { + AgentManager agentManager = mock(AgentManager.class); + Answer answer = new PerformanceMonitorAnswer(null, true, "1.1:" + counterValue); + try { + when(agentManager.send(eq(1L), any(Command.class))).thenReturn(answer); + }catch(Exception e){} + autoScaleStatsCollector._agentMgr = agentManager; + } + + protected void mockCounterDao(String counterName) { + CounterDao _asCounterDao = mock(CounterDao.class); + CounterVO counter = new CounterVO(Counter.Source.valueOf(counterName), counterName, ""); + when(_asCounterDao.findById(1L)).thenReturn(counter); + autoScaleStatsCollector._asCounterDao = _asCounterDao; + } + + protected void mockConditionDao() { + ConditionDao _asConditionDao = mock(ConditionDao.class); + ConditionVO conditionUp = new ConditionVO(1L, 90L, 1L, 1L, Condition.Operator.GT); + ConditionVO conditionDown = new ConditionVO(1L, 10L, 1L, 1L, Condition.Operator.LT); + when(_asConditionDao.findById(1L)).thenReturn(conditionUp); + when(_asConditionDao.findById(2L)).thenReturn(conditionDown); + autoScaleStatsCollector._asConditionDao = _asConditionDao; + } + + protected void mockAutoScalePolicyConditionMapDao() { + AutoScalePolicyConditionMapDao _asConditionMapDao = mock(AutoScalePolicyConditionMapDao.class); + List policiesMap = new ArrayList<>(); + policiesMap.add(new AutoScalePolicyConditionMapVO(1L, 1L)); + when(_asConditionMapDao.findByPolicyId(anyLong())).thenReturn(policiesMap); + autoScaleStatsCollector._asConditionMapDao = _asConditionMapDao; + } + + protected void mockAutoScalePolicyDao() { + AutoScalePolicyDao _asPolicyDao = mock(AutoScalePolicyDao.class); + AutoScalePolicyVO policyUp = new AutoScalePolicyVO(1L, 1L, 60, 120, new Date(), "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + AutoScalePolicyVO policyDown = new AutoScalePolicyVO(1L, 1L, 60, 120, new Date(), "scaledown", 1, AutoScalePolicy.LogicalOperator.AND); + when(_asPolicyDao.findById(1L)).thenReturn(policyUp); + when(_asPolicyDao.findById(2L)).thenReturn(policyDown); + autoScaleStatsCollector._asPolicyDao = _asPolicyDao; + } + + protected void mockAutoScaleGroupPolicyMapDao() { + AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao = mock(AutoScaleVmGroupPolicyMapDao.class); + List groupPolicymap = new ArrayList<>(); + groupPolicymap.add(new AutoScaleVmGroupPolicyMapVO(1L, 1L)); + groupPolicymap.add(new AutoScaleVmGroupPolicyMapVO(1L, 2L)); + when(_asGroupPolicyDao.listByVmGroupId(anyLong())).thenReturn(groupPolicymap); + autoScaleStatsCollector._asGroupPolicyDao = _asGroupPolicyDao; + } + + protected void mockServiceOfferingDao(Integer memorySize) { + ServiceOfferingDao _serviceOfferingDao = mock(ServiceOfferingDao.class); + when(_serviceOfferingDao.findById(anyLong())).thenReturn(new ServiceOfferingVO("offering", 100, memorySize, 1, 1000, 1000, false, "offering", null, false, false, null, false, VirtualMachine.Type.Instance, false)); + autoScaleStatsCollector._serviceOfferingDao = _serviceOfferingDao; + } + + protected void mockAutoScaleProfileDao() { + AutoScaleVmProfileDao _asProfileDao = mock(AutoScaleVmProfileDao.class); + when(_asProfileDao.findById(anyLong())).thenReturn(new AutoScaleVmProfileVO(1L, 1L, 1L, 1L, 1L, null, null, 30, 1L, null)); + autoScaleStatsCollector._asProfileDao = _asProfileDao; + } +} diff --git a/server/test/com/cloud/network/as/ElasticSearchAutoScaleStatsCollectorTest.java b/server/test/com/cloud/network/as/ElasticSearchAutoScaleStatsCollectorTest.java new file mode 100644 index 000000000000..41db33d79ca8 --- /dev/null +++ b/server/test/com/cloud/network/as/ElasticSearchAutoScaleStatsCollectorTest.java @@ -0,0 +1,97 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.as; + +import org.elasticsearch.action.ListenableActionFuture; +import org.elasticsearch.action.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ElasticSearchAutoScaleStatsCollectorTest extends AutoScaleStatsCollectorTest{ + + @Before + public void setUp() { + super.setUp(); + autoScaleStatsCollector = new ElasticSearchAutoScaleStatsCollector(); + } + + @Test + public void testReadVmStatsWithCpuCounter(){ + mockElasticSearchClient(0.10); + super.testReadVmStatsWithCpuCounter(); + } + + @Test + public void testReadVmStatsWithMemoryCounter(){ + mockElasticSearchClient(0.25); + super.testReadVmStatsWithMemoryCounter(); + } + + @Test + public void testReadVmStatsWithNullAverage(){ + mockElasticSearchClient(null); + mockAutoScaleVmGroupVmMapDao(); + mockAutoScaleGroupPolicyMapDao(); + mockAutoScalePolicyDao(); + mockAutoScalePolicyConditionMapDao(); + mockConditionDao(); + mockCounterDao("cpu"); + + Map countersSummary = autoScaleStatsCollector.retrieveMetrics(asGroup, vmList); + + assert countersSummary.get("cpu") == null; + } + + private void mockElasticSearchClient(Double average){ + TransportClient client = mock(TransportClient.class); + SearchRequestBuilder searchRequestBuilder = mock(SearchRequestBuilder.class); + ListenableActionFuture listenableActionFuture = mock(ListenableActionFuture.class); + SearchResponse searchResponse = mock(SearchResponse.class); + Aggregations aggregations = mock(Aggregations.class); + Map aggregationResponse = new HashMap<>(); + aggregationResponse.put("counter_average", new InternalAvg("counter_average", average != null ? average : Double.NaN, 1)); + + when(client.prepareSearch(anyString())).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.setTypes(anyString())).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.setFrom(anyInt())).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.setSize(anyInt())).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.setQuery(any(QueryBuilder.class))).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.addAggregation(any(AbstractAggregationBuilder.class))).thenReturn(searchRequestBuilder); + when(searchRequestBuilder.execute()).thenReturn(listenableActionFuture); + when(listenableActionFuture.actionGet()).thenReturn(searchResponse); + when(searchResponse.getAggregations()).thenReturn(aggregations); + when(aggregations.asMap()).thenReturn(aggregationResponse); + + ((ElasticSearchAutoScaleStatsCollector)autoScaleStatsCollector).elasticSearchClient = client; + } +} diff --git a/server/test/com/cloud/network/as/RRDAutoScaleStatsCollectorTest.java b/server/test/com/cloud/network/as/RRDAutoScaleStatsCollectorTest.java new file mode 100644 index 000000000000..97d70a339e00 --- /dev/null +++ b/server/test/com/cloud/network/as/RRDAutoScaleStatsCollectorTest.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.as; + +import org.junit.Before; + +public class RRDAutoScaleStatsCollectorTest extends AutoScaleStatsCollectorTest{ + + @Before + public void setUp() { + super.setUp(); + autoScaleStatsCollector = new RRDAutoScaleStatsCollector(); + } +} diff --git a/server/test/com/cloud/network/as/SNMPClientTest.java b/server/test/com/cloud/network/as/SNMPClientTest.java new file mode 100644 index 000000000000..5ce63099bd9f --- /dev/null +++ b/server/test/com/cloud/network/as/SNMPClientTest.java @@ -0,0 +1,172 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.as; + +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.Target; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.smi.Integer32; +import org.snmp4j.smi.IpAddress; +import org.snmp4j.smi.Null; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.VariableBinding; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.stub; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class SNMPClientTest { + + private SNMPClientImpl snmpClient; + @Mock + Snmp snmp; + @Mock + Logger logger; + + private static final String DISK_OID = "1.3.6.1.4.1.2021.9.1.7.1"; + private static final String USED_CPU_OID = "1.3.6.1.4.1.2021.11.9.0"; + private static final String USED_MEMORY_OID = "1.3.6.1.4.1.2021.11.10.1"; + + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + snmpClient = new SNMPClientImpl(snmp); + SNMPClientImpl.s_logger = logger; + } + + @Test + public void testReadUsedCPU() throws IOException { + mockSnmpGet(Arrays.asList(SNMPClientImpl.CPU_SYSTEM_OID, SNMPClientImpl.CPU_USER_OID), Arrays.asList("50", "10")); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("cpu_used"), Arrays.asList(USED_CPU_OID))); + + assertEquals(1, metrics.size()); + assertTrue(60.0 == metrics.get("cpu_used")); + verify(snmp).get(any(PDU.class), any(Target.class)); + } + + @Test + public void testReadUsedMemory() throws IOException { + mockSnmpGet(Arrays.asList(SNMPClientImpl.MEMORY_TOTAL_OID, SNMPClientImpl.MEMORY_FREE_OID), Arrays.asList("512", "256")); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("memory_used"), Arrays.asList(USED_MEMORY_OID))); + + assertEquals(1, metrics.size()); + assertTrue(50.0 == metrics.get("memory_used")); + verify(snmp).get(any(PDU.class), any(Target.class)); + } + + @Test + public void testReadCpuAndMemory() throws IOException { + mockSnmpGet(Arrays.asList(SNMPClientImpl.CPU_SYSTEM_OID, SNMPClientImpl.CPU_USER_OID, SNMPClientImpl.MEMORY_TOTAL_OID, SNMPClientImpl.MEMORY_FREE_OID), + Arrays.asList("50", "10", "256", "128")); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("cpu_used", "memory_used"), Arrays.asList(USED_CPU_OID, USED_MEMORY_OID))); + + assertEquals(2, metrics.size()); + assertTrue(60.0 == metrics.get("cpu_used")); + assertTrue(50.0 == metrics.get("memory_used")); + verify(snmp).get(any(PDU.class), any(Target.class)); + } + + @Test + public void testReadCpuMemoryAndDisk() throws IOException { + mockSnmpGet(Arrays.asList(SNMPClientImpl.CPU_SYSTEM_OID, SNMPClientImpl.CPU_USER_OID, SNMPClientImpl.MEMORY_TOTAL_OID, SNMPClientImpl.MEMORY_FREE_OID, DISK_OID), + Arrays.asList("50", "10", "256", "128", "1000")); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("cpu_used", "memory_used", "disk"), Arrays.asList(USED_CPU_OID, USED_MEMORY_OID, DISK_OID))); + + assertEquals(3, metrics.size()); + assertTrue(60.0 == metrics.get("cpu_used")); + assertTrue(50.0 == metrics.get("memory_used")); + assertTrue(1000 == metrics.get("disk")); + verify(snmp).get(any(PDU.class), any(Target.class)); + } + + @Test + public void testReadGivenSnmpTimeout() throws IOException { + stub(snmp.get(any(PDU.class), any(Target.class))).toReturn(null); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("memory_used"), Arrays.asList(USED_MEMORY_OID))); + + assertNull(metrics); + verify(logger).info("SNMP agent did not respond. Possible causes: vm SNMP agent not ready / request timed out"); + } + + @Test + public void testReadGivenErrorResponse() throws IOException { + PDU response = new PDU(); + response.setErrorStatus(3); + stub(snmp.get(any(PDU.class), any(Target.class))).toReturn(new ResponseEvent("", new IpAddress("172.168.10.10"), new PDU(), response, "")); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("memory_used"), Arrays.asList(USED_MEMORY_OID))); + + assertNull(metrics); + verify(logger).error("Error Status status/text: 3/Bad value"); + } + + @Test + public void testReadGivenIOException() throws IOException { + Exception ex = new IOException(); + stub(snmp.get(any(PDU.class), any(Target.class))).toThrow(ex); + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("memory_used"), Arrays.asList(USED_MEMORY_OID))); + + assertNull(metrics); + verify(logger).error("Error querying SNMP on: 172.168.10.10", ex); + } + + @Test + public void testReadGivenSNMPAgentNotReadyOnVm() throws IOException { + PDU response = new PDU(); + response.add(new VariableBinding(new OID(SNMPClientImpl.MEMORY_TOTAL_OID), new Null(129))); + response.add(new VariableBinding(new OID(SNMPClientImpl.MEMORY_TOTAL_OID), new Null(129))); + stub(snmp.get(any(PDU.class), any(Target.class))).toReturn(new ResponseEvent("", new IpAddress("172.168.10.10"), new PDU(), response, "")); + + Map metrics = snmpClient.read("172.168.10.10", createRequestCounters(Arrays.asList("memory_used"), Arrays.asList(USED_MEMORY_OID))); + + assertNull(metrics); + verify(logger).info(eq("The SNMP agent was not ready on the VM 172.168.10.10. Error: Result was 1.3.6.1.4.1.2021.4.5.0 = noSuchInstance")); + } + + private Map createRequestCounters(List counterNames, List values) { + Map counters = new HashMap<>(); + for (int i = 0; i < counterNames.size(); i++) { + counters.put(counterNames.get(i), values.get(i)); + } + return counters; + } + + private void mockSnmpGet(List oids, List values) throws IOException { + PDU response = new PDU(); + for (int i = 0; i < oids.size(); i++) { + response.add(new VariableBinding(new OID(oids.get(i)), new Integer32(Integer.parseInt(values.get(i))))); + } + when(snmp.get(any(PDU.class), any(Target.class))).thenReturn(new ResponseEvent("", new IpAddress("172.168.10.10"), new PDU(), response, "")); + } +} diff --git a/server/test/com/cloud/network/lb/AssignLoadBalancerTest.java b/server/test/com/cloud/network/lb/AssignLoadBalancerTest.java index 5df40fff2d04..36f9f14a6c88 100644 --- a/server/test/com/cloud/network/lb/AssignLoadBalancerTest.java +++ b/server/test/com/cloud/network/lb/AssignLoadBalancerTest.java @@ -24,6 +24,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.NetworkModelImpl; import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerNetworkMapDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.dao.LoadBalancerVO; @@ -171,11 +172,13 @@ public void testNicIsNotInNw() throws ResourceAllocationException, ResourceUnava LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); + LoadBalancerNetworkMapDao lbNetMapDao = Mockito.mock(LoadBalancerNetworkMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._lbNetMapDao = lbNetMapDao; _lbvmMapList = new ArrayList<>(); _lbMgr._rulesMgr = _rulesMgr; _lbMgr._networkModel = _networkModel; @@ -205,12 +208,14 @@ public void tesSecIpNotSetToVm() throws ResourceAllocationException, ResourceUna LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); + LoadBalancerNetworkMapDao lbNetMapDao = Mockito.mock(LoadBalancerNetworkMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); NicSecondaryIpDao nicSecIpDao = Mockito.mock(NicSecondaryIpDao.class); _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._lbNetMapDao = lbNetMapDao; _lbMgr._nicSecondaryIpDao = nicSecIpDao; _lbvmMapList = new ArrayList<>(); _lbMgr._rulesMgr = _rulesMgr; @@ -243,6 +248,7 @@ public void testVmIdAlreadyExist() throws ResourceAllocationException, ResourceU LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); + LoadBalancerNetworkMapDao lbNetMapDao = Mockito.mock(LoadBalancerNetworkMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); NicSecondaryIpDao nicSecIpDao = Mockito.mock(NicSecondaryIpDao.class); LoadBalancerVMMapVO lbVmMapVO = new LoadBalancerVMMapVO(1L, 1L, "10.1.1.175", false); @@ -250,6 +256,7 @@ public void testVmIdAlreadyExist() throws ResourceAllocationException, ResourceU _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._lbNetMapDao = lbNetMapDao; _lbMgr._nicSecondaryIpDao = nicSecIpDao; _lbvmMapList = new ArrayList<>(); _lbvmMapList.add(lbVmMapVO); diff --git a/server/test/com/cloud/network/lb/UpdateLoadBalancerTest.java b/server/test/com/cloud/network/lb/UpdateLoadBalancerTest.java index 0b67eb72367a..93f8a53ed488 100644 --- a/server/test/com/cloud/network/lb/UpdateLoadBalancerTest.java +++ b/server/test/com/cloud/network/lb/UpdateLoadBalancerTest.java @@ -24,9 +24,15 @@ import java.util.ArrayList; import java.util.UUID; +import com.cloud.network.dao.LoadBalancerNetworkMapDao; +import com.cloud.network.dao.LoadBalancerOptionsDao; +import com.cloud.network.dao.LoadBalancerPortMapDao; import com.cloud.user.User; import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerRuleCmd; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.globoconfig.GloboResourceConfigurationDao; +import org.apache.cloudstack.globoconfig.GloboResourceKey; +import org.apache.cloudstack.globoconfig.GloboResourceType; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -77,7 +83,11 @@ public void setUp() { _lbMgr._lb2healthcheckDao = Mockito.mock(LBHealthCheckPolicyDao.class); _lbMgr._lb2stickinesspoliciesDao = Mockito.mock(LBStickinessPolicyDao.class); _lbMgr._lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); + _lbMgr._lbNetMapDao = Mockito.mock(LoadBalancerNetworkMapDao.class); + _lbMgr._lbPortMapDao = Mockito.mock(LoadBalancerPortMapDao.class); + _lbMgr._lbOptionsDao = Mockito.mock(LoadBalancerOptionsDao.class); _lbMgr._lbCertMapDao = Mockito.mock(LoadBalancerCertMapDao.class); + _lbMgr._globoResourceConfigurationDao = Mockito.mock(GloboResourceConfigurationDao.class); _lbMgr._lbDao = lbDao; _lbMgr._lbProviders = new ArrayList(); _lbMgr._lbProviders.add(lbServiceProvider); @@ -102,7 +112,8 @@ public void testValidateRuleBeforeUpdateLB() throws ResourceAllocationException, _lbMgr.updateLoadBalancerRule(updateLbRuleCmd); - InOrder inOrder = Mockito.inOrder(lbServiceProvider, lbDao); + InOrder inOrder = Mockito.inOrder(_lbMgr._globoResourceConfigurationDao, lbServiceProvider, lbDao); + inOrder.verify(_lbMgr._globoResourceConfigurationDao).getFirst(GloboResourceType.LOAD_BALANCER, lb.getUuid(), GloboResourceKey.dsr); inOrder.verify(lbServiceProvider).validateLBRule(any(Network.class), any(LoadBalancingRule.class)); inOrder.verify(lbDao).update(anyLong(), eq(lb)); } diff --git a/server/test/com/cloud/projects/MockProjectManagerImpl.java b/server/test/com/cloud/projects/MockProjectManagerImpl.java index 46946aac71b7..36cd7cabfaad 100644 --- a/server/test/com/cloud/projects/MockProjectManagerImpl.java +++ b/server/test/com/cloud/projects/MockProjectManagerImpl.java @@ -31,7 +31,7 @@ public class MockProjectManagerImpl extends ManagerBase implements ProjectManager { @Override - public Project createProject(String name, String displayText, String accountName, Long domainId) throws ResourceAllocationException { + public Project createProject(String name, String displayText, String accountName, Long domainId, String businessServiceId, String clientId, String componentId, String subComponentId, String productId, Boolean detailedUsage) throws ResourceAllocationException { // TODO Auto-generated method stub return null; } @@ -79,7 +79,7 @@ public Project findByNameAndDomainId(String name, long domainId) { } @Override - public Project updateProject(long id, String displayText, String newOwnerName) throws ResourceAllocationException { + public Project updateProject(long id, String displayText, String newOwnerName, String businessServiceId, String clientId, String componentId, String subComponentId, String productId, Boolean detailedUsage) throws ResourceAllocationException { // TODO Auto-generated method stub return null; } diff --git a/server/test/com/cloud/server/as/AutoScaleCounterCollectorTest.java b/server/test/com/cloud/server/as/AutoScaleCounterCollectorTest.java new file mode 100644 index 000000000000..e8ff846bcd2e --- /dev/null +++ b/server/test/com/cloud/server/as/AutoScaleCounterCollectorTest.java @@ -0,0 +1,282 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.server.as; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.as.AutoScalePolicy; +import com.cloud.network.as.AutoScalePolicyConditionMapVO; +import com.cloud.network.as.AutoScalePolicyVO; +import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.Condition; +import com.cloud.network.as.ConditionVO; +import com.cloud.network.as.Counter; +import com.cloud.network.as.CounterVO; +import com.cloud.network.as.AutoScaleCounterProcessor; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.vm.NicVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.stub; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class AutoScaleCounterCollectorTest { + + AutoScaleCounterCollector autoScaleCounterCollector; + + @Mock + ConfigurationDao configurationDao; + @Mock + AutoScaleVmGroupDao autoScaleVmGroupDao; + @Mock + NicDao nicDao; + @Mock + AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao; + @Mock + VMInstanceDao vmInstanceDao; + @Mock + AutoScaleVmGroupPolicyMapDao asPolicyMapDao; + @Mock + AutoScalePolicyDao asPolicyDao; + @Mock + AutoScalePolicyConditionMapDao asPolicyConditionMapDao; + @Mock + ConditionDao conditionDao; + @Mock + CounterDao counterDao; + @Mock + AutoScaleCounterProcessor counterProcessor; + @Mock + Logger logger; + @Mock + ConfigKey ManagementServerId; + + @Before + public void setUp(){ + MockitoAnnotations.initMocks(this); + autoScaleCounterCollector = new AutoScaleCounterCollector(); + autoScaleCounterCollector.configurationDao = configurationDao; + autoScaleCounterCollector.autoScaleVmGroupDao = autoScaleVmGroupDao; + autoScaleCounterCollector.autoScaleVmMapDao = autoScaleVmGroupVmMapDao; + autoScaleCounterCollector.vmInstanceDao = vmInstanceDao; + autoScaleCounterCollector.nicDao = nicDao; + autoScaleCounterCollector.asPolicyMapDao = asPolicyMapDao; + autoScaleCounterCollector.asPolicyDao = asPolicyDao; + autoScaleCounterCollector.asPolicyConditionMapDao = asPolicyConditionMapDao; + autoScaleCounterCollector.conditionDao = conditionDao; + autoScaleCounterCollector.counterDao = counterDao; + autoScaleCounterCollector.counterProcessor = counterProcessor; + autoScaleCounterCollector.s_logger = logger; + autoScaleCounterCollector.managementServer = ManagementServerId; + when(ManagementServerId.value()).thenReturn(autoScaleCounterCollector.currentManagementServerId); + } + + @Test + public void testRunInContext(){ + mockConfigurationDaoDataSource("elasticsearch"); + mockListEnabledAutoScaleGroups(); + mockFindCounters(Arrays.asList("cpu"), Arrays.asList("1.1.1.1.1")); + + when(autoScaleVmGroupVmMapDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1L, 1L))); + when(vmInstanceDao.findById(1L)).thenReturn(createVM(20L)); + mockFindNIC(20L, "172.168.10.5", false); + + autoScaleCounterCollector.runInContext(); + + Map counters = new HashMap<>(); + counters.put("cpu", "1.1.1.1.1"); + verify(counterProcessor, times(1)).process(any(AutoScaleVmGroupVO.class), eq(Arrays.asList(new VirtualMachineAddress("172.168.10.5", "vm-20"))), eq(counters)); + } + + @Test + public void testRunInContextGivenEmptyAutoScaleGroup(){ + mockConfigurationDaoDataSource("elasticsearch"); + mockListEnabledAutoScaleGroups(); + + when(autoScaleVmGroupVmMapDao.listByGroup(anyLong())).thenReturn(null); + when(vmInstanceDao.findById(1L)).thenReturn(null); + + autoScaleCounterCollector.runInContext(); + + verifyZeroInteractions(counterProcessor); + } + + @Test + public void testRunInContextGivenErrorOnSnmpReader(){ + mockConfigurationDaoDataSource("elasticsearch"); + mockListEnabledAutoScaleGroups(); + + when(autoScaleVmGroupVmMapDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1L, 20L))); + when(vmInstanceDao.findById(20L)).thenReturn(createVM(20L)); + mockFindNIC(20L, "172.168.10.5", false); + stub(counterProcessor.process(any(AutoScaleVmGroupVO.class), anyList(), anyMap())).toThrow(new RuntimeException()); + + autoScaleCounterCollector.runInContext(); + + verify(logger).error(anyString(), any(Exception.class)); + } + + @Test + public void testRunInContextGivenElasticSearchNotEnabled(){ + mockConfigurationDaoDataSource("rrd"); + autoScaleCounterCollector.runInContext(); + verify(logger).debug("[AutoScale] Elasticsearch stats datasource not enabled or management server not configured"); + } + + @Test + public void testGetOneCounter(){ + mockFindCounters(Arrays.asList("cpu"), Arrays.asList("1.1.1.1.1")); + Map counters = autoScaleCounterCollector.getCountersFrom(createAutoScaleGroup()); + + assertEquals(1, counters.keySet().size()); + assertEquals("1.1.1.1.1", counters.get("cpu")); + } + + @Test + public void testGetMoreThanOneCounter(){ + mockFindCounters(Arrays.asList("cpu", "memory"), Arrays.asList("1.1.1.1.1", "1.1.1.1.2")); + Map counters = autoScaleCounterCollector.getCountersFrom(createAutoScaleGroup()); + + assertEquals(2, counters.keySet().size()); + assertEquals("1.1.1.1.1", counters.get("cpu")); + assertEquals("1.1.1.1.2", counters.get("memory")); + } + + @Test + public void testGetCountersWithRepeatedCounters(){ + mockFindCounters(Arrays.asList("cpu", "cpu"), Arrays.asList("1.1.1.1.1", "1.1.1.1.1")); + Map counters = autoScaleCounterCollector.getCountersFrom(createAutoScaleGroup()); + + assertEquals(1, counters.keySet().size()); + assertEquals("1.1.1.1.1", counters.get("cpu")); + } + + @Test + public void testGetIpAddressGivenVmWithIpv4(){ + mockFindNIC(1L, "172.168.10.5", false); + String ipAddress = autoScaleCounterCollector.getIpAddressesFrom(createVM(1L)); + + assertEquals("172.168.10.5", ipAddress); + } + + @Test + public void testGetIpAddressGivenVmWithIpv6(){ + mockFindNIC(1L, "FE80:0000:0000:0000:0202:B3FF:FE1E:8329", true); + String ipAddress = autoScaleCounterCollector.getIpAddressesFrom(createVM(1L)); + + assertEquals("FE80:0000:0000:0000:0202:B3FF:FE1E:8329", ipAddress); + } + + @Test + public void testGetIpAddressGivenVmWithMoreThanOneNic(){ + VMInstanceVO vm = createVM(1L); + NicVO defaultNic = createNIC("172.168.10.5", false); + defaultNic.setDefaultNic(true); + NicVO nonDefaultNic = createNIC("172.168.10.6", false); + nonDefaultNic.setDefaultNic(false); + + when(nicDao.listByVmId(vm.getId())).thenReturn(Arrays.asList(defaultNic, nonDefaultNic)); + + String ipAddressesFrom = autoScaleCounterCollector.getIpAddressesFrom(vm); + assertEquals("IP should be from the non default NIC if VM has more than one NIC", nonDefaultNic.getIPv4Address(), ipAddressesFrom); + } + + private void mockConfigurationDaoDataSource(String datasource) { + when(configurationDao.findByName("autoscale.stats.datasource")).thenReturn(new ConfigurationVO("", "", "", "", datasource, "")); + } + + private void mockListEnabledAutoScaleGroups() { + when(autoScaleVmGroupDao.listAllEnabled()).thenReturn(Arrays.asList(createAutoScaleGroup())); + } + + private void mockFindNIC(Long vmId, String address, boolean ipv6) { + when(nicDao.listByVmId(vmId)).thenReturn(Arrays.asList(createNIC(address, ipv6))); + } + + private void mockFindCounters(List counterNames, List counterValues) { + when(asPolicyMapDao.listByVmGroupId(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupPolicyMapVO(1L, 1L))); + when(asPolicyDao.findById(1L)).thenReturn(createAutoScalePolicy()); + + List policyConditionMap = new ArrayList<>(); + for (int i = 0; i < counterNames.size(); i++) { + policyConditionMap.add(new AutoScalePolicyConditionMapVO(1L, new Long(i+1))); + } + when(asPolicyConditionMapDao.findByPolicyId(anyLong())).thenReturn(policyConditionMap); + + for (int i = 0; i < counterNames.size(); i++) { + when(conditionDao.findById(new Long(i+1))).thenReturn(new ConditionVO((i + 1), 50, 1, 1, Condition.Operator.GT)); + when(counterDao.findById(new Long(i+1))).thenReturn(new CounterVO(Counter.Source.valueOf(counterNames.get(i)),counterNames.get(i), counterValues.get(i))); + } + } + + private NicVO createNIC(String address, boolean isIpv6) { + NicVO nic = new NicVO("", 1L, 1, VirtualMachine.Type.Instance); + if(isIpv6){ + nic.setIPv6Address(address); + } else { + nic.setIPv4Address(address); + } + return nic; + } + + private VMInstanceVO createVM(long id){ + return new VMInstanceVO(id, 1, "vm-" + id, "vm-" + id, VirtualMachine.Type.Instance, 1L, Hypervisor.HypervisorType.Simulator, 1, 1, 1, 1, true, true, 1l); + } + + private AutoScaleVmGroupVO createAutoScaleGroup() { + return new AutoScaleVmGroupVO(1L,1l, 1L, 1L, 1, 3, 80, 30, new Date(), 1, "enabled", "as-group"); + } + + private AutoScalePolicyVO createAutoScalePolicy() { + return new AutoScalePolicyVO(1, 1, 1, 120, new Date(), "scaleup", 1, AutoScalePolicy.LogicalOperator.AND); + } +} diff --git a/server/test/com/cloud/server/as/AutoScaleMonitorTest.java b/server/test/com/cloud/server/as/AutoScaleMonitorTest.java new file mode 100644 index 000000000000..d16ea67108bf --- /dev/null +++ b/server/test/com/cloud/server/as/AutoScaleMonitorTest.java @@ -0,0 +1,511 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.server.as; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.as.AutoScaleManager; +import com.cloud.network.as.AutoScalePolicy; +import com.cloud.network.as.AutoScalePolicyConditionMapVO; +import com.cloud.network.as.AutoScalePolicyVO; +import com.cloud.network.as.AutoScaleStatsCollector; +import com.cloud.network.as.AutoScaleStatsCollectorFactory; +import com.cloud.network.as.AutoScaleVmGroup; +import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.Condition; +import com.cloud.network.as.ConditionVO; +import com.cloud.network.as.Counter; +import com.cloud.network.as.CounterVO; +import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; +import com.cloud.network.as.dao.AutoScalePolicyDao; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +public class AutoScaleMonitorTest { + + private AutoScaleMonitor autoScaleMonitor; + protected List asGroups = new ArrayList<>(); + protected List vmList = new ArrayList<>(); + + @Before + public void setUp(){ + autoScaleMonitor = new AutoScaleMonitor(); + autoScaleMonitor._asCounterDao = mock(CounterDao.class); + AutoScaleVmGroupVO asGroup = new AutoScaleVmGroupVO(1L,1l, 1L, 1L, 1, 3, 80, 30, new Date(), 1, "enabled", "as-group"); + asGroups.add(asGroup); + VMInstanceVO vm = new VMInstanceVO(1, 1, "vm-01", "vm-01", VirtualMachine.Type.Instance, 1L, Hypervisor.HypervisorType.Simulator, 1, 1, 1, 1, false, true, 1L); + vmList.add(vm); + } + + @Test + public void testScaleUp(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + mockIsNative("cpu", new ConditionVO(1L, 90L, 1L, 1L, Condition.Operator.GT)); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroup.setMinMembers(1); + asGroup.setMaxMembers(2); + asGroup.setLastInterval(new Date(1)); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + // Mock auto scale group VMs + when(autoScaleMonitor._asGroupVmDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1, 1))); + + autoScaleMonitor._vmInstance = mock(VMInstanceDao.class); + when(autoScaleMonitor._vmInstance.findById(1L)).thenReturn(vmList.get(0)); + + // Mock Stats collector return + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 100.0); + AutoScaleStatsCollector collector = mock(AutoScaleStatsCollector.class); + when(collector.retrieveMetrics(any(AutoScaleVmGroup.class), anyList())).thenReturn(avgSummary); + + autoScaleMonitor.autoScaleStatsCollectorFactory = mock(AutoScaleStatsCollectorFactory.class); + when(autoScaleMonitor.autoScaleStatsCollectorFactory.getStatsCollector()).thenReturn(collector); + + //Mock scale up policy + autoScaleMonitor._asPolicyDao = mock(AutoScalePolicyDao.class); + when(autoScaleMonitor._asPolicyDao.findById(anyLong())).thenReturn(new AutoScalePolicyVO(1, 1, 60, 120, new Date(1), "scaleup", 1, AutoScalePolicy.LogicalOperator.AND)); + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verify(autoScaleMonitor._asManager).doScaleUp(asGroup.getId(), 1); + verify(autoScaleMonitor._asGroupDao, times(3)).persist(any(AutoScaleVmGroupVO.class)); + } + + @Test + public void testScaleDown(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + mockIsNative("cpu", new ConditionVO(1L, 5L, 1L, 1L, Condition.Operator.LT)); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroup.setMinMembers(1); + asGroup.setMaxMembers(2); + asGroup.setLastInterval(new Date(1)); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + // Mock auto scale group VMs + when(autoScaleMonitor._asGroupVmDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1, 1))); + + autoScaleMonitor._vmInstance = mock(VMInstanceDao.class); + when(autoScaleMonitor._vmInstance.findById(1L)).thenReturn(vmList.get(0)); + + // Mock Stats collector return + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 0.0); + AutoScaleStatsCollector collector = mock(AutoScaleStatsCollector.class); + when(collector.retrieveMetrics(any(AutoScaleVmGroup.class), anyList())).thenReturn(avgSummary); + + autoScaleMonitor.autoScaleStatsCollectorFactory = mock(AutoScaleStatsCollectorFactory.class); + when(autoScaleMonitor.autoScaleStatsCollectorFactory.getStatsCollector()).thenReturn(collector); + + //Mock scale down policy + autoScaleMonitor._asPolicyDao = mock(AutoScalePolicyDao.class); + when(autoScaleMonitor._asPolicyDao.findById(anyLong())).thenReturn(new AutoScalePolicyVO(1, 1, 60, 120, new Date(1), "scaledown", 1, AutoScalePolicy.LogicalOperator.AND)); + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verify(autoScaleMonitor._asManager).doScaleDown(asGroup.getId(), 1); + verify(autoScaleMonitor._asGroupDao, times(3)).persist(any(AutoScaleVmGroupVO.class)); + } + + @Test + public void testScaleDownWithQuietTimeNotYetReached(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + mockIsNative("cpu", new ConditionVO(1L, 5L, 1L, 1L, Condition.Operator.LT)); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroup.setMinMembers(1); + asGroup.setMaxMembers(2); + asGroup.setLastInterval(new Date(1)); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + // Mock auto scale group VMs + when(autoScaleMonitor._asGroupVmDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1, 1))); + + autoScaleMonitor._vmInstance = mock(VMInstanceDao.class); + when(autoScaleMonitor._vmInstance.findById(1L)).thenReturn(vmList.get(0)); + + + // Mock Stats collector return + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 0.0); + AutoScaleStatsCollector collector = mock(AutoScaleStatsCollector.class); + when(collector.retrieveMetrics(any(AutoScaleVmGroup.class), anyList())).thenReturn(avgSummary); + + autoScaleMonitor.autoScaleStatsCollectorFactory = mock(AutoScaleStatsCollectorFactory.class); + when(autoScaleMonitor.autoScaleStatsCollectorFactory.getStatsCollector()).thenReturn(collector); + + //Mock scale down policy with NOW as last quiet time + Date now = new Date(); + autoScaleMonitor._asPolicyDao = mock(AutoScalePolicyDao.class); + when(autoScaleMonitor._asPolicyDao.findById(anyLong())).thenReturn(new AutoScalePolicyVO(1, 1, 60, 120, now, "scaledown", 1, AutoScalePolicy.LogicalOperator.AND)); + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verifyZeroInteractions(autoScaleMonitor._asManager); + verify(autoScaleMonitor._asGroupDao, times(3)).persist(any(AutoScaleVmGroupVO.class)); + } + + @Test + public void testCpuReturnedAsNull(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + mockIsNative("cpu"); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroup.setMinMembers(1); + asGroup.setMaxMembers(2); + asGroup.setLastInterval(new Date(1)); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + // Mock auto scale group VMs + when(autoScaleMonitor._asGroupVmDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1, 1))); + + autoScaleMonitor._vmInstance = mock(VMInstanceDao.class); + when(autoScaleMonitor._vmInstance.findById(1L)).thenReturn(vmList.get(0)); + + // Mock Stats collector return - CPU metric set to NULL + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", null); + AutoScaleStatsCollector collector = mock(AutoScaleStatsCollector.class); + when(collector.retrieveMetrics(any(AutoScaleVmGroup.class), anyList())).thenReturn(avgSummary); + + autoScaleMonitor.autoScaleStatsCollectorFactory = mock(AutoScaleStatsCollectorFactory.class); + when(autoScaleMonitor.autoScaleStatsCollectorFactory.getStatsCollector()).thenReturn(collector); + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verifyZeroInteractions(autoScaleMonitor._asManager); + verify(autoScaleMonitor._asGroupDao, times(3)).persist(any(AutoScaleVmGroupVO.class)); + } + + @Test + public void testNoMetricsReturned(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + mockIsNative("cpu"); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroup.setMinMembers(1); + asGroup.setMaxMembers(2); + asGroup.setLastInterval(new Date(1)); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + // Mock auto scale group VMs + when(autoScaleMonitor._asGroupVmDao.listByGroup(anyLong())).thenReturn(Arrays.asList(new AutoScaleVmGroupVmMapVO(1, 1))); + + autoScaleMonitor._vmInstance = mock(VMInstanceDao.class); + when(autoScaleMonitor._vmInstance.findById(1L)).thenReturn(vmList.get(0)); + + // Mock Stats collector return - No metrics returned + Map avgSummary = new HashMap<>(); + AutoScaleStatsCollector collector = mock(AutoScaleStatsCollector.class); + when(collector.retrieveMetrics(any(AutoScaleVmGroup.class), anyList())).thenReturn(avgSummary); + + autoScaleMonitor.autoScaleStatsCollectorFactory = mock(AutoScaleStatsCollectorFactory.class); + when(autoScaleMonitor.autoScaleStatsCollectorFactory.getStatsCollector()).thenReturn(collector); + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verifyZeroInteractions(autoScaleMonitor._asManager); + verify(autoScaleMonitor._asGroupDao, times(3)).persist(any(AutoScaleVmGroupVO.class)); + } + + @Test + public void testMinMembersNotMet(){ + mockIsNative("cpu"); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(1); + asGroups.get(0).setMinMembers(2); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + autoScaleMonitor.processAutoScaleGroup(asGroups.get(0)); + + verify(asManager).doScaleUp(asGroups.get(0).getId(), 1); + } + + @Test + public void testMaxMembersExceeded(){ + mockIsNative("cpu"); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(3); + asGroups.get(0).setMaxMembers(1); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + autoScaleMonitor.processAutoScaleGroup(asGroups.get(0)); + + verify(asManager).doScaleDown(asGroups.get(0).getId(), 2); + } + + @Test + public void testWithoutNativeCounter(){ + mockIsNative("snmp"); + mockAutoScaleGroupDao(); + mockAutoScaleVmGroupVmMapDaoCountBy(3); + asGroups.get(0).setMaxMembers(1); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + autoScaleMonitor.processAutoScaleGroup(asGroups.get(0)); + + verifyZeroInteractions(asManager); + } + + @Test + public void testAutoScaleGroupLocked(){ + AutoScaleVmGroupVO asGroup = asGroups.get(0); + asGroup.setLocked(true); + mockAutoScaleGroupDao(); + + AutoScaleManager asManager = mock(AutoScaleManager.class); + autoScaleMonitor._asManager = asManager; + + autoScaleMonitor.processAutoScaleGroup(asGroup); + + verifyZeroInteractions(asManager); + } + + @Test + public void testRunInContext(){ + asGroups = Arrays.asList(new AutoScaleVmGroupVO[]{ asGroups.get(0), asGroups.get(0), asGroups.get(0) }); + AutoScaleVmGroupDao _asGroupVmDao = mock(AutoScaleVmGroupDao.class); + when(_asGroupVmDao.listAllNotLocked()).thenReturn(asGroups); + autoScaleMonitor._asGroupDao = _asGroupVmDao; + + autoScaleMonitor.threadExecutor = mock(ExecutorService.class); + + autoScaleMonitor.runInContext(); + verify(autoScaleMonitor.threadExecutor, times(3)).execute(any(Runnable.class)); + verify(autoScaleMonitor._asGroupDao, times(1)).listAllNotLocked(); + } + + @Test + public void testIsPolicyValidGivenPolicyHasOneTrueCondition(){ + ConditionVO condition = new ConditionVO(1L, 30L, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 50.0); + assertTrue(autoScaleMonitor.isPolicyValid(avgSummary, Collections.singletonList(condition), AutoScalePolicy.LogicalOperator.AND)); + } + + @Test + public void testIsPolicyValidGivenPolicyHasOneFalseCondition(){ + ConditionVO condition = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 1.0); + assertFalse(autoScaleMonitor.isPolicyValid(avgSummary, Collections.singletonList(condition), AutoScalePolicy.LogicalOperator.AND)); + } + + @Test + public void testIsPolicyValidGivenPolicyHasTwoTrueConditionsAndLogicalOperatorAND(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 100.0); + avgSummary.put("memory", 100.0); + assertTrue(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.AND)); + } + @Test + public void testIsPolicyValidGivenPolicyHasTwoFalseConditionsAndLogicalOperatorAND(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 1.0); + avgSummary.put("memory", 1.0); + assertFalse(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.AND)); + } + + @Test + public void testIsPolicyValidGivenPolicyHasTrueAndFalseConditionsAndLogicalOperatorAND(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 1.0); + avgSummary.put("memory", 100.0); + assertFalse(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.AND)); + } + + @Test + public void testIsPolicyValidGivenPolicyHasTwoTrueConditionsAndLogicalOperatorOR(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 100.0); + avgSummary.put("memory", 100.0); + assertTrue(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.OR)); + } + @Test + public void testIsPolicyValidGivenPolicyHasTwoFalseConditionsAndLogicalOperatorOR(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 1.0); + avgSummary.put("memory", 1.0); + assertFalse(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.OR)); + } + + @Test + public void testIsPolicyValidGivenPolicyHasTrueAndFalseConditionsAndLogicalOperatorOR(){ + ConditionVO condition1 = new ConditionVO(1L, 50, 1L, 1L, Condition.Operator.GT); + ConditionVO condition2 = new ConditionVO(2L, 50, 1L, 1L, Condition.Operator.GT); + when(autoScaleMonitor._asCounterDao.findById(1L)).thenReturn(new CounterVO(Counter.Source.cpu, "cpu", "")); + when(autoScaleMonitor._asCounterDao.findById(2L)).thenReturn(new CounterVO(Counter.Source.memory, "memory", "")); + + Map avgSummary = new HashMap<>(); + avgSummary.put("cpu", 1.0); + avgSummary.put("memory", 100.0); + assertTrue(autoScaleMonitor.isPolicyValid(avgSummary, Arrays.asList(condition1, condition2), AutoScalePolicy.LogicalOperator.OR)); + } + + @Test + public void testIsConditionValidGreaterThanOperator(){ + assertTrue(autoScaleMonitor.isConditionValid(50, 60.0, Condition.Operator.GT)); + assertFalse(autoScaleMonitor.isConditionValid(50, 50.0, Condition.Operator.GT)); + assertFalse(autoScaleMonitor.isConditionValid(50, 49.0, Condition.Operator.GT)); + } + + @Test + public void testIsConditionValidGreaterThanEqualsOperator(){ + assertTrue(autoScaleMonitor.isConditionValid(50, 60.0, Condition.Operator.GE)); + assertTrue(autoScaleMonitor.isConditionValid(50, 50.0, Condition.Operator.GE)); + assertFalse(autoScaleMonitor.isConditionValid(50, 49.0, Condition.Operator.GE)); + } + + @Test + public void testIsConditionValidEqualsOperator(){ + assertFalse(autoScaleMonitor.isConditionValid(50, 60.0, Condition.Operator.EQ)); + assertTrue(autoScaleMonitor.isConditionValid(50, 50.0, Condition.Operator.EQ)); + assertFalse(autoScaleMonitor.isConditionValid(50, 49.0, Condition.Operator.EQ)); + } + + @Test + public void testIsConditionValidLesserThanOperator(){ + assertFalse(autoScaleMonitor.isConditionValid(50, 60.0, Condition.Operator.LT)); + assertFalse(autoScaleMonitor.isConditionValid(50, 50.0, Condition.Operator.LT)); + assertTrue(autoScaleMonitor.isConditionValid(50, 49.0, Condition.Operator.LT)); + } + + @Test + public void testIsConditionValidLesserThanEqualsOperator(){ + assertFalse(autoScaleMonitor.isConditionValid(50, 60.0, Condition.Operator.LE)); + assertTrue(autoScaleMonitor.isConditionValid(50, 50.0, Condition.Operator.LE)); + assertTrue(autoScaleMonitor.isConditionValid(50, 49.0, Condition.Operator.LE)); + } + + protected void mockAutoScaleGroupDao(){ + AutoScaleVmGroupDao _asGroupVmDao = mock(AutoScaleVmGroupDao.class); + when(_asGroupVmDao.listAllNotLocked()).thenReturn(asGroups); + when(_asGroupVmDao.findById(anyLong())).thenReturn(asGroups.get(0)); + autoScaleMonitor._asGroupDao = _asGroupVmDao; + } + + protected void mockAutoScaleVmGroupVmMapDaoCountBy(Integer count){ + AutoScaleVmGroupVmMapDao _asGroupVmDao = mock(AutoScaleVmGroupVmMapDao.class); + when(_asGroupVmDao.countByGroup(anyInt())).thenReturn(count); + autoScaleMonitor._asGroupVmDao = _asGroupVmDao; + } + + protected void mockIsNative(String counterName, ConditionVO condition){ + CounterDao _asCounterDao = mock(CounterDao.class); + CounterVO counter = new CounterVO(Counter.Source.valueOf(counterName), counterName, ""); + when(_asCounterDao.findById(1L)).thenReturn(counter); + autoScaleMonitor._asCounterDao = _asCounterDao; + + ConditionDao _asConditionDao = mock(ConditionDao.class); + + when(_asConditionDao.findById(anyLong())).thenReturn(condition); + autoScaleMonitor._asConditionDao = _asConditionDao; + + AutoScaleVmGroupPolicyMapDao _asGroupPolicyDao = mock(AutoScaleVmGroupPolicyMapDao.class); + List groupPolicymap = new ArrayList<>(); + groupPolicymap.add(new AutoScaleVmGroupPolicyMapVO(1L, 1L)); + when(_asGroupPolicyDao.listByVmGroupId(anyLong())).thenReturn(groupPolicymap); + autoScaleMonitor._asGroupPolicyDao = _asGroupPolicyDao; + + AutoScalePolicyConditionMapDao _asConditionMapDao = mock(AutoScalePolicyConditionMapDao.class); + List policiesMap = new ArrayList<>(); + policiesMap.add(new AutoScalePolicyConditionMapVO(1L, 1L)); + when(_asConditionMapDao.findByPolicyId(anyLong())).thenReturn(policiesMap); + autoScaleMonitor._asConditionMapDao = _asConditionMapDao; + } + + protected void mockIsNative(String counterName){ + mockIsNative(counterName, new ConditionVO(1L, 5L, 1L, 1L, Condition.Operator.LT)); + } +} \ No newline at end of file diff --git a/server/test/com/cloud/tags/TagKeysBuilderTest.java b/server/test/com/cloud/tags/TagKeysBuilderTest.java new file mode 100644 index 000000000000..1b79c5102aaf --- /dev/null +++ b/server/test/com/cloud/tags/TagKeysBuilderTest.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.tags; + +import com.cloud.server.ResourceTag; +import java.util.ArrayList; +import java.util.List; +import junit.framework.TestCase; + +public class TagKeysBuilderTest extends TestCase { + + + public void testGetKeyMetadata() throws Exception { + TaggedResourceManagerImpl manager = new TaggedResourceManagerImpl(); + + String keyMetadata = TagKeysBuilder.getKeyMetadata("MYKEY"); + assertEquals("TAG_MYKEY" , keyMetadata); + } + + public void testTagKeysBuilder() { + TagKeysBuilder tagKeysBuilder = new TagKeysBuilder(); + + tagKeysBuilder.add("MY_TAG"); + tagKeysBuilder.add("TAG_1"); + tagKeysBuilder.add("TAG_2"); + + assertEquals("MY_TAG,TAG_1,TAG_2", tagKeysBuilder.value()); + + + tagKeysBuilder = new TagKeysBuilder(); + + tagKeysBuilder.add(" MY_TAG "); + tagKeysBuilder.add(" TAG_1"); + tagKeysBuilder.add("TAG_2"); + + assertEquals("MY_TAG,TAG_1,TAG_2", tagKeysBuilder.value()); + } + + public void testBuildTagKeys() { + + List tags = new ArrayList(); + tags.add(new ResourceTagVO("MY_TAG", "321", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + tags.add(new ResourceTagVO("MY_TAG_1", "---", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + tags.add(new ResourceTagVO("MY_TAG_2", "444", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + + String keys = TagKeysBuilder.buildTagKeys(tags); + + assertEquals("MY_TAG,MY_TAG_1,MY_TAG_2", keys); + } + + public void testBuildTagKeys_whitespace() { + + List tags = new ArrayList(); + tags.add(new ResourceTagVO("MY_TAG ", "321", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + tags.add(new ResourceTagVO("MY_TAG_1 ", "---", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + tags.add(new ResourceTagVO(" MY_TAG_2 ", "444", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + + String keys = TagKeysBuilder.buildTagKeys(tags); + + assertEquals("MY_TAG,MY_TAG_1,MY_TAG_2", keys); + } + + public void testBuildTagKeys_empty_list() { + List tags = new ArrayList(); + + String keys = TagKeysBuilder.buildTagKeys(tags); + assertEquals("", keys); + + + keys = TagKeysBuilder.buildTagKeys(null); + assertEquals("", keys); + } +} \ No newline at end of file diff --git a/server/test/com/cloud/tags/TaggedResourceManagerImplTest.java b/server/test/com/cloud/tags/TaggedResourceManagerImplTest.java new file mode 100644 index 000000000000..e62ae40875e8 --- /dev/null +++ b/server/test/com/cloud/tags/TaggedResourceManagerImplTest.java @@ -0,0 +1,124 @@ +package com.cloud.tags; + +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.router.NetworkHelper; +import com.cloud.server.ResourceTag; +import com.cloud.tags.dao.ResourceTagDao; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import junit.framework.TestCase; +import org.mockito.Mockito; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +public class TaggedResourceManagerImplTest extends TestCase { + + public void setUp() throws Exception { + super.setUp(); + + } + + public void tearDown() throws Exception { + + } + + public void testAddTagToRemoveMetadata() { + TaggedResourceManagerImpl manager = new TaggedResourceManagerImpl(); + + Map> vmsIdResourceTagToRemove = new HashMap>(); + + //userVmId = 3 + manager.addTagToVMMetadata(vmsIdResourceTagToRemove, 3l, "MY_KEY", "123-123", ResourceTag.ResourceObjectType.UserVm); + + Map tags = vmsIdResourceTagToRemove.get(3l); + assertNotNull(tags); + assertTrue( tags.containsKey("TAG_MY_KEY")); + assertEquals("123-123", tags.get("TAG_MY_KEY")); + + //userVmId = 3 + manager.addTagToVMMetadata(vmsIdResourceTagToRemove, 3l, "MY_KEY_12", "444", ResourceTag.ResourceObjectType.UserVm); + + tags = vmsIdResourceTagToRemove.get(3l); + assertNotNull(tags); + assertEquals(2, tags.size()); + assertTrue( tags.containsKey("TAG_MY_KEY_12")); + assertEquals("444", tags.get("TAG_MY_KEY_12")); + + //userVmId = 2 + manager.addTagToVMMetadata(vmsIdResourceTagToRemove, 2l, "MY_I_12", null, ResourceTag.ResourceObjectType.UserVm); + + tags = vmsIdResourceTagToRemove.get(3l); + assertNotNull(tags); + assertEquals(2, tags.size()); + + tags = vmsIdResourceTagToRemove.get(2l); + assertNotNull(tags); + assertEquals(1, tags.size()); + assertTrue( tags.containsKey("TAG_MY_I_12")); + assertEquals("", tags.get("TAG_MY_I_12")); + + } + public void testAddTagToRemoveMetadata_wrong_type() { + //resourceObjectType != UserVM + TaggedResourceManagerImpl manager = new TaggedResourceManagerImpl(); + + Map> vmsIdResourceTagToRemove = new HashMap>(); + + manager.addTagToVMMetadata(vmsIdResourceTagToRemove, 3l, "MY_KEY", "123123", ResourceTag.ResourceObjectType.AutoScaleVmGroup); + + Map tags = vmsIdResourceTagToRemove.get(3l); + assertNull(tags); + } + + public void testDeleteTagsMetadata() throws InsufficientCapacityException, ResourceUnavailableException { + TaggedResourceManagerImpl manager = new TaggedResourceManagerImpl(); + + ResourceTagDao resourceTagDao = mock(ResourceTagDao.class); + + List listTag2 = new ArrayList(); + listTag2.add(new ResourceTagVO("MY_TAG_1", "---", 1l,2l, 2l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + listTag2.add(new ResourceTagVO("MY_TAG_2", "444", 1l,2l, 2l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + + + Mockito.>when(resourceTagDao.listBy(3l, ResourceTag.ResourceObjectType.UserVm)).thenReturn(listTag2); + + List listTag3 = new ArrayList(); + listTag2.add(new ResourceTagVO("TAG_TEST", "---", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + listTag2.add(new ResourceTagVO("TAG_TEST_2", "444", 1l,2l, 3l, ResourceTag.ResourceObjectType.UserVm, "admin", "123123-123123")); + + Mockito.>when(resourceTagDao.listBy(3l, ResourceTag.ResourceObjectType.UserVm)).thenReturn(listTag3); + manager._resourceTagDao = resourceTagDao; + + Map> vmsIdResourceTagToRemove = new HashMap>(); + + Map tags2 = new HashMap(); + tags2.put("TAG_TEST_333", ""); + tags2.put("TAG_TEST_444", ""); + tags2.put(TagKeysBuilder.TAGKEYS_METADATA_KEY, "MY_TAG_1,MY_TAG_2"); + vmsIdResourceTagToRemove.put(2l, tags2); + + Map tags3 = new HashMap(); + tags3.put("TAG_TEST", ""); + tags3.put("TAG_TEST_2", ""); + tags2.put(TagKeysBuilder.TAGKEYS_METADATA_KEY, "TAG_TEST,TAG_TEST_2"); + vmsIdResourceTagToRemove.put(3l, tags3); + + NetworkHelper networkHelper = mock(NetworkHelper.class); + when(networkHelper.updateVMMetadaInVrouter(2l, tags2)).thenReturn(true); + when(networkHelper.updateVMMetadaInVrouter(3l, tags3)).thenReturn(true); + manager._networkHelper = networkHelper; + + manager.updateVMMetaData(vmsIdResourceTagToRemove); + + verify(networkHelper, times(1)).updateVMMetadaInVrouter(2l, tags2); + verify(networkHelper, times(1)).updateVMMetadaInVrouter(3l, tags3); + } + + +} \ No newline at end of file diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index 9057241cc78a..f3f400779f48 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -51,6 +51,7 @@ import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.PortableIpRangeVO; import org.springframework.stereotype.Component; import com.cloud.configuration.ConfigurationManager; @@ -558,4 +559,8 @@ public Domain getVlanDomain(long vlanId) { return null; } + @Override + public PortableIpRangeVO createPortableIpRange(Integer regionId, String startIP, String endIP, String gateway, String netmask, String vlanId) { + return null;// TODO Auto-generated method stub + } } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 8cbf30cdc883..db4f9380cdb9 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -203,6 +203,11 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd return null; } + @Override + public List searchForAllNetworks(ListNetworksCmd cmd) { + return null; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkService#deleteNetwork(long) */ diff --git a/server/test/resources/network-mgr-component.xml b/server/test/resources/network-mgr-component.xml index a0ddce04c0ca..3933f1bd100a 100644 --- a/server/test/resources/network-mgr-component.xml +++ b/server/test/resources/network-mgr-component.xml @@ -71,7 +71,8 @@ under the License. - + + diff --git a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index 66c436b15c33..e13baf782f28 100644 --- a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -30,7 +30,10 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.configuration.ConfigurationManagerImpl; + import org.apache.cloudstack.agent.lb.IndirectAgentLB; + import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -65,7 +68,6 @@ import com.cloud.capacity.dao.CapacityDao; import com.cloud.cluster.ClusterManager; import com.cloud.configuration.Config; -import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.ZoneConfig; import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.DataCenter; @@ -408,7 +410,7 @@ public boolean generateVMSetupCommand(Long ssAHostId) { return true; } else { if (s_logger.isDebugEnabled()) { - s_logger.debug("failed to program http auth into secondary storage vm : " + secStorageVm.getHostName()); + s_logger.warn("failed to program http auth into secondary storage vm : " + secStorageVm.getHostName()); } return false; } @@ -451,7 +453,7 @@ public boolean generateFirewallConfiguration(Long ssAHostId) { } } else { if (s_logger.isDebugEnabled()) { - s_logger.debug("failed to program firewall rules into secondary storage vm : " + ssvm.getName()); + s_logger.warn("failed to program firewall rules into secondary storage vm : " + ssvm.getName()); } return false; } @@ -472,7 +474,7 @@ public boolean generateFirewallConfiguration(Long ssAHostId) { } } else { if (s_logger.isDebugEnabled()) { - s_logger.debug("failed to program firewall rules into secondary storage vm : " + thisSecStorageVm.getHostName()); + s_logger.warn("failed to program firewall rules into secondary storage vm : " + thisSecStorageVm.getHostName()); } return false; } @@ -495,7 +497,7 @@ public SecondaryStorageVmVO startNew(long dataCenterId, SecondaryStorageVm.Role if (!isSecondaryStorageVmRequired(dataCenterId)) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Secondary storage vm not required in zone " + dataCenterId + " acc. to zone config"); + s_logger.warn("Secondary storage vm not required in zone " + dataCenterId + " acc. to zone config"); } return null; } @@ -523,7 +525,7 @@ public SecondaryStorageVmVO startNew(long dataCenterId, SecondaryStorageVm.Role return secStorageVm; } else { if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to allocate secondary storage vm storage, remove the secondary storage vm record from DB, secondary storage vm id: " + + s_logger.warn("Unable to allocate secondary storage vm storage, remove the secondary storage vm record from DB, secondary storage vm id: " + secStorageVmId); } SubscriptionMgr.getInstance().notifySubscribers(ALERT_SUBJECT, this, @@ -654,6 +656,7 @@ protected Map createSecStorageVmInstance(long dataCenterId, Seco SecondaryStorageVmVO secStorageVm = new SecondaryStorageVmVO(id, serviceOffering.getId(), name, template.getId(), template.getHypervisorType(), template.getGuestOSId(), dataCenterId, systemAcct.getDomainId(), systemAcct.getId(), _accountMgr.getSystemUser().getId(), role, serviceOffering.getOfferHA()); + secStorageVm.setDynamicallyScalable(template.isDynamicallyScalable()); secStorageVm = _secStorageVmDao.persist(secStorageVm); try { diff --git a/setup/db/db/schema-443to444.sql b/setup/db/db/schema-443to444.sql new file mode 100644 index 000000000000..44e0406a8e6d --- /dev/null +++ b/setup/db/db/schema-443to444.sql @@ -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. + +--; +-- Schema upgrade from 4.4.3 to 4.4.4; +--; diff --git a/setup/db/db/schema-451to452-cleanup.sql b/setup/db/db/schema-451to452-cleanup.sql new file mode 100644 index 000000000000..9f5e62a36742 --- /dev/null +++ b/setup/db/db/schema-451to452-cleanup.sql @@ -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. + +--; +-- Schema cleanup from 4.5.1 to 4.5.2; +--; diff --git a/setup/db/db/schema-451to452.sql b/setup/db/db/schema-451to452.sql new file mode 100644 index 000000000000..d0aeabf3d1b7 --- /dev/null +++ b/setup/db/db/schema-451to452.sql @@ -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. + +--; +-- Schema upgrade from 4.5.1 to 4.5.2; +--; + +DELETE FROM `cloud`.`configuration` WHERE name like 'saml%'; + +ALTER TABLE `cloud`.`user` ADD COLUMN `external_entity` text DEFAULT NULL COMMENT "reference to external federation entity"; + +DROP TABLE IF EXISTS `cloud`.`saml_token`; +CREATE TABLE `cloud`.`saml_token` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(255) UNIQUE NOT NULL COMMENT 'The Authn Unique Id', + `domain_id` bigint unsigned DEFAULT NULL, + `entity` text NOT NULL COMMENT 'Identity Provider Entity Id', + `created` DATETIME NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_saml_token__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +SET foreign_key_checks = 0; +ALTER TABLE `cloud`.`region` MODIFY `id` int unsigned UNIQUE NOT NULL; +SET foreign_key_checks = 1; diff --git a/systemvm/agent/scripts/config_ssl.sh b/systemvm/agent/scripts/config_ssl.sh index e9340b099f62..cfaa30e66f59 100755 --- a/systemvm/agent/scripts/config_ssl.sh +++ b/systemvm/agent/scripts/config_ssl.sh @@ -137,6 +137,7 @@ then keytool -delete -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt || true keytool -import -alias $aliasName -keystore $keyStore -storepass $storepass -noprompt -file $customCACert keytool -importkeystore -srckeystore $defaultJavaKeyStoreFile -destkeystore $keyStore -srcstorepass $defaultJavaKeyStorePass -deststorepass $storepass -noprompt + keytool -import -alias ${aliasName}CHAIN -keystore $keyStore -storepass $storepass -noprompt -file $customCertChain fi config_apache2_conf $publicIp $hostName diff --git a/systemvm/debian/opt/cloud/bin/merge.py b/systemvm/debian/opt/cloud/bin/merge.py index 0aaa3401f793..eccb194d9e25 100755 --- a/systemvm/debian/opt/cloud/bin/merge.py +++ b/systemvm/debian/opt/cloud/bin/merge.py @@ -236,7 +236,7 @@ def processCLItem(self, num, nw_type): if('localgw' in self.qFile.data['cmd_line']): dp['gateway'] = self.qFile.data['cmd_line']['localgw'] else: - dp['gateway'] = 'None' + dp['gateway'] = '' dp['nic_dev_id'] = num dp['nw_type'] = nw_type qf = QueueFile() diff --git a/systemvm/debian/opt/cloud/bin/setup/common.sh b/systemvm/debian/opt/cloud/bin/setup/common.sh index f26ff5b4c359..b16b65ebb7ef 100755 --- a/systemvm/debian/opt/cloud/bin/setup/common.sh +++ b/systemvm/debian/opt/cloud/bin/setup/common.sh @@ -113,17 +113,24 @@ setup_interface() { setup_interface_ipv6() { sysctl net.ipv6.conf.all.disable_ipv6=0 sysctl net.ipv6.conf.all.forwarding=1 - sysctl net.ipv6.conf.all.accept_ra=1 + sysctl net.ipv6.conf.all.accept_ra=2 sed -i "s/net.ipv6.conf.all.disable_ipv6 =.*$/net.ipv6.conf.all.disable_ipv6 = 0/" /etc/sysctl.conf sed -i "s/net.ipv6.conf.all.forwarding =.*$/net.ipv6.conf.all.forwarding = 1/" /etc/sysctl.conf - sed -i "s/net.ipv6.conf.all.accept_ra =.*$/net.ipv6.conf.all.accept_ra = 1/" /etc/sysctl.conf + sed -i "s/net.ipv6.conf.all.accept_ra =.*$/net.ipv6.conf.all.accept_ra = 2/" /etc/sysctl.conf + + sysctl -w local intfnum=$1 local ipv6="$2" local prelen="$3" local intf=eth${intfnum} + # + # Patch to work with IPV6 + # + ip=$(ifconfig $intf | awk '/inet6 addr.*Scope:Global/ {print $3}' | cut -d\/ -f1) + echo "iface $intf inet6 static" >> /etc/network/interfaces echo " address $ipv6 " >> /etc/network/interfaces echo " netmask $prelen" >> /etc/network/interfaces @@ -646,6 +653,8 @@ parse_cmd_line() { ;; ip6gateway) export IP6GW=$VALUE + echo -en ${COMMA} >> ${CHEF_TMP_FILE} + echo -n \"gateway\"': '\"${VALUE}\" >> ${CHEF_TMP_FILE} ;; eth0mask) export ETH0_MASK=$VALUE @@ -658,9 +667,13 @@ parse_cmd_line() { ;; eth0ip6) export ETH0_IP6=$VALUE + echo -en ${COMMA} >> ${CHEF_TMP_FILE} + echo -n \"eth0ip\"': '\"${VALUE}\" >> ${CHEF_TMP_FILE} ;; eth0ip6prelen) export ETH0_IP6_PRELEN=$VALUE + echo -en ${COMMA} >> ${CHEF_TMP_FILE} + echo -n \"eth0mask\"': '\"${VALUE}\" >> ${CHEF_TMP_FILE} ;; internaldns1) export internalNS1=$VALUE @@ -684,7 +697,7 @@ parse_cmd_line() { export DOMAIN=$VALUE ;; dnssearchorder) - export DNS_SEARCH_ORDER=$VALUE + export DNS_SEARCH_ORDER=$DOMAIN,$VALUE ;; useextdns) export USE_EXTERNAL_DNS=$VALUE diff --git a/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh b/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh index 9161aeb37ee4..d943d88f5e8d 100755 --- a/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh +++ b/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh @@ -27,11 +27,16 @@ setup_dhcpsrvr() { log_it "Setting up dhcp server system vm" setup_common eth0 eth1 setup_dnsmasq - setup_apache2 $ETH0_IP + # + # Patch to work with IPV6 + # + # setup_apache2 $ETH0_IP + [[ $ETH0_IP ]] && ETH0_IPVERSION=$ETH0_IP + [[ $ETH0_IP6 ]] && ETH0_IPVERSION=$ETH0_IP6 + setup_apache2 $ETH0_IPVERSION sed -i /$NAME/d /etc/hosts - [ $ETH0_IP ] && echo "$ETH0_IP $NAME" >> /etc/hosts - [ $ETH0_IP6 ] && echo "$ETH0_IP6 $NAME" >> /etc/hosts + [ $ETH0_IPVERSION ] && echo "$ETH0_IPVERSION $NAME" >> /etc/hosts enable_irqbalance 0 enable_fwding 0 diff --git a/systemvm/debian/opt/cloud/bin/setup/router.sh b/systemvm/debian/opt/cloud/bin/setup/router.sh index f41e57e63750..2a3875c03d65 100755 --- a/systemvm/debian/opt/cloud/bin/setup/router.sh +++ b/systemvm/debian/opt/cloud/bin/setup/router.sh @@ -66,10 +66,16 @@ setup_router() { setup_aesni setup_dnsmasq - setup_apache2 $ETH0_IP + # + # Patch to work with IPV6 + # + # setup_apache2 $ETH0_IP + [[ $ETH0_IP ]] && ETH0_IPVERSION=$ETH0_IP + [[ $ETH0_IP6 ]] && ETH0_IPVERSION=$ETH0_IP6 + setup_apache2 $ETH0_IPVERSION sed -i /$NAME/d /etc/hosts - echo "$ETH0_IP $NAME" >> /etc/hosts + [ $ETH0_IPVERSION ] && echo "$ETH0_IPVERSION $NAME" >> /etc/hosts enable_irqbalance 1 disable_rpfilter_domR diff --git a/systemvm/debian/root/.ssh/authorized_keys b/systemvm/debian/root/.ssh/authorized_keys index c09f6379a34b..6e718936f587 100644 --- a/systemvm/debian/root/.ssh/authorized_keys +++ b/systemvm/debian/root/.ssh/authorized_keys @@ -1 +1 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2RIE3hgSAD8zULuyE7KDW9EKh2oVbNGY7iSL/VI5xHLISKh4e8ksTshWjlGBtrUCnuzR7y2BUxZ65RI8XkB1fEDxcOU4/0lVPvJYDSsGveXoOgpLwOtKRoGLgjFUGzBQlj2s6YaYQxoNTqtBVkDIH6ekPNq0Q38hRrFcsVIk1sFo5ejuvFxt2wx6APcFIQtHSNezEDO0GVUScDU1N1YEMMv1PU3M/SrcezkXrGl/efF3kWtY9L5xm7sojHMCCqsI38r8ogof67F7JdWRXM6Nl3VzkdCBzWGcyAl+cYfjzgOiBGXyAyYBk8qqzJjKwUOtdjfRvCyowA/0xBwMW1T7PQ== +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvFu3MLSPphFRBR1yM7nBukXWS9gPdAXfqq9cfC8ZqQN9ybi531aj44CybZ4BVT4kLfzbAs7+7nJeSIpPHxjv9XFqbxjIxoFeGYkj7s0RrJgtsEmvAAubZ3mYboUAYUivMgnJFLnv4VqyAbpjix6CfECUiU4ygwo24F3F6bAmhl4Vo1R5TSUdDIX876YePJTFtuVkLl4lu/+xw1QRWrgaSFosGICT37IKY7RjE79Ozb0GjNHyJPPgVAGkUVO4LawroL9dYOBlzdHpmqqA9Kc44oQBpvcU7s1+ezRTt7fZNnP7TG9ninZtrvnP4qmwAc4iUJ7N1bwh0mCblnoTfZ28hw== \ No newline at end of file diff --git a/test/integration/globo/README b/test/integration/globo/README new file mode 100644 index 000000000000..492e235477d2 --- /dev/null +++ b/test/integration/globo/README @@ -0,0 +1 @@ +nosetests --with-marvin --marvin-config=demo.cfg --load test_dns_api.py diff --git a/test/integration/globo/cfg/advanced-globo.cfg b/test/integration/globo/cfg/advanced-globo.cfg new file mode 100644 index 000000000000..4e7c83abbf84 --- /dev/null +++ b/test/integration/globo/cfg/advanced-globo.cfg @@ -0,0 +1,226 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +{ + "zones": [ + { + "name": "Sandbox-simulator", + "guestcidraddress": "10.1.1.0/24", + "dns1": "10.147.28.6", + "physical_networks": [ + { + "broadcastdomainrange": "Zone", + "vlan": "100-200", + "name": "Sandbox-pnet", + "traffictypes": [ + { + "typ": "Guest" + }, + { + "typ": "Management" + }, + { + "typ": "Public" + } + ], + "providers": [ + { + "broadcastdomainrange": "ZONE", + "name": "VirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "VpcVirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "InternalLbVm" + } + ], + "isolationmethods": [ + "VLAN" + ] + } + ], + "ipranges": [ + { + "startip": "192.168.2.2", + "endip": "192.168.2.200", + "netmask": "255.255.255.0", + "vlan": "50", + "gateway": "192.168.2.1" + } + ], + "networktype": "Advanced", + "pods": [ + { + "endip": "172.16.15.200", + "name": "POD0", + "startip": "172.16.15.2", + "netmask": "255.255.255.0", + "clusters": [ + { + "clustername": "C0", + "hypervisor": "simulator", + "hosts": [ + { + "username": "root", + "url": "http://sim/c0/h0", + "password": "password" + }, + { + "username": "root", + "url": "http://sim/c0/h1", + "password": "password" + } + ], + "clustertype": "CloudManaged", + "primaryStorages": [ + { + "url": "nfs://10.147.28.6:/export/home/sandbox/primary0", + "name": "PS0" + }, + { + "url": "nfs://10.147.28.6:/export/home/sandbox/primary1", + "name": "PS1" + } + ] + }, + { + "clustername": "C1", + "hypervisor": "simulator", + "hosts": [ + { + "username": "root", + "url": "http://sim/c1/h0", + "password": "password" + } + ], + "clustertype": "CloudManaged", + "primaryStorages": [ + { + "url": "nfs://10.147.28.6:/export/home/sandbox/primary2", + "name": "PS2" + } + ] + } + ], + "gateway": "172.16.15.1" + } + ], + "internaldns1": "10.147.28.6", + "secondaryStorages": [ + { + "url": "nfs://10.147.28.6:/export/home/sandbox/secondary", + "provider" : "NFS" + } + ] + } + ], + "dbSvr": { + "dbSvr": "localhost", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": + { + "LogFolderPath": "/tmp/" + }, + "globalConfig": [ + { + "name": "network.gc.wait", + "value": "60" + }, + { + "name": "storage.cleanup.interval", + "value": "300" + }, + { + "name": "vm.op.wait.interval", + "value": "5" + }, + { + "name": "default.page.size", + "value": "10000" + }, + { + "name": "network.gc.interval", + "value": "60" + }, + { + "name": "instance.name", + "value": "QA" + }, + { + "name": "workers", + "value": "10" + }, + { + "name": "account.cleanup.interval", + "value": "600" + }, + { + "name": "guest.domain.suffix", + "value": "sandbox.simulator" + }, + { + "name": "expunge.delay", + "value": "60" + }, + { + "name": "vm.allocation.algorithm", + "value": "random" + }, + { + "name": "expunge.interval", + "value": "60" + }, + { + "name": "expunge.workers", + "value": "3" + }, + { + "name": "check.pod.cidrs", + "value": "true" + }, + { + "name": "secstorage.allowed.internal.sites", + "value": "10.147.28.0/24" + }, + { + "name": "direct.agent.load.size", + "value": "1000" + }, + { + "name": "router.version.check", + "value": "false" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "localhost", + "passwd": "password", + "user": "root", + "port": 8096, + "hypervisor": "simulator", + "useHttps": "False", + "certCAPath": "NA", + "certPath": "NA" + } + ] +} diff --git a/test/integration/globo/cfg/devcloud.cfg b/test/integration/globo/cfg/devcloud.cfg new file mode 100644 index 000000000000..758ee8f09d47 --- /dev/null +++ b/test/integration/globo/cfg/devcloud.cfg @@ -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. +# +# This configuration is meant for running advanced networking, with management server on the laptop. +# It requires that the user run a DNS resolver within devcloud via 'apt-get install dnsmasq' + +{ + "zones": [ + { + "localstorageenabled": "true", + "name": "local-Zone", + "guestcidraddress": "10.1.1.0/24", + "dns1": "10.2.162.12", + "physical_networks": [ + { + "broadcastdomainrange": "Zone", + "vlan": "3900-4000", + "name": "rede fisica unica", + "traffictypes": [ + { + "xen": "MGMT", + "typ": "Management" + }, + { + "xen": "MGMT", + "typ": "Guest" + }, + { + "xen": "MGMT", + "typ": "Public" + } + ], + "providers": [ + { + "broadcastdomainrange": "ZONE", + "name": "VirtualRouter" + }, + { + "broadcastdomainrange": "ZONE", + "name": "VpcVirtualRouter" + } + ] + } + ], + "ipranges": [ + { + "startip": "10.0.3.100", + "endip": "10.0.3.200", + "netmask": "255.255.255.0", + "vlan": "untagged", + "gateway": "10.0.3.2" + } + ], + "networktype": "Advanced", + "pods": [ + { + "endip": "192.168.56.249", + "name": "testpod", + "startip": "192.168.56.200", + "netmask": "255.255.255.0", + "clusters": [ + { + "clustername": "testcluster", + "hypervisor": "XenServer", + "hosts": [ + { + "username": "root", + "url": "http://192.168.56.10/", + "password": "password" + } + ], + "clustertype": "CloudManaged", + "primaryStorages": [ + ] + } + ], + "gateway": "192.168.56.1" + } + ], + "internaldns1": "10.2.162.12", + "secondaryStorages": [ + { + "name": "sec-stg", + "url": "nfs://192.168.56.1:/home/exports/devcloud/secondary", + "provider": "NFS", + "details": [ ] + } + ] + } + ], + "dbSvr": { + "dbSvr": "127.0.0.1", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": [ + { + "name": "TestCase", + "file": "/tmp/testcase.log" + }, + { + "name": "TestClient", + "file": "/tmp/testclient.log" + } + ], + "mgtSvr": [ + { + "mgtSvrIp": "localhost", + "port": 8096 + } + ], + "globalConfig": [ + { + "name": "networkapi.vm.equipmentgroup", + "value": "23" + } + ] + +} diff --git a/test/integration/globo/demo.cfg b/test/integration/globo/demo.cfg new file mode 100644 index 000000000000..60608118b615 --- /dev/null +++ b/test/integration/globo/demo.cfg @@ -0,0 +1,26 @@ +{ + "zones": [ + { + "name": "Sandbox-simulator" + } + ], + "dbSvr": { + "dbSvr": "localhost", + "passwd": "cloud", + "db": "cloud", + "port": 3306, + "user": "cloud" + }, + "logger": { + "LogFolderPath": "/tmp" + }, + "mgtSvr": [ + { + "mgtSvrIp": "localhost", + "port": 8096, + "user": "admin", + "passwd": "password", + "hypervisor": "simulator" + } + ] +} diff --git a/test/integration/globo/setup.sh b/test/integration/globo/setup.sh new file mode 100755 index 000000000000..960a6b66fafc --- /dev/null +++ b/test/integration/globo/setup.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +[[ ! -f /etc/redhat-release ]] && PrintLog ERROR "Opss... run this script only in RedHat OS. Exiting..." && exit 1 + +PrintLog() { + level=$1 + msg=$2 + timestamp=$(date +"%d/%b/%Y:%H:%M:%S %z") + echo "[${timestamp}] [${level}] ${msg}" +} + + +PrintLog DEBUG "project_branch: ${project_branch}" +PrintLog DEBUG "globodns_host: ${globodns_host}" +PrintLog DEBUG "globodns_resolver_nameserver: ${globodns_resolver_nameserver}" + +# virtual env vars +virtualenv_name='cloudstack' + +if [ -f "/opt/generic/python27/bin/virtualenvwrapper.sh" ]; then + source /opt/generic/python27/bin/virtualenvwrapper.sh + + req_pkgs=$(rpm -qa | egrep -c "(python-devel|gmp-devel)") + if [ ${req_pkgs} -ne 2 ]; then + PrintLog FATAL "Please install python-devel and gmp-devel packages" + exit 1 + fi + PrintLog INFO "Switching to '${virtualenv_name}' virtualenv" + + [[ -z ${WORKON_HOME} ]] && WORKON_HOME=~jenkins/.virtualenvs + [[ ! -d "${WORKON_HOME}/${virtualenv_name}" ]] && mkvirtualenv -p /opt/generic/python27/bin/python ${virtualenv_name} + source $WORKON_HOME/${virtualenv_name}/bin/activate + + pip="${WORKON_HOME}/${virtualenv_name}/bin/pip" + pip_options="--extra-index-url=https://artifactory.globoi.com/artifactory/pypi/ --extra-index-url=https://artifactory.globoi.com/artifactory/api/pypi/pypi/simple --extra-index-url=https://pypi.python.org" + python="${WORKON_HOME}/${virtualenv_name}/bin/python" + nosetests="${WORKON_HOME}/${virtualenv_name}/bin/nosetests" + ${pip} freeze | grep -q simple-db-migrate || ${pip} install simple-db-migrate + # require for dnsapi + ${pip} freeze | grep -q beautifulsoup4 || ${pip} install beautifulsoup4==4.3.2 ${pip_options} + # {pip} freeze | grep -q pycrypto || {pip} install pycrypto +else + PrintLog FATAL "No virtualenv wrapper was found, please install it!" +fi + +project_basedir=$(pwd) +globo_test_basedir="${project_basedir}/test/integration/globo" +maven_log='/tmp/cloudstack.log' + +cloudstack_deploy_dir='/var/lib/jenkins/cloudstack-deploy' +export JAVA_HOME='/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.65.x86_64' +export PATH="$JAVA_HOME/bin:$PATH" + +debug=1 + +StartJetty() { + max_retries=18 + sleep_time=10 + ret_count=1 + PrintLog INFO "Starting cloudstack w/ simulator..." + MAVEN_OPTS="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" mvn --log-file ${maven_log} -pl client jetty:run -Dsimulator >/dev/null & + [[ $debug ]] && PrintLog DEBUG "Checking if jetty is ready..." + [[ ! -f $maven_log ]] && sleep 5 + while [ $ret_count -le $max_retries ]; do + if grep -q '\[INFO\] Started Jetty Server' ${maven_log}; then + [[ $debug ]] && PrintLog INFO "Jetty is running and ready" + return 0 + else + [[ $debug ]] && PrintLog DEBUG "Jetty is not ready yet... sleeping more ${sleep_time}sec (${ret_count}/${max_retries})" + sleep $sleep_time + ret_count=$[$ret_count+1] + fi + done + PrintLog ERROR "Jetty is not ready after waiting for $((${max_retries}*${sleep_time})) sec." + exit 1 +} + +ShutdownJetty() { + max_retries=7 + sleep_time=3 + ret_count=1 + PrintLog INFO "Stopping cloudstack..." + kill $(ps wwwaux | awk '/[m]aven.*jetty:run -Dsimulator/ {print $2}') 2>/dev/null + [[ $? -ne 0 ]] && PrintLog WARN "Failed to stop jetty" + while [ $ret_count -le $max_retries ]; do + if [[ -z $(ps wwwaux | awk '/[m]aven.*jetty:run -Dsimulator/ {print $2}') ]]; then + return 1 + else + [[ $debug ]] && PrintLog DEBUG "Jetty is alive, waiting more ${sleep_time} (${ret_count}/${max_retries})" + sleep $sleep_time + ret_count=$[$ret_count+1] + fi + done + PrintLog WARN "Kill -9 to jetty process!!!" + kill -9 $(ps wwwaux | awk '/[m]aven.*jetty:run -Dsimulator/ {print $2}') +} + +WaitForInfrastructure() { + max_retries=22 + sleep_time=10 + ret_count=1 + PrintLog INFO "Waiting for infrastructure..." + while [ $ret_count -le $max_retries ]; do + if grep -q 'server resources successfully discovered by SimulatorSecondaryDiscoverer' ${maven_log}; then + [[ $debug ]] && PrintLog INFO "Infrastructure is ready" + return 1 + else + [[ $debug ]] && PrintLog DEBUG "Infrasctructure is not ready yet... sleeping more ${sleep_time}sec (${ret_count}/${max_retries})" + sleep $sleep_time + ret_count=$[$ret_count+1] + fi + done + PrintLog ERROR "Infrastructure was not ready in $((${max_retries}*${sleep_time})) seconds..." + exit 1 +} + +installMarvin() { + # Tries to install marvin.. just in case.. + ${pip} freeze | grep -qi Marvin || ${pip} install --allow-external mysql-connector-python ${project_basedir}/tools/marvin/dist/Marvin-*.tar.gz + + # Install marvin to ensure that we are using the correct version + ${pip} freeze | grep -qi Marvin && ${pip} install --upgrade --allow-external mysql-connector-python ${project_basedir}/tools/marvin/dist/Marvin-*.tar.gz + + ls ~/.virtualenvs/cloudstack/lib/python2.7/site-packages/marvin/cloudstackAPI/ | grep addG +} + + +# Checkout repository, compile, use virtualenv and sync the mavin commands +ShutdownJetty +PrintLog INFO "Removing log file '${maven_log}'" +rm -f ${maven_log} +[[ $debug ]] && PrintLog DEBUG "Change work dir to ${project_basedir}" +[[ ! -d $project_basedir ]] && PrintLog ERROR "Directory ${project_basedir} does not exist...exit" && exit 1 +cd ${project_basedir} +PrintLog INFO "Checking out to branch '${project_branch}'" +git checkout ${project_branch} >/dev/null 2>/dev/null +PrintLog INFO "Pulling latest modifications" +git pull + +last_commit=$(git log -n 1 | grep commit | cut -d' ' -f2) +last_commit_file="/tmp/cloudstack-integration-tests-last-commit.txt" + +PrintLog INFO "last git commit: ${last_commit}" + +#vejo se o arquivo de ultimo commit existe +[[ ! -f "$last_commit_file" ]] && echo "" > ${last_commit_file} + +saved_last_commit=$(cat ${last_commit_file} | head -1) + +if [ "${last_commit}" != "${saved_last_commit}" ]; then + + PrintLog INFO "Solving some dependencies..." + if [ ! -d "/var/lib/jenkins/cloudstack-deploy" ]; then + PrintLog INFO "Clone cloudstack-deploy project into ${cloudstack_deploy_dir}" + git clone https://gitlab.globoi.com/time-evolucao-infra/cloudstack-deploy.git -b master ${cloudstack_deploy_dir} + fi + + PrintLog INFO "Compiling cloudstack..." + + mvn -Pdeveloper -Dsimulator clean install + [[ $? -ne 0 ]] && PrintLog ERROR "Failed to compile ACS" && exit 1 + PrintLog INFO "Compiling and packing marvin..." + mvn -P developer -pl :cloud-marvin + [[ $? -ne 0 ]] && PrintLog ERROR "Failed to compile marvin" && exit 1 + + echo "${last_commit}" > ${last_commit_file} + + installMarvin + + # Deploy DB, Populate DB and create infra structure + PrintLog INFO "Creating SQL schema" + mvn -q -P developer -pl developer -Ddeploydb >/dev/null 2>/dev/null + [[ $? -ne 0 ]] && PrintLog ERROR "Failed to deploy DB" && exit 1 + mvn -Pdeveloper -pl developer -Ddeploydb-simulator >/dev/null 2>/dev/null + [[ $? -ne 0 ]] && PrintLog ERROR "Failed to deploy DB simulator" && exit 1 + PrintLog INFO "Doing some required SQL migrations" + (cd /var/lib/jenkins/cloudstack-deploy/dbmigrate && git checkout master && db-migrate >/dev/null) + cd - + StartJetty + PrintLog INFO "Creating an advanced zone..." + ${python} ${project_basedir}/tools/marvin/marvin/deployDataCenter.py -i ${project_basedir}/test/integration/globo/cfg/advanced-globo.cfg + + # Required restart + WaitForInfrastructure + ShutdownJetty + PrintLog INFO "Removing log file '${maven_log}'" + rm -f ${maven_log} +else + PrintLog INFO "There were no code changes, so we don't need compile!!! yaayyyyyyyy" +fi + +StartJetty + +# Tests +PrintLog INFO "Sync marvin" +cd ${project_basedir} +pwd +mvn -Pdeveloper,marvin.sync -Dendpoint=localhost -pl :cloud-marvin + +sleep 5 + +installMarvin + +# check if Globo assets are in marvin tarball file +[[ ! `tar tvzf ${project_basedir}/tools/marvin/dist/Marvin-*.tar.gz | grep Globo` ]] && PrintLog ERROR "Tests will fail!!! Marvin tarball does not contain Globo files" && exit 1 + +PrintLog INFO "Testing DNS API" +${nosetests} --with-marvin --marvin-config=${globo_test_basedir}/demo.cfg --zone=Sandbox-simulator ${globo_test_basedir}/test_dns_api.py +retval=$? +if [[ $retval -ne 0 ]]; then + PrintLog ERROR "Tests failed!!!" + ShutdownJetty + exit 1 +fi + +results_file=$(ls -tr /tmp/MarvinLogs/$(date +"%b_%d_%Y")*/results.txt | tail -1) +echo "Results file: ${results_file}" + +tail -1 ${results_file} | grep -qw 'OK' +retval=$? +cat ${results_file} +if [[ $retval -eq 0 ]]; then + ShutdownJetty + PrintLog INFO "All steps and tests successfully passed" + exit 0 +else + PrintLog ERROR "Tests failed!!!" + exit 1 +fi diff --git a/test/integration/globo/test_deploy_vm.py b/test/integration/globo/test_deploy_vm.py new file mode 100644 index 000000000000..8ef2c9070209 --- /dev/null +++ b/test/integration/globo/test_deploy_vm.py @@ -0,0 +1,172 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#Test from the Marvin - Testing in Python wiki + +#All tests inherit from cloudstackTestCase +from marvin.cloudstackTestCase import cloudstackTestCase + +#Import Integration Libraries + +#base - contains all resources as entities and defines create, delete, list operations on them +from marvin.lib.base import Account, VirtualMachine, ServiceOffering, Network + +#utils - utility classes for common cleanup, external library wrappers etc +from marvin.lib.utils import cleanup_resources + +#common - commonly used methods for all tests are listed here +from marvin.lib.common import get_zone, get_domain, get_template + +# from nose.plugins.attrib import attr + + +class TestData(object): + """Test data object that is required to create resources + """ + def __init__(self): + self.testdata = { + # data to create an account + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + "password": "password", + }, + # data reqd for virtual machine creation + "virtual_machine": { + "name": "testfromnose", + "displayname": "testfromnose", + }, + #small service offering + "service_offering": { + "name": "Small Instance", + "displaytext": "Small Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 100, + "storagetype": 'local', + }, + # network + "network": { + "name": "NetworkviaNose", + "displaytext": "NetworkviaNose" + }, + "ostype": 'CentOS 5.6 (64-bit)', + } + + +class TestDeployVM(cloudstackTestCase): + """Test deploy a VM into a user account + """ + + @classmethod + def setUpClass(self): + self.testdata = TestData().testdata + self.apiclient = super(TestDeployVM, self).getClsTestClient().getApiClient() + + # Get Zone, Domain and Default Built-in template + self.domain = get_domain(self.apiclient, self.testdata) + self.zone = get_zone(self.apiclient, self.testdata) + self.testdata["mode"] = self.zone.networktype + self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"]) + + # create a user account + self.account = Account.create( + self.apiclient, + self.testdata["account"], + domainid=self.domain.id + ) + # create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offering"] + ) + # create a network + self.network = Network.create( + self.apiclient, + self.testdata["network"], + self.account.name, + self.account.domainid, + networkofferingid='66f5e68e-8815-4e24-a85d-bb3677cb6f6e', + zoneid=self.zone.id + ) + #build cleanup list + self.cleanup = [ + self.service_offering, + self.account + ] + + # @attr(tags=['advanced', 'simulator', 'basic', 'sg']) + def test_deploy_vm(self): + """Test Deploy Virtual Machine + + # Validate the following: + # 1. Virtual Machine is accessible via SSH + # 2. listVirtualMachines returns accurate information + """ + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id, + networkids=[self.network.id] + ) + + list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vms, list), + True, + "List VM response was not a valid list" + ) + self.assertNotEqual( + len(list_vms), + 0, + "List VM response was empty" + ) + + vm = list_vms[0] + self.assertEqual( + vm.id, + self.virtual_machine.id, + "Virtual Machine ids do not match" + ) + self.assertEqual( + vm.name, + self.virtual_machine.name, + "Virtual Machine names do not match" + ) + self.assertEqual( + vm.state, + "Running", + msg="VM is not in Running state" + ) + + @classmethod + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) diff --git a/test/integration/globo/test_dns_api.py b/test/integration/globo/test_dns_api.py new file mode 100644 index 000000000000..580b0fa33971 --- /dev/null +++ b/test/integration/globo/test_dns_api.py @@ -0,0 +1,329 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 for DNS lookup +from bs4 import BeautifulSoup +import dns.resolver +import requests +import os +import sys +import time +import json + +#All tests inherit from cloudstackTestCase +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import addGloboDnsHost, createNetwork + +#Import Integration Libraries +from marvin.lib.base import Account, VirtualMachine, ServiceOffering, Network, NetworkOffering, NetworkServiceProvider, PhysicalNetwork +from marvin.lib.utils import cleanup_resources +from marvin.lib.common import get_zone, get_domain, get_template + +# get globodns endpoint +if os.environ.get('globodns_host'): + globodns_host = os.environ.get('globodns_host') +else: + sys.exit("The environment variable 'globodns_host' was not found!") + +if os.getenv('globodns_admin_user'): + globodns_admin_user = os.environ.get('globodns_admin_user') +else: + sys.exit("The environment variable 'globodns_admin_user' was not found!") + +if os.getenv('globodns_admin_password'): + globodns_admin_password = os.environ.get('globodns_admin_password') +else: + sys.exit("The environment variable 'globodns_admin_password' was not found!") + +if os.getenv('globodns_resolver_nameserver_1'): + resolver_nameserver_1 = os.getenv('globodns_resolver_nameserver_1') +else: + sys.exit("The environment variable 'globodns_resolver_nameserver_1' was not found!") + +resolver_nameserver_2 = os.getenv('globodns_resolver_nameserver_2','') + +if os.getenv('globodns_auth_token'): + globodns_payload = {"auth_token": os.getenv('globodns_auth_token'), "now": "true"} +else: + sys.exit("The environment variable 'globodns_auth_token' was not found!") + + +globodns_export_path = '/bind9/export' +globodns_headers = {"Content-type": "application/json", "Accept": "application/json"} + + +class Data(object): + """Test data object that is required to create resources + """ + def __init__(self): + self.testdata = { + # data to create an account + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + "password": "password", + }, + # data reqd for virtual machine creation + "virtual_machine": { + "name": "testglobodns", + "displayname": "testglobodns", + }, + # service offering + "service_offering": { + "name": "Small Instance", + "displaytext": "Small Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 100, + }, + "network_offering": { + "name": "DNS API Network Offering", + "displaytext": "DNS API Network Offering", + "guestiptype": "Isolated", + "supportedservices": "Dhcp,Dns,SourceNat", + "traffictype": "GUEST", + "serviceProviderList": { + "Dhcp": "VirtualRouter", + "Dns": "GloboDns", + "SourceNat": "VirtualRouter" + } + }, + # network + "network": { + "name": "GloboDnsNetwork", + "displaytext": "GloboDnsNetwork", + "networkdomain": "integrationtest.globo.com" + }, + "globodns_provider": { + "url": globodns_host, + "username": globodns_admin_user, + "password": globodns_admin_password + }, + "ostype": 'CentOS 5.6 (64-bit)', + } + + +class TestVMGloboDns(cloudstackTestCase): + """Test deploy a VM using DNS API + """ + + def setUp(self): + self.testdata = Data().testdata + self.apiclient = self.testClient.getApiClient() + + # Get Zone, Domain and Default Built-in template + self.domain = get_domain(self.apiclient) + self.zone = get_zone(self.apiclient) + self.testdata["mode"] = self.zone.networktype + self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"]) + + # create a user account + self.account = Account.create( + self.apiclient, + self.testdata["account"], + domainid=self.domain.id + ) + # create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offering"] + ) + + self.physical_network = PhysicalNetwork.list( + self.apiclient, + zoneid=self.zone.id + ) + + # set up DNS API Provider + nw_service_providers = NetworkServiceProvider.list( + self.apiclient, + name='GloboDns', + physicalnetworkid=self.physical_network[0].id + ) + if isinstance(nw_service_providers, list): + self.globodns_provider = nw_service_providers[0] + else: + self.globodns_provider = NetworkServiceProvider.add( + self.apiclient, + 'GloboDns', + self.physical_network[0].id, + None + ) + + cmd = addGloboDnsHost.addGloboDnsHostCmd() + cmd.username = self.testdata["globodns_provider"]["username"] + cmd.password = self.testdata["globodns_provider"]["password"] + cmd.url = self.testdata["globodns_provider"]["url"] + cmd.physicalnetworkid = self.physical_network[0].id + self.apiclient.addGloboDnsHost(cmd) + + if self.globodns_provider.state != 'Enabled': + NetworkServiceProvider.update(self.apiclient, self.globodns_provider.id, state='Enabled') + + self.network_offering = NetworkOffering.create( + self.apiclient, + self.testdata["network_offering"] + ) + # Enable Net Offering + self.network_offering.update(self.apiclient, state='Enabled') + + #build cleanup list + self.cleanup = [ + self.service_offering, + self.account, + self.network_offering + ] + + self.resolver = dns.resolver.Resolver() + self.resolver.nameservers = [resolver_nameserver_1] # Set nameserver to resolve hostnames + if resolver_nameserver_2: + self.resolver.nameservers.append(resolver_nameserver_2) + + def test_deploy_vm_with_globodns(self): + """Test Deploy Virtual Machine with DNS API + + # Validate the following: + # 1. Network domain is correctly filled + # 2. VM is accessible through its DNS entry + """ + # create a network + cmd = createNetwork.createNetworkCmd() + cmd.name = self.testdata["network"]["name"] + cmd.displaytext = self.testdata["network"]["displaytext"] + cmd.networkdomain = self.testdata["network"]["networkdomain"] + cmd.account = self.account.name + cmd.domainid = self.account.domainid + cmd.networkofferingid = self.network_offering.id + cmd.zoneid = self.zone.id + self.network = self.apiclient.createNetwork(cmd) + + list_networks = Network.list(self.apiclient, id=self.network.id) + + self.debug( + "Verify listNetworks response for network: %s" % self.network.id + ) + + self.assertEqual( + isinstance(list_networks, list), + True, + "List networks response was not a valid list" + ) + self.assertNotEqual( + len(list_networks), + 0, + "List networks response was empty" + ) + + network = list_networks[0] + self.assertEqual( + network.id, + self.network.id, + "Network ids do not match" + ) + self.assertEqual( + network.name, + self.network.name, + "Network names do not match" + ) + self.assertEqual( + network.networkdomain, + self.network.networkdomain, + "Network domains do not match" + ) + + # Throws exception if domain doesn't exist + # self.resolver.query(network.networkdomain) + + self.debug("creating virtual machine") + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id, + networkids=[self.network.id] + ) + + list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id) + # force export & reload bind in dns-api + self.info("forcing export & reload bind in dns-api") + login_url = globodns_host + '/users/sign_in' + globodns_headers = {"Referer": login_url} + client = requests.Session() + soup = BeautifulSoup(client.get(login_url).content) + authenticity_token = soup.find('input', dict(name='authenticity_token'))['value'] + self.debug("Globodns host: %s | authenticity_token: %s | globodns_admin_user: %s" % (globodns_host, authenticity_token, globodns_admin_user)) + login_data={"authenticity_token": authenticity_token, 'user[email]':globodns_admin_user, 'user[password]':globodns_admin_password, 'user[remember_me]': "1", "now": "true"} + + r = client.post(login_url, data=login_data, headers=globodns_headers) + r = client.post(globodns_host + globodns_export_path, data=login_data, headers=globodns_headers) + + self.debug("DnsAPI response:\n %s" % r.content) + #requests.post(globodns_host + globodns_export_path, data=json.dumps(globodns_payload), headers=globodns_headers) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vms, list), + True, + "List VM response was not a valid list" + ) + self.assertNotEqual( + len(list_vms), + 0, + "List VM response was empty" + ) + + vm = list_vms[0] + self.assertEqual( + vm.id, + self.virtual_machine.id, + "Virtual Machine ids do not match" + ) + self.assertEqual( + vm.name, + self.virtual_machine.name, + "Virtual Machine names do not match" + ) + self.assertEqual( + vm.state, + "Running", + msg="VM is not in Running state" + ) + + query_name = vm.name + '.' + network.networkdomain + self.debug("Asserting query name %s to ip %s" % (query_name, vm.nic[0].ipaddress)) + resolver_ = self.resolver.query(query_name) + self.assertEqual( + resolver_[0].address, + vm.nic[0].ipaddress, + "Resolved IP address and VM IP address do not match" + ) + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + self.globodns_provider.delete(self.apiclient) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) diff --git a/test/integration/smoke/test_pvlan.py b/test/integration/smoke/test_pvlan.py index 149a310c0972..6e187b99dc13 100644 --- a/test/integration/smoke/test_pvlan.py +++ b/test/integration/smoke/test_pvlan.py @@ -38,8 +38,12 @@ class TestPVLAN(cloudstackTestCase): vlan = 1234 isolatedpvlan = 567 - def setUp(self): - self.apiClient = self.testClient.getApiClient() + @classmethod + def setUpClass(cls): + cls.testClient = super(TestPVLAN, cls).getClsTestClient() + cls.apiClient = cls.testClient.getApiClient() + cls.zone = get_zone(cls.apiClient, cls.testClient.getZoneForTests()) + cls.zoneId = cls.zone.id @attr(tags = ["advanced"], required_hardware="false") def test_create_pvlan_network(self): diff --git a/test/integration/smoke/test_vm_ha.py b/test/integration/smoke/test_vm_ha.py new file mode 100644 index 000000000000..1d9021ce9fc3 --- /dev/null +++ b/test/integration/smoke/test_vm_ha.py @@ -0,0 +1,199 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#Test from the Marvin - Testing in Python wiki + +import time + +#All tests inherit from cloudstackTestCase +from marvin.cloudstackTestCase import cloudstackTestCase + +#Import Integration Libraries + +#base - contains all resources as entities and defines create, delete, list operations on them +from marvin.lib.base import Account, VirtualMachine, Cluster, Host, ServiceOffering, Configurations, SimulatorMock + +#utils - utility classes for common cleanup, external library wrappers etc +from marvin.lib.utils import cleanup_resources + +#common - commonly used methods for all tests are listed here +from marvin.lib.common import get_zone, get_domain, get_template + +from nose.plugins.attrib import attr + +class TestDeployVMHA(cloudstackTestCase): + """Test VM HA + """ + + def setUp(self): + self.testdata = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + + # Get Zone, Domain and Default Built-in template + self.domain = get_domain(self.apiclient) + self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) + + self.testdata["mode"] = self.zone.networktype + self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"]) + + self.hosts = [] + suitablecluster = None + clusters = Cluster.list(self.apiclient) + self.assertTrue(isinstance(clusters, list) and len(clusters) > 0, msg = "No clusters found") + for cluster in clusters: + self.hosts = Host.list(self.apiclient, clusterid=cluster.id, type='Routing') + if isinstance(self.hosts, list) and len(self.hosts) >= 2: + suitablecluster = cluster + break + self.assertTrue(isinstance(self.hosts, list) and len(self.hosts) >= 2, msg = "Atleast 2 hosts required in cluster for VM HA test") + #update host tags + for host in self.hosts: + Host.update(self.apiclient, id=host.id, hosttags=self.testdata["service_offerings"]["hasmall"]["hosttags"]) + + #create a user account + self.account = Account.create( + self.apiclient, + self.testdata["account"], + domainid=self.domain.id + ) + #create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offerings"]["hasmall"] + ) + #deploy ha vm + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id) + self.debug( + "Verify listVirtualMachines response for virtual machine: %s"\ + % self.virtual_machine.id + ) + self.assertTrue(isinstance(list_vms, list) and len(list_vms) == 1, msg = "List VM response was empty") + self.virtual_machine = list_vms[0] + + self.mock_checkhealth = SimulatorMock.create( + apiclient=self.apiclient, + command="CheckHealthCommand", + zoneid=suitablecluster.zoneid, + podid=suitablecluster.podid, + clusterid=suitablecluster.id, + hostid=self.virtual_machine.hostid, + value="result:fail") + self.mock_ping = SimulatorMock.create( + apiclient=self.apiclient, + command="PingCommand", + zoneid=suitablecluster.zoneid, + podid=suitablecluster.podid, + clusterid=suitablecluster.id, + hostid=self.virtual_machine.hostid, + value="result:fail") + self.mock_checkvirtualmachine = SimulatorMock.create( + apiclient=self.apiclient, + command="CheckVirtualMachineCommand", + zoneid=suitablecluster.zoneid, + podid=suitablecluster.podid, + clusterid=suitablecluster.id, + hostid=self.virtual_machine.hostid, + value="result:fail") + self.mock_pingtest = SimulatorMock.create( + apiclient=self.apiclient, + command="PingTestCommand", + zoneid=suitablecluster.zoneid, + podid=suitablecluster.podid, + value="result:fail") + self.mock_checkonhost_list = [] + for host in self.hosts: + if host.id != self.virtual_machine.hostid: + self.mock_checkonhost_list.append(SimulatorMock.create( + apiclient=self.apiclient, + command="CheckOnHostCommand", + zoneid=suitablecluster.zoneid, + podid=suitablecluster.podid, + clusterid=suitablecluster.id, + hostid=host.id, + value="result:fail")) + #build cleanup list + self.cleanup = [ + self.service_offering, + self.account, + self.mock_checkhealth, + self.mock_ping, + self.mock_checkvirtualmachine, + self.mock_pingtest + ] + self.cleanup = self.cleanup + self.mock_checkonhost_list + + @attr(tags = ['advanced'], required_hardware="false", BugId="CLOUDSTACK-6873") + def test_vm_ha(self): + """Test VM HA + + # Validate the following: + # VM started on other host in cluster + """ + + #wait for VM to HA + ping_timeout = Configurations.list(self.apiclient, name="ping.timeout") + ping_interval = Configurations.list(self.apiclient, name="ping.interval") + total_duration = int(float(ping_timeout[0].value) * float(ping_interval[0].value)) + time.sleep(total_duration) + + duration = 0 + vm = None + while duration < total_duration: + list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id) + self.assertTrue(isinstance(list_vms, list) and len(list_vms) == 1, msg = "List VM response was empty") + vm = list_vms[0] + if vm.hostid != self.virtual_machine.hostid and vm.state == "Running": + break + else: + time.sleep(10) + duration = duration + 10 + + self.assertEqual( + vm.id, + self.virtual_machine.id, + "VM ids do not match") + self.assertEqual( + vm.name, + self.virtual_machine.name, + "VM names do not match") + self.assertEqual( + vm.state, + "Running", + msg="VM is not in Running state") + self.assertNotEqual( + vm.hostid, + self.virtual_machine.hostid, + msg="VM is not started on another host as part of HA") + + def tearDown(self): + try: + for host in self.hosts: + Host.update(self.apiclient, id=host.id, hosttags="") + + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) + diff --git a/test/integration/smoke/test_vm_sync.py b/test/integration/smoke/test_vm_sync.py new file mode 100644 index 000000000000..6d5694538771 --- /dev/null +++ b/test/integration/smoke/test_vm_sync.py @@ -0,0 +1,151 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#Test from the Marvin - Testing in Python wiki + +import time + +#All tests inherit from cloudstackTestCase +from marvin.cloudstackTestCase import cloudstackTestCase + +#Import Integration Libraries + +#base - contains all resources as entities and defines create, delete, list operations on them +from marvin.lib.base import Account, VirtualMachine, Cluster, Host, ServiceOffering, Configurations, SimulatorMock + +#utils - utility classes for common cleanup, external library wrappers etc +from marvin.lib.utils import cleanup_resources + +#common - commonly used methods for all tests are listed here +from marvin.lib.common import get_zone, get_domain, get_template + +from nose.plugins.attrib import attr + +class TestDeployVMSync(cloudstackTestCase): + """Test VM Sync + """ + + def setUp(self): + self.testdata = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + + # Get Zone, Domain and Default Built-in template + self.domain = get_domain(self.apiclient) + self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) + + self.testdata["mode"] = self.zone.networktype + self.template = get_template(self.apiclient, self.zone.id, self.testdata["ostype"]) + + hosts = Host.list(self.apiclient, type='Routing') + self.assertTrue(isinstance(hosts, list) and len(hosts) > 0, msg = "No hosts found") + self.host = hosts[0] + #update host tags + Host.update(self.apiclient, id=self.host.id, hosttags=self.testdata["service_offerings"]["taggedsmall"]["hosttags"]) + + #create a user account + self.account = Account.create( + self.apiclient, + self.testdata["account"], + domainid=self.domain.id + ) + #create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.testdata["service_offerings"]["taggedsmall"] + ) + #deploy vms + self.vm1 = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + self.vm2 = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + self.vm3 = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + list_vms = VirtualMachine.list(self.apiclient, ids=[self.vm1.id, self.vm2.id, self.vm3.id], listAll=True) + self.assertTrue(isinstance(list_vms, list) and len(list_vms) == 3, msg = "List VM response is empty") + clusters = Cluster.list(self.apiclient, id=self.host.clusterid) + self.assertTrue(isinstance(clusters, list) and len(clusters) > 0, msg = "Cluster not found") + + json_response = '{"com.cloud.agent.api.PingRoutingWithNwGroupsCommand":{"newGroupStates":{},"newStates":{},"_hostVmStateReport":{"%s":{"state":"PowerOn","host":"%s"},"%s":{"state":"PowerOff","host":"%s"}},"_gatewayAccessible":true,"_vnetAccessible":true,"hostType":"Routing","hostId":0,"contextMap":{},"wait":0}}' + json_response = json_response%(self.vm1.instancename, self.host.name, self.vm2.instancename, self.host.name) + + #create a mock to simulate vm1 as power-on, vm2 as power-off and vm3 as missing + self.mock_ping = SimulatorMock.create( + apiclient=self.apiclient, + command="PingRoutingWithNwGroupsCommand", + zoneid=self.zone.id, + podid=clusters[0].podid, + clusterid=clusters[0].id, + hostid=self.host.id, + value='', + jsonresponse=json_response, + method='POST') + + #build cleanup list + self.cleanup = [ + self.service_offering, + self.account, + self.mock_ping + ] + + @attr(tags = ['advanced'], required_hardware="false", BugId="CLOUDSTACK-6873") + def test_vm_sync(self): + """Test VM Sync + + # Validate the following: + # vm1 should be running, vm2 should be stopped as power report says PowerOff, vm3 should be stopped as missing from power report + """ + + #wait for vmsync to happen + ping_interval = Configurations.list(self.apiclient, name="ping.interval") + total_duration = int(float(ping_interval[0].value) * 3.2) + time.sleep(total_duration) + + list_vms = VirtualMachine.list(self.apiclient, ids=[self.vm1.id, self.vm2.id, self.vm3.id], listAll=True) + self.assertTrue(isinstance(list_vms, list) and len(list_vms) == 3, msg = "List VM response is empty") + for vm in list_vms: + if vm.id == self.vm1.id: + self.assertTrue(vm.state == "Running", msg = "VM {0} is expected to be in running state".format(vm.name)) + elif vm.id == self.vm2.id or vm.id == self.vm3.id: + self.assertTrue(vm.state == "Stopped", msg = "VM {0} is expected to be in stopped state".format(vm.name)) + + def tearDown(self): + try: + Host.update(self.apiclient, id=self.host.id, hosttags="") + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) diff --git a/test/integration/stress/test_multipleremotevpn_vpc.py b/test/integration/stress/test_multipleremotevpn_vpc.py new file mode 100755 index 000000000000..e3ff9b95afa5 --- /dev/null +++ b/test/integration/stress/test_multipleremotevpn_vpc.py @@ -0,0 +1,795 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +""" Component tests Multiple Connections for Remote Access VPN on VPC Functionality. +""" +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.lib.base import (Vpn, + VpnUser, + VPC, + Account, + User, + VpcOffering, + VPC, + ServiceOffering, + NATRule, + NetworkACL, + PublicIPAddress, + NetworkOffering, + Network, + VirtualMachine, + StaticNATRule, + Template, + Configurations + ) +from marvin.lib.common import (list_publicIP, + get_domain, + get_zone, + get_template, + list_networks, + list_templates, + list_service_offering, + list_vpc_offerings, + list_network_offerings, + list_routers, + list_hosts + ) +from marvin.lib.utils import (cleanup_resources, + random_gen, + get_process_status, + get_host_credentials + ) +import time +import string +from marvin.sshClient import SshClient +import traceback +import thread +import json +from marvin.codes import ( + SUCCESS, FAILED +) + +class TestMultipleVPNAccessonVPC(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + + cloudstackTestClient = super( + TestMultipleVPNAccessonVPC, + cls + ).getClsTestClient() + + cls.debug("Obtain the Admin's API Client") + cls.api_client = cloudstackTestClient.getApiClient() + cls.debug("Get the dictionary information that will be used during CCP tests, from test_data.py present on the Client") + cls.services = cloudstackTestClient.getParsedTestDataConfig() + + if cls.services is None: + cls.debug("Services Object is None") + raise Exception("Services Object is None") + + cls.debug("Procure the CloudStack Setup configuration Information") + with open(cls.services["config_path"], 'rb') as fp: + cls.pullconfig = json.load(fp) + + cls.debug("Update 'remote.access.vpn.client.iprange','remote.access.vpn.user.limit','max.account.primary.storage','max.account.public.ips','max.account.user.vms','max.account.volumes','max.account.cpus', Global Configuration Parameters") + + update_vpn_client_iprange = Configurations.update( + cls.api_client, + name="remote.access.vpn.client.iprange", + value="10.1.2.1-10.1.2.120") + + cls.debug("'remote.access.vpn.client.iprange' Global Configuration Parameter Updated Successfully") + + update_vpn_user_limit = Configurations.update( + cls.api_client, + name="remote.access.vpn.user.limit", + value=str(int(cls.services["vpnclient_count"]*2)) + ) + + cls.debug("'remote.access.vpn.user.limit' Global Configuration Parameter Updated Successfully") + + update_max_account_primary_stg_limit = Configurations.update( + cls.api_client, + name="max.account.primary.storage", + value=str(int(cls.services["vpnclient_count"]*20 + 100)) + ) + cls.debug("'max.account.primary.storage' Global Configuration Parameter Updated Successfully") + + update_max_account_public_ips_limit = Configurations.update( + cls.api_client, + name="max.account.public.ips", + value=str(int(cls.services["vpnclient_count"]*2 + 10)) + ) + cls.debug("'max.account.public.ips' Global Configuration Parameter Updated Successfully") + + update_max_account_user_vms_limit = Configurations.update( + cls.api_client, + name="max.account.user.vms", + value=str(int(cls.services["vpnclient_count"]*2)) + ) + cls.debug("'max.account.user.vms' Global Configuration Parameter Updated Successfully") + + update_max_account_volumes_limit = Configurations.update( + cls.api_client, + name="max.account.volumes", + value=str(int(cls.services["vpnclient_count"]*2)) + ) + cls.debug("'max.account.volumes' Global Configuration Parameter Updated Successfully") + + update_max_account_cpus_limit = Configurations.update( + cls.api_client, + name="max.account.cpus", + value=str(int(cls.services["vpnclient_count"]*2)) + ) + cls.debug("'max.account.cpus' Global Configuration Parameter Updated Successfully") + + cls.debug("Restart the Management Server") + TestMultipleVPNAccessonVPC.restart_mgmt_server(cls.services["config_path"]) + cls.debug("Completed restarting the Management Server") + + cls.debug("Wait for 120 seconds...") + time.sleep(120) + cls.debug("End of 120 seconds wait time....") + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client) + cls.zone = get_zone( + cls.api_client, + zone_name = cls.services["zone_vpn"]["name"]) + + cls.debug("Use an Existing 'Tiny Instance' Service Offering on the Setup") + list_service_offerings = [] + list_service_offerings = list_service_offering( + cls.api_client, + keyword="Tiny Instance", + ) + + cls._cleanup = [] + + if list_service_offerings is not None: + cls.debug("Found an Existing 'Tiny Instance' Service Offering on the Setup") + cls.service_offering = list_service_offerings[0] + + else: + cls.debug("Create a service offering which will be used for VM deployments in this test") + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls.debug("Add the created service offering to the _cleanup queue") + cls._cleanup.append(cls.service_offering) + + try: + cls.debug("Create or Use Existing Account to own the VPN Clients, which is used to test Remote VPN Access to VPC") + cls.api_client_vpn_client_reg_user = cloudstackTestClient.getUserApiClient( + UserName="CSRegularVPNClientUser", + DomainName="ROOT" + ) + + list_vpn_client_regular_user = User.list( + cls.api_client, + username="CSRegularVPNClientUser" + ) + + cls.debug("Procure the Account Name and DomainID Information of the Regular Account") + cls.vpn_client_reg_acct_name = list_vpn_client_regular_user[0].account + cls.vpn_client_reg_domain_id = list_vpn_client_regular_user[0].domainid + + list_vpn_client_regular_user_acct = Account.list( + cls.api_client, + name = cls.vpn_client_reg_acct_name, + listall = True + ) + + cls._cleanup.append(Account(list_vpn_client_regular_user_acct[0].__dict__)) + + # Register a Template that already has VPN client installed on it. The template registered here + # has extra scripts to facilitate automated operations to execute Test Cases. + # Template has pre-configured configuration files required for the VPN Client operations. + # The following files are present on the registered template. The location of the files are locations + # on a VM deployed from this template + # 1. "/tmp/ipsec.conf" + # 2. "/tmp/ipsec.secrets" + # 3. "/tmp/options.xl2tpd.client" + # 4. "/tmp/xl2tpd.conf" + # 5 "/tmp/vpnclient_services.sh" + # 6. "/tmp/firstconn_expectscript.exp" + # 7. "/tmp/secondconn_expectscript.exp" + + cls.debug("Use an Existing VPN Client Template on the Setup") + list_vpn_client_templates = list_templates( + cls.api_client_vpn_client_reg_user, + keyword="VPNClient", + templatefilter="featured", + zoneid = cls.zone.id + ) + + if list_vpn_client_templates is not None: + cls.debug("Found an Existing VPN Client Template on the Setup") + cls.template = list_vpn_client_templates[0] + + else: + cls.debug("Register a Template that already has VPN client installed on it") + cls.template = Template.register( + cls.api_client, + cls.services["vpn_template"], + zoneid=cls.zone.id, + hypervisor='XenServer' + ) + + cls._cleanup.append(cls.template) + + cls.debug("Sleep for {0} seconds specified in the dictionary before checking for Template's Availability".format(cls.services["sleep"])) + time.sleep(cls.services["sleep"]) + + cls.debug("Procure Timeout Value from the dictionary") + timeout = cls.services["timeout"] + + while True: + list_template_response = list_templates( + cls.api_client_vpn_client_reg_user, + templatefilter='featured', + id=cls.template.id, + ) + + if isinstance(list_template_response, list): + break + elif timeout == 0: + raise Exception("List template failed!") + + time.sleep(5) + timeout = timeout - 1 + + cls.debug("Verify template response to check whether template is present") + + if list_template_response is None: + raise Exception("Check whether the VPN Client Template is available") + template_response = list_template_response[0] + if template_response.isready == False: + raise Exception("Template state is not ready, it is %r" % template_response.isready) + + # Queue that holds all the VPN Client VMs Information + cls.vpnclientvms = [] + + cls.debug("Deploy {0} VPN Clients in the account".format(int(cls.services["vpnclient_count"]))) + + for vm in xrange(0,int(cls.services["vpnclient_count"])): + + cls.debug("Deploy a new VM {0} in first account. This VM which will be configured as VPN Client".format(int(vm))) + new_vpnclient_vm = VirtualMachine.create( + cls.api_client_vpn_client_reg_user, + cls.services["virtual_machine"], + zoneid=cls.zone.id, + serviceofferingid=cls.service_offering.id, + templateid=cls.template.id, + ) + + cls.debug("Add new VM {0} to the vpnclientvms Queue".format(int(vm))) + cls.vpnclientvms.append(new_vpnclient_vm) + + cls.debug("Allow SSH Access to the new VPN Client VM {0}".format(int(vm))) + new_vpnclient_vm.access_ssh_over_nat( + cls.api_client_vpn_client_reg_user, + cls.services, + new_vpnclient_vm, + allow_egress=True + ) + cls.debug("VM for VPNClient Access Got Created with Public IP Address %s" % new_vpnclient_vm.public_ip) + + cls.debug("Create or Use existing Account in which we deploy VPCs and test remote access to them from the First Account's VMs present on isolated Network") + cls.api_client_vpn_server_reg_user = cloudstackTestClient.getUserApiClient( + UserName="CSRegularVPNServerUser", + DomainName="ROOT" + ) + + list_vpn_server_regular_user = User.list( + cls.api_client, + username="CSRegularVPNServerUser" + ) + + cls.debug("Procure the Account Name and DomainID Information of the Regular Account") + cls.vpn_server_reg_acct_name = list_vpn_server_regular_user[0].account + cls.vpn_server_reg_domain_id = list_vpn_server_regular_user[0].domainid + + list_vpn_server_regular_user_acct = Account.list( + cls.api_client, + name = cls.vpn_server_reg_acct_name, + listall = True + ) + + cls._cleanup.append(Account(list_vpn_server_regular_user_acct[0].__dict__)) + + cls.debug("Use an Existing 'VPC off-' Service Offering on the Setup") + list_available_vpc_offerings = list_vpc_offerings( + cls.api_client, + keyword="VPC off-", + ) + + if list_available_vpc_offerings is not None: + cls.debug("Found an Existing 'VPC off-' Service Offering on the Setup") + cls.vpc_offering = VpcOffering(list_available_vpc_offerings[0].__dict__) + + else: + cls.debug("Creating a VPC offering..") + cls.vpc_offering = VpcOffering.create( + cls.api_client, + cls.services["vpc_offering"] + ) + + # Add the created VPC Offering to __cleanup queue + cls._cleanup.append(cls.vpc_offering) + + # Enable to created VPC Offering inorder to deploy VPCs with it + cls.debug("Enabling the VPC offering created") + cls.vpc_offering.update(cls.api_client, state='Enabled') + cls.debug("Enabled the VPC Offering") + + # Create a VPC for the second account + cls.debug("Creating a VPC in the account: %s" % cls.vpn_server_reg_acct_name) + cls.firstvpc = VPC.create( + cls.api_client_vpn_server_reg_user, + cls.services["vpc_remote_vpn"], + vpcofferingid=cls.vpc_offering.id, + zoneid=cls.zone.id + ) + + cls.debug("Use an Existing 'NET_OFF-RemoteAccessVPNTest-' Network Offering on the Setup") + list_available_network_offerings = list_network_offerings( + cls.api_client, + keyword="NET_OFF-RemoteAccessVPNTest-", + ) + + if list_available_network_offerings is not None: + cls.debug("Found an Existing 'NET_OFF-RemoteAccessVPNTest-' Network Offering on the Setup") + cls.network_off = NetworkOffering(list_available_network_offerings[0].__dict__) + + else: + cls.debug('Create NetworkOffering for Networks in VPC') + cls.services["vpc_network_offering"]["name"] = "NET_OFF-RemoteAccessVPNTest-"+ random_gen() + cls.network_off = NetworkOffering.create( + cls.api_client, + cls.services["vpc_network_offering"], + conservemode=False + ) + + # Add the created Network Offering to __cleanup queue + cls._cleanup.append(cls.network_off) + + # Enable Network offering + cls.network_off.update(cls.api_client, state='Enabled') + + cls.debug('Created and Enabled NetworkOffering') + cls.services["network"]["name"] = "NETWORK-" + random_gen() + + # Create First Network Tier in the First VPC created for second account using the network offering created above. + cls.debug('Adding Network=%s' % cls.services["network"]) + cls.firstnetworktier = Network.create( + cls.api_client_vpn_server_reg_user, + cls.services["network"], + networkofferingid=cls.network_off.id, + zoneid=cls.zone.id, + gateway=cls.services["firstnetwork_tier"]["gateway"], + netmask=cls.services["firstnetwork_tier"]["netmask"], + vpcid=cls.firstvpc.id + ) + + cls.debug("Created network with ID: %s" % cls.firstnetworktier.id) + + # Create Ingress and Egress NetworkACL rules for First Network Tier in the First VPC created for second account. + cls.debug("Adding NetworkACL rules to make Network accessible for all Protocols and all CIDRs ") + NetworkACL.create( + cls.api_client_vpn_server_reg_user, + cls.services["all_rule"], + networkid=cls.firstnetworktier.id, + traffictype='Ingress' + ) + + NetworkACL.create( + cls.api_client_vpn_server_reg_user, + cls.services["all_rule"], + networkid=cls.firstnetworktier.id, + traffictype='Egress' + ) + + listFirstVPC = VPC.list( + cls.api_client_vpn_server_reg_user, + id=cls.firstvpc.id + ) + + cls.debug("Information about the VPC: {0}".format(str(listFirstVPC))) + + cls.debug("Obtain the source nat IP Address of the first VPC.") + cls.listFirstVPCPublicIpAddress = list_publicIP( + cls.api_client_vpn_server_reg_user, + issourcenat="true", + vpcid=listFirstVPC[0].id, + listall="true" + ) + cls.debug("Information about the VPC's Source NAT IP Address: {0}".format(str(cls.listFirstVPCPublicIpAddress))) + + cls.debug("Enable Remote Access VPN on the source nat Public IP Address of the first VPC") + cls.FirstVPNonFirstVPC = Vpn.create( + cls.api_client_vpn_server_reg_user, + cls.listFirstVPCPublicIpAddress[0].id + ) + + cls.debug("Successfully Created First VPN on VPC with preshared key:"+ cls.FirstVPNonFirstVPC.presharedkey) + cls.listfirstNetworkTier = list_networks( + cls.api_client_vpn_server_reg_user, + id=cls.firstnetworktier.id, + listall=True + ) + + + cls.debug("Create a VM using the default template on the First Network Tier in the First VPC of the Second Account") + cls.vm1 = VirtualMachine.create( + cls.api_client_vpn_server_reg_user, + cls.services["virtual_machine"], + zoneid=cls.zone.id, + serviceofferingid=cls.service_offering.id, + templateid=cls.template.id, + networkids=[str(cls.firstnetworktier.id)] + ) + + cls.debug("First VM deployed in the first Network Tier") + + except Exception as e: + cleanup_resources(cls.api_client, cls._cleanup) + printex = traceback.format_exc() + cls.debug("Exception Occurred : {0}".format(printex)) + raise Exception("Warning: Exception during Setting Up the Test Suite Configuration : %s" % e) + + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + print("Warning: Exception during cleanup : %s" % e) + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def filecopy(cls,virtual_machine,localfile=None,remotefilelocation=None,permissions="644"): + cls.ssh = SshClient( + host=virtual_machine.public_ip, + port=TestMultipleVPNAccessonVPC.services["virtual_machine"]["ssh_port"], + user='root', + passwd='password') + + cls.ssh.scp(localfile,remotefilelocation) + cls.ssh.runCommand('chmod %s %s' % (permissions,remotefilelocation)) + cls.debug("%s file successfully copied to %s " % (localfile, remotefilelocation)) + + cls.ssh.close() + + @classmethod + def restart_mgmt_server(cls,config): + + cls.ssh = SshClient( + host=cls.pullconfig["mgtSvr"][0]["mgtSvrIp"], + port=22, + user=cls.pullconfig["mgtSvr"][0]["user"], + passwd=cls.pullconfig["mgtSvr"][0]["passwd"] + ) + + result = cls.ssh.runCommand("/etc/init.d/cloudstack-management restart") + + if result["status"] == SUCCESS: + time.sleep(120) + else: + raise Exception("Failure in restarting cloudstack Management Server") + + cls.ssh.close() + + @staticmethod + def char_xrange(firstchar, lastchar): + #Generates a range of Alphabetical Characters, lastchar inclusive + for c in xrange(ord(firstchar),ord(lastchar)+1): + yield chr(c) + + @staticmethod + def get_guest_ip_address(guest_ip_address): + + for ch in ["'","[","]","\\","n"]: + if ch in guest_ip_address: + guest_ip_address = guest_ip_address.replace(ch,"") + + return guest_ip_address + + @staticmethod + def configureVPNClientServicesFile(virtual_machine,vpnclient_services_script,vpnserverip=None,vpnnetworkcidr="192.168.10.0/24",psk=None,vpnuser=None,vpnuserpassword=None): + + ssh = SshClient( + host=virtual_machine.public_ip, + port=TestMultipleVPNAccessonVPC.services["virtual_machine"]["ssh_port"], + user='root', + passwd='password') + + + cidr = "\/".join(vpnnetworkcidr.rsplit("/",1)) + + ssh.execute(''' sed -i "s/VPN_ADDR=.*/VPN_ADDR='%s'/g" %s ''' % (vpnserverip,vpnclient_services_script)) + ssh.execute(''' sed -i "s/CIDR=.*/CIDR='%s'/g" %s ''' % (cidr,vpnclient_services_script)) + ssh.execute(''' sed -i "s/PSK=.*/PSK='%s'/g" %s ''' % (psk,vpnclient_services_script)) + ssh.execute(''' sed -i "s/VPN_USR=.*/VPN_USR='%s'/g" %s ''' % (vpnuser,vpnclient_services_script)) + ssh.execute(''' sed -i "s/VPN_USR_PWD=.*/VPN_USR_PWD='%s'/g" %s ''' % (vpnuserpassword,vpnclient_services_script)) + ssh.execute('echo "VPN Client Services File Ready" >> /tmp/executionoutput.txt') + + ssh.close() + + @staticmethod + def vpnClientServicesInit(virtual_machine,vpnclient_services_script): + + ssh = SshClient( + host=virtual_machine.public_ip, + port=TestMultipleVPNAccessonVPC.services["virtual_machine"]["ssh_port"], + user='root', + passwd='password') + + ssh.execute('%s init >> /tmp/executionoutput.txt' % (vpnclient_services_script)) + ssh.execute('echo "VPN Client Services Configuration Files Initiated" >> /tmp/executionoutput.txt') + + ssh.close() + + @staticmethod + def vpnClientServicesStart(virtual_machine,vpnclient_services_script): + + ssh = SshClient( + host=virtual_machine.public_ip, + port=TestMultipleVPNAccessonVPC.services["virtual_machine"]["ssh_port"], + user='root', + passwd='password') + + ssh.execute('%s start >> /tmp/executionoutput.txt' % (vpnclient_services_script)) + ssh.execute('echo "VPN Client Services Started" >> /tmp/executionoutput.txt') + + ssh.close() + + + def exec_script_on_user_vm(self, virtual_machine,script, exec_cmd_params, expected_result, negative_test=False,public_ip=None): + try: + exec_success = False + + if public_ip is not None: + self.debug("getting SSH client for Vm: %s with ip %s" % (virtual_machine.id,public_ip)) + sshClient = virtual_machine.get_ssh_client(ipaddress=public_ip) + else: + self.debug("getting SSH client for Vm: %s with ip %s" % (virtual_machine.id,virtual_machine.public_ip)) + sshClient = virtual_machine.get_ssh_client(ipaddress=virtual_machine.public_ip) + + result = sshClient.execute(script+exec_cmd_params) + + self.debug("script: %s" % script+exec_cmd_params) + self.debug("result: %s" % result) + + str_result = str(str(result).strip()) + str_expected_result = str(expected_result).strip() + if str_result == str_expected_result: + exec_success = True + + if negative_test: + self.assertEqual(exec_success, + True, + "Script result is %s matching with %s" % (str_result, str_expected_result)) + else: + self.assertEqual(exec_success, + True, + "Script result is %s is not matching with %s" % (str_result, str_expected_result)) + + except Exception as e: + self.debug('Error=%s' % e) + printex = traceback.format_exc() + self.debug("Exception Occurred : {0}".format(printex)) + raise e + + def ping_vm(self, virtual_machine, guest_ip_address, count=1, interval=15, thread_name="Thread-None"): + + try: + self.debug("{4} : Check whether VM with ID: {0} with Public IP: {1} is able to ping Guest VM with IP: {2} - {3} Times !!!".format(virtual_machine.id, virtual_machine.public_ip, guest_ip_address, count, thread_name)) + iteration = 1 + total_times = count + + while count > 0: + self.debug("{4} : Test whether VM with ID: {0} with Public IP: {1} is able to ping Guest VM with IP: {2} for {3} iteration".format(virtual_machine.id, virtual_machine.public_ip, guest_ip_address, iteration, thread_name)) + self.exec_script_on_user_vm( + virtual_machine, + 'ping -c 1 {0}'.format(guest_ip_address), + "| grep -oP \'\d+(?=% packet loss)\'", + "[u'0']", + negative_test=False + ) + self.debug("{4} : Verified Successfully that VM with ID: {0} with Public IP: {1} is able to ping Guest VM with IP: {2} for {3} iteration".format(virtual_machine.id, virtual_machine.public_ip, guest_ip_address, iteration, thread_name)) + count = count-1 + iteration = iteration + 1 + + self.debug("{1} : Wait for {0} seconds before next ping".format(interval,thread_name)) + time.sleep(interval) + self.debug("Still {0} iterations left for Thread {1}".format(count,thread_name)) + + if count == 0: + self.debug("Ping {0} Times is Completed for Thread {1} ".format(total_times,thread_name)) + else: + raise Exception("Count value is still at {0}".format(count)) + + except Exception as e: + + printex = traceback.format_exc() + self.debug("Exception Occurred : {0}".format(printex)) + raise Exception("Warning: Exception during pinging VM : %s" % e) + + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.hypervisor = self.testClient.getHypervisorInfo() + self.cleanup=None + return + + def tearDown(self): + try: + #Clean up, terminate the created network offerings + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "intervlan"],required_hardware="true") + def test_01_Multiple_RemoteAccessVPN_Connections_To_VPC_Ping_Guest_VM_Multiple_Times(self): + """ Test case no : Test Multiple VPN Connections to a VPN Server on VPC + + + # Validate the following for Each VPN VM Client + # 1. Create VPN User on the VPC + # 2. Configure the VPN Client VM with the required Information + # 3. Initialize the VPN Client Services on the VPN Client + # 4. Start the VPN Client Services on the VPN Client + # 5. Wait for 30 seconds before attempting to ping + # 6. Conduct the Ping Test on the VM + + # After the deployment VPN Client VMs and the post deployment steps, do the following steps: + # 7. Wait for 60 seconds + # 8. Check Routers pppX NICs Information + """ + + for vm in xrange(0,int(TestMultipleVPNAccessonVPC.services["vpnclient_count"])): + + vpn_user_name = ''.join((str(vm),"-user")) + vpn_password = ''.join((str(vm),"-pass")) + + self.debug("VPN User Name created with %s " % vpn_user_name) + self.debug("VPN Password created with %s " % vpn_password) + + self.debug("Create new VPN User to use the Remote Access Service enabled on the VPC") + newVPNUser = VpnUser.create( + TestMultipleVPNAccessonVPC.api_client_vpn_server_reg_user, + vpn_user_name, + vpn_password, + rand_name=False + ) + self.debug("VPN User %s got created Successfully " % vpn_user_name) + + self.debug("Configure the VPN Client Services on the VM deployed for VPN client purpose.") + TestMultipleVPNAccessonVPC.configureVPNClientServicesFile( + TestMultipleVPNAccessonVPC.vpnclientvms[vm], + "/tmp/vpnclient_services.sh", + TestMultipleVPNAccessonVPC.listFirstVPCPublicIpAddress[0].ipaddress, + TestMultipleVPNAccessonVPC.listfirstNetworkTier[0].cidr, + TestMultipleVPNAccessonVPC.FirstVPNonFirstVPC.presharedkey, + vpn_user_name, + vpn_password + ) + self.debug("Configuration of VPN Client VM %d Done " % (vm)) + + self.debug("Initialize the VPN Client Services on the VPN Client") + TestMultipleVPNAccessonVPC.vpnClientServicesInit( + TestMultipleVPNAccessonVPC.vpnclientvms[vm], + "/tmp/vpnclient_services.sh" + ) + self.debug("Initiation of VPN Client Services on VM %d Done " % (vm)) + + self.debug("Start the VPN Client Services on the VPN Client") + TestMultipleVPNAccessonVPC.vpnClientServicesStart( + TestMultipleVPNAccessonVPC.vpnclientvms[vm], + "/tmp/vpnclient_services.sh" + ) + self.debug("VPN Client Services on VM %d Started Successfully " % (vm)) + + self.debug("Wait for 30 seconds before attempting to ping") + time.sleep(30) + + self.debug("Conduct the Ping Test on the VM %d" % (vm)) + thread.start_new_thread(self.ping_vm,( + TestMultipleVPNAccessonVPC.vpnclientvms[vm], + TestMultipleVPNAccessonVPC.vm1.nic[0].ipaddress, + 25000, + 15, + "Thread-{0}".format(vm) + )) + + self.debug("Waiting for 60 seconds.........") + time.sleep(60) + self.debug("End of 60 seconds.........") + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + vpcid= TestMultipleVPNAccessonVPC.firstvpc.id, + listall=True + ) + + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + + host = hosts[0] + + self.debug("Router ID: %s, state: %s" % (router.id, router.state)) + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + if self.hypervisor.lower() == 'vmware': + result = get_process_status( + self.apiclient.connection.mgtSvr, + 22, + self.apiclient.connection.user, + self.apiclient.connection.passwd, + router.linklocalip, + "ifconfig | grep ppp", + hypervisor=self.hypervisor + ) + else: + try: + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) + result = get_process_status( + host.ipaddress, + 22, + host.user, + host.passwd, + router.linklocalip, + "ifconfig | grep ppp" + ) + self.debug("Routers pppX NICs Information : %s" % str(result)) + except KeyError: + self.skipTest("Marvin configuration has no host credentials to check router services") + diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index a025efefcbcb..356cbc71ea6a 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -87,6 +87,11 @@ 'OpenDaylight': 'Network', 'createServiceInstance': 'Network', 'addGloboDnsHost': 'Network', + 'listBusinessService': 'Globo Dictionary', + 'listClients': 'Globo Dictionary', + 'listComponents': 'Globo Dictionary', + 'listSubComponents': 'Globo Dictionary', + 'listProducts': 'Globo Dictionary', 'listnuagevspdomaintemplates': 'Network', 'listnuagevspglobaldomaintemplate': 'Network', 'Vpn': 'VPN', @@ -190,7 +195,18 @@ 'CA': 'Certificate', 'listElastistorInterface': 'Misc', 'cloudian': 'Cloudian', - 'Sioc' : 'Sioc' + 'Sioc' : 'Sioc', + 'listBusinessService': 'Globo Dictionary', + 'listClients': 'Globo Dictionary', + 'listComponents': 'Globo Dictionary', + 'listSubComponents': 'Globo Dictionary', + 'listProducts': 'Globo Dictionary', + 'oAuth2Login': 'Oauth2 Login', + 'oauthRedirect': 'Oauth2 Redirect', + 'removeGloboACLRule': 'Remove Globo ACL rule', + 'listGloboACLRules': 'List Globo ACL rules', + 'createGloboACLRule': 'Create Globo ACL rule', + 'addGloboAclApiHost': 'Add Globo Acl ApiHost' } diff --git a/tools/build/setnextversion.sh b/tools/build/setnextversion.sh index 2387b8926bc0..1dbf730be887 100755 --- a/tools/build/setnextversion.sh +++ b/tools/build/setnextversion.sh @@ -64,6 +64,7 @@ echo "found $currentversion" echo 'setting version numbers' mvn versions:set -DnewVersion=$version -P vmware -P developer -P systemvm -P simulator -P baremetal -P ucs -Dnoredist mv deps/XenServerJava/pom.xml.versionsBackup deps/XenServerJava/pom.xml +perl -pi -e "s/$currentversion/$version/" tools/checkstyle/pom.xml perl -pi -e "s/$currentversion/$version/" deps/XenServerJava/pom.xml perl -pi -e "s/$currentversion/$version/" tools/apidoc/pom.xml perl -pi -e "s/$currentversion/$version/" debian/changelog diff --git a/tools/marvin/marvin/jsonHelper.py b/tools/marvin/marvin/jsonHelper.py index 2cd342f615bf..5cdaea7d6f90 100644 --- a/tools/marvin/marvin/jsonHelper.py +++ b/tools/marvin/marvin/jsonHelper.py @@ -157,7 +157,7 @@ def getResultObj(returnObj, responsecls=None): if result.count is not None: for key in result.__dict__.iterkeys(): - if key == "count": + if key == "count" or key == "context": continue else: return getattr(result, key) diff --git a/tools/marvin/marvin/lib/__init__.py b/tools/marvin/marvin/lib/__init__.py index 978b68af62a4..13a83393a912 100644 --- a/tools/marvin/marvin/lib/__init__.py +++ b/tools/marvin/marvin/lib/__init__.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 diff --git a/tools/marvin/marvin/src/__init__.py~4.5.2 b/tools/marvin/marvin/src/__init__.py~4.5.2 new file mode 100644 index 000000000000..13a83393a912 --- /dev/null +++ b/tools/marvin/marvin/src/__init__.py~4.5.2 @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/tools/marvin/marvin/src/__init__.py~HEAD b/tools/marvin/marvin/src/__init__.py~HEAD new file mode 100644 index 000000000000..13a83393a912 --- /dev/null +++ b/tools/marvin/marvin/src/__init__.py~HEAD @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/tools/marvin/marvin/sshClient.py b/tools/marvin/marvin/sshClient.py index e872256812c8..1877cf1d42a4 100644 --- a/tools/marvin/marvin/sshClient.py +++ b/tools/marvin/marvin/sshClient.py @@ -121,13 +121,14 @@ def createConnection(self): timeout=self.timeout, allow_agent=False) else: + self.ssh.load_host_keys(self.keyPairFiles) 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 + look_for_keys=True ) self.logger.debug("===SSH to Host %s port : %s SUCCESSFUL===" % (str(self.host), str(self.port))) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index d68b4f8c3cf7..01fe65d9b094 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -2018,6 +2018,58 @@ div.copypasteicon:hover { background: url("../images/sprites.png") no-repeat -271px -646px; } +.detail-group .main-groups table td.value > span.copypasteenabledvalue { + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.detail-group .main-groups table td.value > .copypasteactive { + display: auto; + white-space: nowrap; + overflow: none; +} + +div.copypasteicon { + background: url("../images/sprites.png") no-repeat -271px -65px; + float: left; + height: 21px; + margin-left: 6px; + margin-top: 0px; + width: 18px; +} + +div.copypasteicon:hover { + background: url("../images/sprites.png") no-repeat -271px -646px; +} + +.detail-group .main-groups table td.value > span.copypasteenabledvalue { + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.detail-group .main-groups table td.value > .copypasteactive { + display: auto; + white-space: nowrap; + overflow: none; +} + +div.copypasteicon { + background: url("../images/sprites.png") no-repeat -271px -65px; + float: left; + height: 21px; + margin-left: 6px; + margin-top: 0px; + width: 18px; +} + +div.copypasteicon:hover { + background: url("../images/sprites.png") no-repeat -271px -646px; +} + .detail-group .main-groups table td.value > span select { width: 100% !important; } @@ -2881,6 +2933,15 @@ div.detail-group.actions td { background-position: -189px -690px; } +#navigation ul li.loadbalancer span.icon { + background: url(../images/lbsection.png) no-repeat 2px 7px; +} + +#navigation ul li.loadbalancer.active span.icon, +#navigation ul li.loadbalancer:hover span.icon { + background: url(../images/lbsectionactive.png) no-repeat 2px 7px; +} + #navigation ul li.templates span.icon { background-position: -233px -21px; } @@ -3241,6 +3302,49 @@ div.toolbar div.button.main-action, height: 12px; } +div.toolbar div.button.rm_custom, +div.toolbar div.button.refresh, +div.toolbar div.button.rm_custom, +div.toolbar div.button.main-action, +.toolbar div.button.header-action, +.detail-group .button.rm_custom { + /*+placement:shift 0px 5px;*/ + position: relative; + left: 0px; + top: 5px; + background: #EAEAEA; + font-size: 12px; + font-weight: 100; + color: #000000; + margin: 0 14px 0 0; + cursor: pointer; + /*+text-shadow:0px 1px 1px #DEE5EA;*/ + -moz-text-shadow: 0px 1px 1px #DEE5EA; + -webkit-text-shadow: 0px 1px 1px #DEE5EA; + -o-text-shadow: 0px 1px 1px #DEE5EA; + text-shadow: 0px 1px 1px #DEE5EA; + padding: 5px 7px 5px 6px; + 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 ); + border: 1px solid #B7B7B7; + float: right; + /*+border-radius:4px;*/ + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + border-radius: 4px 4px 4px 4px; + height: 12px; +} + div.toolbar div.button.add:hover, div.toolbar div.button.refresh:hover, div.toolbar div.button.main-action:hover, @@ -8372,6 +8476,18 @@ div.container div.panel div#details-tab-addloadBalancer.detail-group div.loadBal margin: 12px 0 0; } +.multi-edit .data .data-body .data-item.nodata { + height: 28px; + border: 1px solid #DDDDDD; +} + +.multi-edit .data .data-body .data-item.nodata .label { + color: #808080; + font-size: 12px; + text-align: center; + margin: 12px 0 0; +} + .multi-edit .data .data-body .data-item table { margin: 0; border: none; @@ -10482,6 +10598,40 @@ div.container div.panel div#details-tab-addloadBalancer.detail-group div.loadBal border: 1px solid #C7C7C7; } +.new-project .field input[type=checkbox] { + background: #FFFFFF 0px 7px; + font-size: 14px; + width: 506px; + height: 20px; + margin: 17px 25px 0 0; + float: right; +} + +.new-project .field select { + background: #FFFFFF 0px 7px; + /*+border-radius:5px;*/ + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -khtml-border-radius: 5px; + border-radius: 5px; + border-radius: 5px 5px 5px 5px; + font-size: 14px; + border: 1px solid #E2E1DF; + /*+box-shadow:inset 0px 1px #A1A1A1;*/ + -moz-box-shadow: inset 0px 1px #A1A1A1; + -webkit-box-shadow: inset 0px 1px #A1A1A1; + -o-box-shadow: inset 0px 1px #A1A1A1; + box-shadow: inset 0px 1px #A1A1A1; + -moz-box-shadow: inset 0px 1px 0px #A1A1A1; + -webkit-box-shadow: inset 0px 1px 0px #A1A1A1; + -o-box-shadow: inset 0px 1px 0px #A1A1A1; + width: 506px; + height: 20px; + margin: 17px 25px 0 0; + float: right; + border: 1px solid #C7C7C7; +} + .new-project .resources .field input[type=text] { margin: 6px 9px 0 0; } @@ -10934,7 +11084,7 @@ div.container div.panel div#details-tab-addloadBalancer.detail-group div.loadBal overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - margin-left: 6px; + margin-left: 15px; } .tagger ul li span.remove { @@ -11668,6 +11818,26 @@ div.container div.panel div#details-tab-network.detail-group div div.multi-edit float: left; } +div.ui-dialog div.autoscaler div.networks { + margin-top: 25px; +} + +div.ui-dialog div.autoscaler div.networks-title div.form-container { + height: 55px; +} + +div.ui-dialog div.autoscaler div.networks div.multi-edit { + margin-top: 0px; +} + +div.ui-dialog div.autoscaler .networks .multi-edit th.add-network { + width: 12%; +} + +div.ui-dialog div.autoscaler .networks .multi-edit td.multi-actions { + width: 12%; +} + div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container { height: 55px; } @@ -11686,13 +11856,11 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit { div.ui-dialog div.autoscaler div.scale-up-policy-title { color: #0055BB; - margin-left: -650px; margin-top: 40px; } div.ui-dialog div.autoscaler div.scale-up-policy-title label { font-size: 13px; - margin-left: 200px; margin-right: 10px; } @@ -11720,51 +11888,38 @@ div.ui-dialog div.autoscaler div.field-group.bottom-fields hr.policy-divider { div.ui-dialog div.autoscaler div.scale-down-policy-title label { font-size: 13px; - margin-left: 170px; margin-right: 10px; } div.ui-dialog div.autoscaler div.scale-down-policy-title { color: #0055BB; - margin-left: -620px; margin-top: 10px; } div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.value input[type=text] { - margin-left: 729px; - width: 30%; + width: 49%; margin-top: -17px; } -div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.name { - margin-left: 420px; -} - div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.value input[type=text] { - margin-left: 698px; - width: 30%; - margin-top: -16px; -} - -div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.name { - margin-left: 420px; + width: 49%; } div.ui-dialog div.autoscaler div.scale-up-policy div.multi-edit div.data div.data-body div.data-item { margin-bottom: 0px; - margin-right: 22px; + margin-right: 0px; } div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit div.data div.data-body div.data-item { margin-bottom: 0px; - margin-right: 22px; + margin-right: 0px; } div.ui-dialog div.autoscaler div.scale-up-policy div.slide-label { color: #A5A3A7; font-size: 14px; margin-bottom: 3px; - margin-left: 755px; + margin-left: 715px; width: 12px; } @@ -11772,7 +11927,15 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.slide-label { color: #A5A3A7; font-size: 14px; margin-bottom: 3px; - margin-left: 755px; + margin-left: 715px; + width: 12px; +} + +div.ui-dialog div.autoscaler div.networks div.slide-label { + color: #A5A3A7; + font-size: 14px; + margin-bottom: 3px; + margin-left: 715px; width: 12px; } @@ -11783,7 +11946,7 @@ div.ui-dialog div.autoscaler div.scale-up-policy div.hide { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -11794,10 +11957,20 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.hide { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } +div.ui-dialog div.autoscaler div.networks div.hide { + background: #FFFFFF url("../images/minus.png") no-repeat 31% 54%; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 75px 0 11px; + width: 14px; +} div.ui-dialog div.autoscaler div.scale-up-policy div.expand { background: #FFFFFF url("../images/sprites.png") repeat -541px -499px; border: 1px solid #D0D0D0; @@ -11805,7 +11978,7 @@ div.ui-dialog div.autoscaler div.scale-up-policy div.expand { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -11816,10 +11989,20 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.expand { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } +div.ui-dialog div.autoscaler div.networks div.expand { + background: #FFFFFF url("../images/sprites.png") repeat -541px -499px; + border: 1px solid #D0D0D0; + border-radius: 9px 9px 9px 9px; + cursor: pointer; + float: right; + height: 15px; + margin: -20px 75px 0 11px; + width: 14px; +} div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-item div.name label { font-size: 11px; } @@ -11945,13 +12128,11 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit { div.ui-dialog div.autoscaler div.scale-up-policy-title { color: #0055BB; - margin-left: -650px; - margin-top: 40px; + margin-top: 15px; } div.ui-dialog div.autoscaler div.scale-up-policy-title label { font-size: 13px; - margin-left: 200px; margin-right: 10px; } @@ -11979,51 +12160,38 @@ div.ui-dialog div.autoscaler div.field-group.bottom-fields hr.policy-divider { div.ui-dialog div.autoscaler div.scale-down-policy-title label { font-size: 13px; - margin-left: 170px; margin-right: 10px; } div.ui-dialog div.autoscaler div.scale-down-policy-title { color: #0055BB; - margin-left: -620px; margin-top: 10px; } div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.value input[type=text] { - margin-left: 195px; - width: 30%; + width: 49%; margin-top: 1px; } -div.ui-dialog div.autoscaler div.scale-up-policy-title div.form-container div.form-item div.name { - margin-left: 390px; -} - div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.value input[type=text] { - margin-left: 670px; - width: 30%; - margin-top: -16px; -} - -div.ui-dialog div.autoscaler div.scale-down-policy-title div.form-container div.form-item div.name { - margin-left: 390px; + width: 49%; } div.ui-dialog div.autoscaler div.scale-up-policy div.multi-edit div.data div.data-body div.data-item { margin-bottom: 0px; - margin-right: 22px; + margin-right: 0px; } div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit div.data div.data-body div.data-item { margin-bottom: 0px; - margin-right: 22px; + margin-right: 0px; } div.ui-dialog div.autoscaler div.scale-up-policy div.slide-label { color: #A5A3A7; font-size: 14px; margin-bottom: 3px; - margin-left: 755px; + margin-left: 715px; width: 12px; } @@ -12031,7 +12199,7 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.slide-label { color: #A5A3A7; font-size: 14px; margin-bottom: 3px; - margin-left: 755px; + margin-left: 715px; width: 12px; } @@ -12042,7 +12210,7 @@ div.ui-dialog div.autoscaler div.scale-up-policy div.hide { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -12053,7 +12221,7 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.hide { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -12064,7 +12232,7 @@ div.ui-dialog div.autoscaler div.scale-up-policy div.expand { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -12075,7 +12243,7 @@ div.ui-dialog div.autoscaler div.scale-down-policy div.expand { cursor: pointer; float: right; height: 15px; - margin: -20px 45px 0 11px; + margin: -20px 75px 0 11px; width: 14px; } @@ -12594,6 +12762,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .attach:hover .icon, .attachISO:hover .icon, +.linkloadbalancer .icon, .attachDisk:hover .icon { background-position: -101px -585px; } @@ -12601,6 +12770,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it .detach .icon, .detachISO .icon, .detachDisk .icon, +.unlinkloadbalancer .icon, .disassociateProfileFromBlade .icon { background-position: -101px -65px; } @@ -13360,3 +13530,17 @@ div.panel.copy-template-destination-list div.list-view div.fixed-header{ left: 310px; } +div.toolbar div.button.rm_custom span.icon{ + background: url(../images/sprites.png) -6px -93px; +} +div.toolbar div.button.add_custom span.icon{ + background: url(../images/sprites.png) -35px -63px; +} + +.rootAdminAddL2Network{ + display: none; +} + +.rootAdminAddGuestNetwork{ + display: none; +} \ No newline at end of file diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp new file mode 100644 index 000000000000..038999ae44a8 --- /dev/null +++ b/ui/dictionary.jsp @@ -0,0 +1,1046 @@ +<%-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> + + + + + +<%-- +**** +NOTE +**** + +Please use dictionary2.jsp for all new mappings. This is due to +file size constraints for JSP files. + +If you add anything else to this file, an error might occur at runtime! +--%> + +<% long now = System.currentTimeMillis(); %> + diff --git a/ui/dictionary2.jsp b/ui/dictionary2.jsp new file mode 100644 index 000000000000..bc4a30368b2e --- /dev/null +++ b/ui/dictionary2.jsp @@ -0,0 +1,1041 @@ +<%-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> + + + + +<% long now = System.currentTimeMillis(); %> + \ No newline at end of file diff --git a/ui/images/lbsection.png b/ui/images/lbsection.png new file mode 100644 index 000000000000..c7f4949320da Binary files /dev/null and b/ui/images/lbsection.png differ diff --git a/ui/images/lbsectionactive.png b/ui/images/lbsectionactive.png new file mode 100644 index 000000000000..b5bb5e64364b Binary files /dev/null and b/ui/images/lbsectionactive.png differ diff --git a/ui/index.html b/ui/index.html index 5003f00b369b..221ada547be1 100644 --- a/ui/index.html +++ b/ui/index.html @@ -1833,6 +1833,9 @@

+ + + @@ -1879,6 +1882,11 @@

+ + + + + diff --git a/ui/l10n/en.js b/ui/l10n/en.js index 86a860616e0b..4d74bf1321f9 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -2351,4 +2351,37 @@ var dictionary = {"ICMP.code":"ICMP Code", "state.detached":"Detached", "title.upload.volume":"Upload Volume", "ui.listView.filters.all":"All", -"ui.listView.filters.mine":"Mine"}; +"ui.listView.filters.mine":"Mine", +"label.action.link.loadbalancer.processing": "Linking Load Balancer", +"label.action.link.loadbalancer": "Link Load Balancer", +"label.action.registry.dns.for.load.balancer": "Please confirm that you want to register DNS for this Load Balancer.", +"label.action.registry.dns.for.vm.nic": "Please confirm that you want to register DNS for this NIC.", +"label.action.unlink.loadbalancer.processing": "Unlinking load balancer...", +"label.action.unlink.loadbalancer": "Unlink load balancer", +"label.add.globo.aclapi": "Add Globo ACL API", +"label.add.globo.dns": "Add GloboDNS", +"label.fqdn": "FQDN", +"label.globo.aclapi.configuration": "Globo ACL API Configuration", +"label.globo.aclapi": "Globo ACL API", +"label.globo.dns.configuration": "GloboDNS Configuration", +"label.globo.dns": "GloboDNS", +"label.globo.network.lbmethod": "Load Balancer Method", +"label.lb.algorithm.leastconn": "Least connections", +"label.lb.algorithm.roundrobin": "Round-robin", +"label.lb.algorithm.source": "Source", +"label.max.hosts.supported": "Max hosts supported", +"label.project.businessservice": "Business Service", +"label.project.client": "Client", +"label.project.component": "Component", +"label.project.detailedusage": "Detailed Usage", +"label.project.product": "Product", +"label.project.subcomponent": "Sub-component", +"label.scale.logicaloperator": "Logical Operator", +"label.scaledown.step": "Scale down step", +"label.scaleup.step": "Scale up step", +"label.vm.ip": "VM IP Address", +"message.action.link.loadbalancer": "Link Load balancer with another lb will destroy pools and associate lb with second lb\\'s pools. This operation needs to redeploy VIP. Are you sure want to link load balancer?", +"message.action.unlink.loadbalancer": "Unlink Load balancer from another will remove pools and create new ones. This operation needs to redeploy VIP. Are you sure want to unlink load balancer?", +"message.registry.dns.for.load.balancer.successful": "DNS succesfully registered for Load Balancer.", +"message.registry.dns.for.vm.nic.successful": "DNS succesfully registered for NIC." +}; diff --git a/ui/l10n/pt_BR.js b/ui/l10n/pt_BR.js index ccfa59ae7d35..70cebb54ec1e 100644 --- a/ui/l10n/pt_BR.js +++ b/ui/l10n/pt_BR.js @@ -2293,5 +2293,18 @@ var dictionary = { "state.detached": "Desanexado", "title.upload.volume": "Upload Volume", "ui.listView.filters.all": "Todos", - "ui.listView.filters.mine": "Meus" + "ui.listView.filters.mine": "Meus", + "label.lb.algorithm.leastconn": "Least connections", + "label.lb.algorithm.roundrobin": "Round-robin", + "label.lb.algorithm.source": "Origem", + "label.max.hosts.supported": "M\u00E1ximo de hosts suportados", + "label.project.businessservice": "Servi\u00e7o de Neg\u00f3cio", + "label.project.client": "Cliente", + "label.project.component": "Componente", + "label.project.detailedusage": "Consumo detalhado", + "label.project.product": "Produto", + "label.project.subcomponent": "Sub-componente", + "label.scale.logicaloperator": "Operador L\u00f3gico", + "label.scaledown.step": "Passo do scale down", + "label.scaleup.step": "Passo do scale up" }; diff --git a/ui/plugins/globoNetworkVipPlugin/config.js b/ui/plugins/globoNetworkVipPlugin/config.js new file mode 100644 index 000000000000..0de8783619fb --- /dev/null +++ b/ui/plugins/globoNetworkVipPlugin/config.js @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +(function (cloudStack) { + cloudStack.plugins.globoNetworkVipPlugin.config = { + title: 'GloboNetwork VIPs', + desc: 'Manager for VIPs in GloboNetwork', + externalLink: '', + authorName: 'Globo.com', + authorEmail: '' + }; +}(cloudStack)); diff --git a/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.css b/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.css new file mode 100644 index 000000000000..f26216fadb4b --- /dev/null +++ b/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.css @@ -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. +*/ + +/* Put your CSS here */ diff --git a/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.js b/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.js new file mode 100644 index 000000000000..9cf063860bbe --- /dev/null +++ b/ui/plugins/globoNetworkVipPlugin/globoNetworkVipPlugin.js @@ -0,0 +1,178 @@ +(function (cloudStack) { + + cloudStack.plugins.globoNetworkVipPlugin = function(plugin) { + plugin.ui.addSection({ + id: 'globoNetworkVipPlugin', + title: 'GloboNetwork VIPs', + preFilter: function(args) { + return true; // isAdmin(); + }, + listView: { + id: 'vips', + fields: { + name: { label: 'label.name' }, + ip: { label: 'IP' }, + }, + dataProvider: function(args) { + plugin.ui.apiCall('listGloboNetworkVips', { + success: function(json) { + var vips = json.listglobonetworkvipsresponse.globonetworkvip || []; + vips.forEach(function(vip) { + vip.ports = vip.ports.join(", "); + }); + args.response.success({ data: vips }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + detailView: { + name: 'VIP details', + isMaximized: true, + noCompact: true, + tabs: { + details: { + title: 'label.details', + fields: [{ + id: { + label: 'label.id' + }, + name: { + label: 'label.name' + }, + ip: { + label: 'IP' + }, + cache: { + label: 'Cache' + }, + method: { + label: 'Balancing method' + }, + persistence: { + label: 'Persistence' + }, + healthchecktype: { + label: 'Healthcheck Type' + }, + healthcheck: { + label: 'Healthcheck' + }, + maxconn: { + label: 'Max Connections' + }, + ports: { + label: 'Ports' + }, + }], + dataProvider: function(args) { + args.response.success({ data: args.jsonObj }); + } + }, + reals: { + title: 'Reals', + listView: { + id: 'reals', + fields: { + vmname: { label: 'VM' }, + ip: { label: 'IP' }, + network: { label: 'Network' }, + ports: { label: 'Ports' }, + }, + dataProvider: function(args) { + plugin.ui.apiCall('listGloboNetworkReals', { + data: { + vipid: args.context.vips[0].id, + }, + success: function(json) { + var reals = json.listglobonetworkrealsresponse.globonetworkreal || []; + args.response.success({ data: reals }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + actions: { + add: { + label: 'Add Real', + action: function(args) { + return;; + }, + messages: { + confirm: function(args) { + return 'This action is no longer supported. You should use the Load Balancer menu instead.'; + }, + notification: function(args) { + return 'Real added successfully'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + remove: { + label: 'label.remove', + action: function(args) { + return; + }, + messages: { + confirm: function(args) { + return 'This action is no longer supported. You should use the Load Balancer menu instead.'; + }, + notification: function(args) { + return 'Real removed successfully'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + } + }, + }, + }, + actions: { + remove: { + label: 'label.remove', + messages: { + confirm: function(args) { + return 'This action is no longer supported. You should use the Load Balancer menu instead.'; + }, + notification: function(args) { + return 'Remove GloboNetwork VIP'; + } + }, + action: function(args) { + return; + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + add: { + label: 'Create new VIP', + action: function(args) { + return; + }, + messages: { + confirm: function(args) { + return 'This action is no longer supported. You should use the Load Balancer menu instead.'; + }, + notification: function(args) { + return 'Vip created'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + } + }); + }; +}(cloudStack)); \ No newline at end of file diff --git a/ui/plugins/globoNetworkVipPlugin/icon.png b/ui/plugins/globoNetworkVipPlugin/icon.png new file mode 100644 index 000000000000..a313d35328c4 Binary files /dev/null and b/ui/plugins/globoNetworkVipPlugin/icon.png differ diff --git a/ui/plugins/plugins.js b/ui/plugins/plugins.js index 6edfe88fe1d7..1344b261b99f 100644 --- a/ui/plugins/plugins.js +++ b/ui/plugins/plugins.js @@ -16,6 +16,7 @@ // under the License. (function($, cloudStack) { cloudStack.plugins = [ + 'globoNetworkVipPlugin', //'testPlugin', 'cloudian', 'quota' diff --git a/ui/scripts/autoscaler.js b/ui/scripts/autoscaler.js index e8ac0443662c..ab651af9b6f5 100644 --- a/ui/scripts/autoscaler.js +++ b/ui/scripts/autoscaler.js @@ -20,6 +20,8 @@ var totalScaleUpCondition = 0; var scaleDownData = []; var totalScaleDownCondition = 0; + var networksData = [] + var totalNetwork = 0; cloudStack.autoscaler = { // UI actions to appear in dialog @@ -106,6 +108,8 @@ totalScaleUpCondition = 0; scaleDownData = []; totalScaleDownCondition = 0; + networksData = [] + totalNetwork = 0; if (!('multiRules' in args.context)) { //from a new LB args.response.success({ @@ -119,7 +123,16 @@ lbruleid: args.context.multiRules[0].id }, success: function(json) { - var autoscaleVmGroup = json.listautoscalevmgroupsresponse.autoscalevmgroup[0]; + var response = json.listautoscalevmgroupsresponse.autoscalevmgroup; + if (!response || !response[0]) { + // Existing LB, but no configured VM Group yet + args.response.success({ + data: null + }); + return; + } + + var autoscaleVmGroup = response[0]; $.ajax({ url: createURL('listAutoScaleVmProfiles'), @@ -133,6 +146,8 @@ var scaleUpPolicy = { id: autoscaleVmGroup.scaleuppolicies[0].id, duration: autoscaleVmGroup.scaleuppolicies[0].duration, + step: autoscaleVmGroup.scaleuppolicies[0].step, + logicaloperator: autoscaleVmGroup.scaleuppolicies[0].logicaloperator, conditions: [] }; $(autoscaleVmGroup.scaleuppolicies[0].conditions).each(function() { @@ -148,6 +163,8 @@ var scaleDownPolicy = { id: autoscaleVmGroup.scaledownpolicies[0].id, duration: autoscaleVmGroup.scaledownpolicies[0].duration, + step: autoscaleVmGroup.scaledownpolicies[0].step, + logicaloperator: autoscaleVmGroup.scaledownpolicies[0].logicaloperator, conditions: [] }; $(autoscaleVmGroup.scaledownpolicies[0].conditions).each(function() { @@ -172,15 +189,16 @@ securityGroups = array2[1]; }); } - var originalAutoscaleData = { templateNames: autoscaleVmProfile.templateid, serviceOfferingId: autoscaleVmProfile.serviceofferingid, minInstance: autoscaleVmGroup.minmembers, maxInstance: autoscaleVmGroup.maxmembers, + userData: autoscaleVmProfile.userdata, scaleUpPolicy: scaleUpPolicy, scaleDownPolicy: scaleDownPolicy, interval: autoscaleVmGroup.interval, + autoScaleVmGroupName: autoscaleVmGroup.autoscalegroupvmprefixname, quietTime: autoscaleVmGroup.scaleuppolicies[0].quiettime, destroyVMgracePeriod: autoscaleVmProfile.destroyvmgraceperiod, securityGroups: securityGroups, @@ -294,6 +312,24 @@ } }); + $.ajax({ + url: createURL('listTemplates'), + data: { + templatefilter: 'shared', + zoneid: args.context.networks[0].zoneid + }, + async: false, + success: function(json) { + var items = json.listtemplatesresponse.template; + $(items).each(function() { + if (!(this.id in templateIdMap)) { + templates.push(this); + templateIdMap[this.id] = 1; + } + }); + } + }); + args.response.success({ data: $.map(templates, function(template) { return { @@ -306,7 +342,7 @@ }, serviceOfferingId: { - label: 'label.compute.offering', + label: 'label.menu.service.offerings', select: function(args) { $.ajax({ url: createURL("listServiceOfferings&issystem=false"), @@ -329,7 +365,8 @@ minInstance: { label: 'label.min.instances', - defaultValue: '3', + defaultValue: '1', + docID: 'helpAutoscaleMinInstance', validation: { required: true, number: true @@ -338,11 +375,26 @@ maxInstance: { label: 'label.max.instances', - defaultValue: '10', + defaultValue: '5', + docID: 'helpAutoscaleMaxInstance', validation: { required: true, number: true } + }, + + autoScaleVmGroupName: { + label: 'label.autoscale.vm.group.name', + defaultValue: 'as-vm', + docID: 'helpAutoscaleVmGroupName', + validation: { + required: true + } + }, + + userData: { + label: 'label.user.data', + docID: 'helpAutoscaleUserData' } }, @@ -354,6 +406,7 @@ interval: { label: 'label.polling.interval.sec', defaultValue: '30', + docID: 'helpAutoscalePollingInterval', validation: { required: true, number: true @@ -363,6 +416,7 @@ quietTime: { label: 'label.quiet.time.sec', defaultValue: '300', + docID: 'helpAutoscaleQuietTime', validation: { required: true, number: true @@ -372,6 +426,7 @@ destroyVMgracePeriod: { label: 'label.destroy.vm.graceperiod', defaultValue: '30', + docID: 'helpAutoscaleVMGracePeriod', isHidden: true, dependsOn: 'isAdvanced', validation: { @@ -379,86 +434,86 @@ number: true } }, - securityGroups: { - label: 'label.menu.security.groups', - isHidden: true, - dependsOn: 'isAdvanced', - select: function(args) { - $.ajax({ - url: createURL("listSecurityGroups&listAll=true"), - dataType: "json", - async: true, - success: function(json) { - var securitygroups = json.listsecuritygroupsresponse.securitygroup; - var items = []; - items.push({ - id: "", - description: "" - }); - $(securitygroups).each(function() { - items.push({ - id: this.id, - description: this.name - }); - }); - args.response.success({ - data: items - }); - } - }); - } - }, + // securityGroups: { + // label: 'label.menu.security.groups', + // isHidden: true, + // dependsOn: 'isAdvanced', + // select: function(args) { + // $.ajax({ + // url: createURL("listSecurityGroups&listAll=true"), + // dataType: "json", + // async: true, + // success: function(json) { + // var securitygroups = json.listsecuritygroupsresponse.securitygroup; + // var items = []; + // items.push({ + // id: "", + // description: "" + // }); + // $(securitygroups).each(function() { + // items.push({ + // id: this.id, + // description: this.name + // }); + // }); + // args.response.success({ + // data: items + // }); + // } + // }); + // } + // }, - diskOfferingId: { - label: 'label.menu.disk.offerings', - isHidden: true, - dependsOn: 'isAdvanced', - select: function(args) { - $.ajax({ - url: createURL("listDiskOfferings&listAll=true"), - dataType: "json", - async: true, - success: function(json) { - var diskofferings = json.listdiskofferingsresponse.diskoffering; - var items = []; - items.push({ - id: "", - description: "" - }); - $(diskofferings).each(function() { - items.push({ - id: this.id, - description: this.name - }); - }); - args.response.success({ - data: items - }); - } - }); - } - }, + // diskOfferingId: { + // label: 'label.menu.disk.offerings', + // isHidden: true, + // dependsOn: 'isAdvanced', + // select: function(args) { + // $.ajax({ + // url: createURL("listDiskOfferings&listAll=true"), + // dataType: "json", + // async: true, + // success: function(json) { + // var diskofferings = json.listdiskofferingsresponse.diskoffering; + // var items = []; + // items.push({ + // id: "", + // description: "" + // }); + // $(diskofferings).each(function() { + // items.push({ + // id: this.id, + // description: this.name + // }); + // }); + // args.response.success({ + // data: items + // }); + // } + // }); + // } + // }, - snmpCommunity: { - isHidden: true, - dependsOn: 'isAdvanced', - label: 'label.SNMP.community', - defaultValue: 'public', - validation: { - required: true - } - }, + // snmpCommunity: { + // isHidden: true, + // dependsOn: 'isAdvanced', + // label: 'label.SNMP.community', + // defaultValue: 'public', + // validation: { + // required: true + // } + // }, - snmpPort: { - isHidden: true, - dependsOn: 'isAdvanced', - label: 'label.SNMP.port', - defaultValue: '161', - validation: { - required: true, - number: true - } - }, + // snmpPort: { + // isHidden: true, + // dependsOn: 'isAdvanced', + // label: 'label.SNMP.port', + // defaultValue: '161', + // validation: { + // required: true, + // number: true + // } + // }, username: { isHidden: true, @@ -530,6 +585,7 @@ } } }, + scaleUpPolicy: { title: 'label.scaleup.policy', label: 'label.scale.up.policy', @@ -736,13 +792,98 @@ data: scaleDownData }); } + }, + + networks: { + label: 'Additional networks', + noSelect: true, + noHeaderActionsColumn: true, + ignoreEmptyFields: true, + fields: { + networkid: { + label: 'Additional Networks', + docID: 'helpAutoscaleProfileNetworks', + select: function(args) { + $.ajax({ + url: createURL("listNetworks"), + dataType: "json", + async: false, + success: function(json) { + var networks = json.listnetworksresponse.network; + + var networksMaps = [] + $.each(networks, function() { + if ( this.id != args.context.loadbalancer.networkid ) { + networksMaps.push({ + name: this.id, + description: this.name + " - " + this.zonename + " - " + this.cidr + }); + } + }); + + args.response.success({data: networksMaps}); + } + }); + } + }, + 'add-network': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + var result = $.grep(networksData, function(e){ return e.networkid == args.data.networkid; }); + + if(result.length == 0){ + networksData.push($.extend(args.data, { + index: totalNetwork + })); + + totalNetwork++; + } + args.response.success(); + } + }, + actions: { + destroy: { + label: '', + action: function(args) { + networksData = $.grep(networksData, function(item) { + return item.index != args.context.multiRule[0].index; + }); + totalNetwork--; + args.response.success(); + } + } + }, + dataProvider: function(args) { + var data = networksData; + var $autoscaler = $('.ui-dialog .autoscaler'); + var initialData = $autoscaler.data('autoscaler-networks-data'); + + if ($.isArray(initialData)) { + $(initialData).each(function() { + this.index = totalNetwork; + totalNetwork++; + networksData.push(this); + }); + + $autoscaler.data('autoscaler-networks-data', null); + } + + args.response.success({ + data: networksData + }); + } } }, actions: { apply: function(args) { //validation (begin) ***** - if (!('multiRules' in args.context)) { //from a new LB + if (!('multiRules' in args.context) || !args.context.originalAutoscaleData) { //from a new LB or from existing LB without AutoscaleVmGroup if (args.formData.name == '' || args.formData.publicport == '' || args.formData.privateport == '') { args.response.error('Name, Public Port, Private Port of Load Balancing are required. Please close this dialog box and fill Name, Public Port, Private Port first.'); return; @@ -770,7 +911,7 @@ }, async: false, success: function(json) { - if (json.listusersresponse.user[0].apikey != null && json.listusersresponse.user[0].secretkey != null) { + if (json.listusersresponse.user[0].apikey != null) { havingApiKeyAndSecretKey = true; } } @@ -812,6 +953,10 @@ args.response.error("Duration of Scale Up Policy should be a number."); return; } + if (args.data.scaleUpStep == "" || isNaN(args.data.scaleUpStep)) { + args.response.error("Scale up step should be a number."); + return; + } if (parseInt(args.data.scaleUpDuration) < parseInt(args.data.interval)) { args.response.error("Duration of Scale Up Policy must be greater than or equal to Polling Interval."); return; @@ -830,6 +975,10 @@ args.response.error("Duration of Scale Down Policy should be a number."); return; } + if (args.data.scaleDownStep == "" || isNaN(args.data.scaleDownStep)) { + args.response.error("Scale down step should be a number."); + return; + } if (parseInt(args.data.scaleDownDuration) < parseInt(args.data.interval)) { args.response.error("Duration of Scale Down Policy must be greater than or equal to Polling Interval."); return; @@ -838,6 +987,22 @@ args.response.error("At least one condition is required in Scale Down Policy."); return; } + + if(args.data.userData.length > 32768){ + args.response.error("User data should be only 30k long."); + return; + } + + if(args.data.userData.length == 1){ + args.response.error("User data is too short."); + return; + } + + var base64Matcher = new RegExp("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$"); + if (args.data.userData.length > 0 && !base64Matcher.test(args.data.userData)) { + args.response.error("User data is not base64 encode."); + return; + } //validation (end) ***** var scaleVmProfileResponse = []; @@ -872,12 +1037,14 @@ var item = json.queryasyncjobresultresponse.jobresult.condition; scaleUpConditionIds.push(item.id); if (scaleUpConditionIds.length == scaleUpData.length) { - if (!('multiRules' in args.context)) { //from a new LB + if (!('multiRules' in args.context) || !args.context.originalAutoscaleData) { //from a new LB or from existing LB without AutoscaleVmGroup var data = { action: 'scaleup', conditionids: scaleUpConditionIds.join(","), duration: args.data.scaleUpDuration, - quiettime: args.data.quietTime + quiettime: args.data.quietTime, + step: args.data.scaleUpStep, + logicaloperator: args.data.scaleUpLogicalOperator }; $.ajax({ url: createURL('createAutoScalePolicy'), @@ -916,7 +1083,9 @@ id: args.context.originalAutoscaleData.scaleUpPolicy.id, conditionids: scaleUpConditionIds.join(","), duration: args.data.scaleUpDuration, - quiettime: args.data.quietTime + quiettime: args.data.quietTime, + step: args.data.scaleUpStep, + logicaloperator: args.data.scaleUpLogicalOperator }; $.ajax({ @@ -1004,12 +1173,14 @@ var item = json.queryasyncjobresultresponse.jobresult.condition; scaleDownConditionIds.push(item.id); if (scaleDownConditionIds.length == scaleDownData.length) { - if (!('multiRules' in args.context)) { //from a new LB + if (!('multiRules' in args.context) || !args.context.originalAutoscaleData) { //from a new LB or from existing LB without AutoscaleVmGroup var data = { action: 'scaledown', conditionids: scaleDownConditionIds.join(","), duration: args.data.scaleDownDuration, - quiettime: args.data.quietTime + quiettime: args.data.quietTime, + step: args.data.scaleDownStep, + logicaloperator: args.data.scaleDownLogicalOperator }; $.ajax({ url: createURL('createAutoScalePolicy'), @@ -1048,7 +1219,9 @@ id: args.context.originalAutoscaleData.scaleDownPolicy.id, conditionids: scaleDownConditionIds.join(","), duration: args.data.scaleDownDuration, - quiettime: args.data.quietTime + quiettime: args.data.quietTime, + step: args.data.scaleDownStep, + logicaloperator: args.data.scaleDownLogicalOperator }; $.ajax({ @@ -1114,7 +1287,7 @@ var createOrUpdateVmProfile = function(args) { var array1 = []; var apiCmd, apiCmdRes; - if (!('multiRules' in args.context)) { //from a new LB + if (!('multiRules' in args.context) || !args.context.originalAutoscaleData) { //from a new LB or from existing LB without AutoscaleVmGroup) { //from a new LB var data = { zoneid: args.context.networks[0].zoneid ? args.context.networks[0].zoneid : args.context.ipAddresses[0].zoneid, //get zoneid from args.context.networks[0]. If it is not null then get it from args.context.ipAddresses[0] because args.context.ipAddresses is null when adding AutoScale rule from Add Load Balancer tab in Network page serviceofferingid: args.data.serviceOfferingId, @@ -1124,11 +1297,23 @@ snmpport: args.data.snmpPort }; + if(args.data.userData != ''){ + $.extend(data, { userdata: args.data.userData }); + } + + var networkIds = []; + $(networksData).each(function(){ + networkIds.push(this.networkid); + }); + $.extend(data, { + networkids: networkIds.join(',') + }); + var allParamNames = $.map(data, function(value, key) { return key; }); - var notParams = ['zoneid', 'serviceofferingid', 'templateid', 'destroyvmgraceperiod']; + var notParams = ['zoneid', 'serviceofferingid', 'templateid', 'destroyvmgraceperiod', 'networkids', 'userdata']; var index = 0; $(allParamNames).each(function() { var param = 'counterparam[' + index + ']'; @@ -1168,6 +1353,7 @@ $.ajax({ url: createURL('createAutoScaleVmProfile'), data: data, + type: "post", success: function(json) { var jobId = json.autoscalevmprofileresponse.jobid; var autoscaleVmProfileTimer = setInterval(function() { @@ -1204,11 +1390,25 @@ snmpport: args.data.snmpPort }; + if(args.data.userData != ''){ + $.extend(data, { userdata: args.data.userData }); + } + + var networkIds = []; + $(networksData).each(function(){ + networkIds.push(this.networkid); + }); + if(networkIds.length > 0){ + $.extend(data, { networkids: networkIds.join(',') }); + }else{ + $.extend(data, { removenetworks: true }); + } + var allParamNames = $.map(data, function(value, key) { return key; }); - var notParams = ['id', 'templateid', 'destroyvmgraceperiod']; + var notParams = ['id', 'templateid', 'destroyvmgraceperiod', 'networkids', 'removenetworks', 'userdata']; var index = 0; $(allParamNames).each(function() { var param = 'counterparam[' + index + ']'; @@ -1223,9 +1423,6 @@ return true; }); - - - if (args.data.username != null && args.data.username.length > 0) { $.extend(data, { autoscaleuserid: args.data.username @@ -1235,6 +1432,7 @@ $.ajax({ url: createURL('updateAutoScaleVmProfile'), data: data, + type: "post", success: function(json) { var jobId = json.updateautoscalevmprofileresponse.jobid; var autoscaleVmProfileTimer = setInterval(function() { @@ -1266,6 +1464,13 @@ }; var loadBalancer = function(args) { + // In case load balancer was already created, but no autoscale options yet + if ('loadbalancer' in args.context) { + loadBalancerResponse = args.context.loadbalancer; + autoScaleVmGroup(args); + return; + } + var networkid; if ('vpc' in args.context) { //from VPC section if (args.data.tier == null) { @@ -1332,7 +1537,7 @@ }; var autoScaleVmGroup = function(args) { - if (!('multiRules' in args.context)) { //from a new LB + if (!('multiRules' in args.context) || !args.context.originalAutoscaleData) { //from a new LB or from existing LB without AutoscaleVmGroup) { //from a new LB var array1 = []; array1.push("&lbruleid=" + loadBalancerResponse.id); array1.push("&minMembers=" + args.data.minInstance); @@ -1341,6 +1546,7 @@ array1.push("&interval=" + args.data.interval); array1.push("&scaleuppolicyids=" + args.scaleUpPolicyResponse.id); array1.push("&scaledownpolicyids=" + args.scaleDownPolicyResponse.id); + array1.push("&autoscalegroupvmprefixname=" + args.data.autoScaleVmGroupName); $.ajax({ url: createURL('createAutoScaleVmGroup' + array1.join("")), @@ -1360,6 +1566,7 @@ clearInterval(scaleVmGroupTimer); if (result.jobstatus == 1) { //autoscale Vm group successfully created scaleVmGroupResponse = result.jobresult.autoscalevmgroup; + $(window).trigger('cloudStack.fullRefresh'); args.response.success(); } else if (result.jobstatus == 2) { args.response.error(_s(result.jobresult.errortext)); @@ -1382,7 +1589,6 @@ scaleuppolicyids: args.context.originalAutoscaleData.scaleUpPolicy.id, scaledownpolicyids: args.context.originalAutoscaleData.scaleDownPolicy.id }; - $.ajax({ url: createURL('updateAutoScaleVmGroup'), data: data, diff --git a/ui/scripts/cloudStack.js b/ui/scripts/cloudStack.js index 119013326fb4..2d7f13e40728 100644 --- a/ui/scripts/cloudStack.js +++ b/ui/scripts/cloudStack.js @@ -22,13 +22,13 @@ var sections = []; if (isAdmin()) { - sections = ["dashboard", "instances", "storage", "network", "templates", "roles", "accounts", "domains", "events", "system", "global-settings", "configuration", "projects", "regions", "affinityGroups"]; + sections = ["dashboard", "instances", "storage", "network", "loadbalancer", "templates", "roles", "accounts", "domains", "events", "system", "global-settings", "configuration", "projects", "regions", "affinityGroups"]; } else if (isDomainAdmin()) { - sections = ["dashboard", "instances", "storage", "network", "templates", "accounts", "domains", "events", "projects", "configuration", "regions", "affinityGroups"]; + sections = ["dashboard", "instances", "storage", "network", "loadbalancer", "templates", "accounts", "domains", "events", "projects", "configuration", "regions", "affinityGroups"]; } else if (g_userProjectsEnabled) { - sections = ["dashboard", "instances", "storage", "network", "templates", "accounts", "events", "projects", "regions", "affinityGroups"]; + sections = ["dashboard", "instances", "storage", "network", "loadbalancer", "templates", "accounts", "events", "projects", "regions", "affinityGroups"]; } else { //normal user - sections = ["dashboard", "instances", "storage", "network", "templates", "accounts", "events", "regions", "affinityGroups"]; + sections = ["dashboard", "instances", "storage", "network", "loadbalancer", "templates", "accounts", "events", "regions", "affinityGroups"]; } $.each(cloudStack.plugins, function(idx, plugin) { @@ -49,6 +49,7 @@ affinityGroups: {}, storage: {}, network: {}, + loadbalancer: {}, templates: {}, events: {}, projects: {}, @@ -72,7 +73,6 @@ /** * Generic error handling */ - $.ajaxSetup({ url: clientApiUrl, async: true, @@ -250,6 +250,7 @@ async: false, success: function(json) { var loginresponse = json.loginresponse; + // sessionkey is recevied as a HttpOnly cookie // therefore reset any g_sessionKey value, an explicit // param in the API call is no longer needed @@ -330,6 +331,7 @@ // Get project configuration // TEMPORARY -- replace w/ output of capability response, etc., once implemented window.g_projectsInviteRequired = false; + }, error: function(XMLHttpRequest) { var errorMsg = parseXMLHttpResponse(XMLHttpRequest); @@ -466,6 +468,106 @@ $('#container').html(''); $('#container').cloudStack(window.cloudStack); }; + }, + // Actual login process, via form + processLogin: function(loginresponse) { + var success; + g_mySession = $.cookie('JSESSIONID'); + g_sessionKey = encodeURIComponent(loginresponse.sessionkey); + g_role = loginresponse.type; + g_username = loginresponse.username; + g_userid = loginresponse.userid; + g_account = loginresponse.account; + g_domainid = loginresponse.domainid; + g_timezone = loginresponse.timezone; + g_timezoneoffset = loginresponse.timezoneoffset; + g_userfullname = loginresponse.firstname + ' ' + loginresponse.lastname; + + $.cookie('sessionKey', g_sessionKey, { + expires: 1 + }); + $.cookie('username', g_username, { + expires: 1 + }); + $.cookie('account', g_account, { + expires: 1 + }); + $.cookie('domainid', g_domainid, { + expires: 1 + }); + $.cookie('role', g_role, { + expires: 1 + }); + $.cookie('timezoneoffset', g_timezoneoffset, { + expires: 1 + }); + $.cookie('timezone', g_timezone, { + expires: 1 + }); + $.cookie('userfullname', g_userfullname, { + expires: 1 + }); + $.cookie('userid', g_userid, { + expires: 1 + }); + + $.ajax({ + url: createURL("listCapabilities"), + dataType: "json", + async: false, + success: function(json) { + success = true; + g_capabilities = json.listcapabilitiesresponse.capability; + $.cookie('capabilities', g_capabilities, { + expires: 1 + }); + + g_supportELB = json.listcapabilitiesresponse.capability.supportELB.toString(); //convert boolean to string if it's boolean + $.cookie('supportELB', g_supportELB, { + expires: 1 + }); + + g_kvmsnapshotenabled = json.listcapabilitiesresponse.capability.kvmsnapshotenabled; //boolean + $.cookie('kvmsnapshotenabled', g_kvmsnapshotenabled, { + expires: 1 + }); + + g_regionsecondaryenabled = json.listcapabilitiesresponse.capability.regionsecondaryenabled; //boolean + $.cookie('regionsecondaryenabled', g_regionsecondaryenabled, { + expires: 1 + }); + + if (json.listcapabilitiesresponse.capability.userpublictemplateenabled != null) { + g_userPublicTemplateEnabled = json.listcapabilitiesresponse.capability.userpublictemplateenabled.toString(); //convert boolean to string if it's boolean + $.cookie('userpublictemplateenabled', g_userPublicTemplateEnabled, { + expires: 1 + }); + } + + g_userProjectsEnabled = json.listcapabilitiesresponse.capability.allowusercreateprojects; + $.cookie('userProjectsEnabled', g_userProjectsEnabled, { + expires: 1 + }); + + g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion; + + if (json.listcapabilitiesresponse.capability.apilimitinterval != null && json.listcapabilitiesresponse.capability.apilimitmax != null) { + var intervalLimit = ((json.listcapabilitiesresponse.capability.apilimitinterval * 1000) / json.listcapabilitiesresponse.capability.apilimitmax) * 3; //multiply 3 to be on safe side + //intervalLimit = 8888; //this line is for testing only, comment it before check in + if (intervalLimit > g_queryAsyncJobResultInterval) + g_queryAsyncJobResultInterval = intervalLimit; + } + }, + error: function(xmlHTTP) { + success = false; + } + }); + + // Get project configuration + // TEMPORARY -- replace w/ output of capability response, etc., once implemented + window.g_projectsInviteRequired = false; + return success; + } }; @@ -483,10 +585,19 @@ }; } - // Localize validation messages + // Localize validation messages cloudStack.localizeValidatorMessages(); + document.title = _l('label.app.name'); - cloudStack.uiCustom.login(loginArgs); + var stopLogin = false; + if ($.urlParam("direct") !== "true") { + // try OAuth2 login + stopLogin = !!cloudStack.OAuth2.login(loginArgs); + } + + if (!stopLogin) { + cloudStack.uiCustom.login(loginArgs); + } document.title = _l('label.app.name'); }); diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index bbe8f3e64b49..54a0a868c30a 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -1326,6 +1326,71 @@ cloudStack.docs = { desc: 'The interval of time in milliseconds to wait on failure before attempting to resend the command to Nuage VSD. Valid values [0 - 10000].', externalLink: '' }, + // Add Load Balancer + helpHealthcheck: { + desc: 'The path for a GET HTTP healthcheck. Optionally, provide full HTTP request (GET or POST). Leave it blank for TCP', + externalLink: '' + }, + helpHealthcheckType: { + desc: 'Healthcheck type used to make the request', + externalLink: '' + }, + helpExpectedHealthcheck: { + desc: 'After request, it is the expected string response to check if service is on/off', + externalLink: '' + }, + helpHostCount: { + desc: 'Max hosts supported in the network, if it is empty, cloudstack will use the default option of the network environment.', + externalLink: '' + }, + helpLbPorts: { + desc: 'List of port mapping for load balancer, e.g. "80:8080,443:8443"', + externalLink: '' + }, + helpAutoscaleMinInstance: { + desc: 'The number of instances in the VM Group will be equal to or more than this number.', + externalLink: '' + }, + helpAutoscaleMaxInstance: { + desc: 'The number of instances in the VM Group will be equal to or less than this number.', + externalLink: '' + }, + helpAutoscalePollingInterval: { + desc: 'The frequency in seconds at which the conditions have to be evaluated', + externalLink: '' + }, + helpAutoscaleQuietTime: { + desc: 'The cool down period in seconds for which the policy should not be evaluated after the action has been taken', + externalLink: '' + }, + helpAutoscaleVMGracePeriod: { + desc: 'The time in seconds allowed for existing connections to get closed before a VM is destroyed', + externalLink: '' + }, + helpAutoscaleDuration: { + desc: 'The duration in seconds for which the conditions have to be true before action is taken', + externalLink: '' + }, + helpAutoscaleStep: { + desc: 'The number of virtual machines that will be created/destroyed on a scale up/down action', + externalLink: '' + }, + helpScaleLogicalOperator: { + desc: 'Logical operator to be used between each of the policy\'s conditions', + externalLink: '' + }, + helpAutoscaleProfileNetworks: { + desc: 'Additional networks (optional) to be added to the created VMs. The default network interface will be the load balancer\'s followed by the networks selected here.', + externalLink: '' + }, + helpAutoscaleUserData: { + desc: 'Binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded. You can send up to 32K of data after base64 encoding.', + externalLink: '' + }, + helpAutoscaleVmGroupName: { + desc: 'The name of the AutoScale group. This name will be prefix of any VM of this group.', + externalLink: '' + }, helpOvm3pool: { desc: 'Pool the Ovm3 nodes in this cluster, required for vm node migrations', externalLink: '' diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js index 1f65ff4bdaed..3824dfbaf395 100644 --- a/ui/scripts/domains.js +++ b/ui/scripts/domains.js @@ -630,7 +630,7 @@ }); $.ajax({ - url: createURL("listProjects&domainid=" + domainObj.id), + url: createURL("listProjects&domainid=" + domainObj.id + "&simple=true"), async: false, data: {}, success: function(json) { diff --git a/ui/scripts/globonetwork.js b/ui/scripts/globonetwork.js new file mode 100644 index 000000000000..5daa213337cc --- /dev/null +++ b/ui/scripts/globonetwork.js @@ -0,0 +1,842 @@ +var globoNetworkAPI; +globoNetworkAPI = globoNetworkAPI || {}; + +(function(globoNetworkAPI, $) { + + // capabilities + var capabilities = null; + + globoNetworkAPI.getCapability = function(name) { + if (!capabilities) { + $.ajax({ + url: createURL('listGloboNetworkCapabilities'), + async: false, + success: function(json) { + capabilities = json.listglobonetworkcapabilitiesresponse.globoNetworkCapability; + } + }); + } + return capabilities[name]; + }; + + // Network Dialog (used in system.js and network.js) + globoNetworkAPI.networkDialog = { + zoneObjs: [], + physicalNetworkObjs: [], + networkOfferingObjs: [], + globoNetworkEnvironmentsObjs: [], + def: { + label: 'Add GloboNetwork', + + messages: { + notification: function(args) { + return 'Add GloboNetwork'; + } + }, + + preFilter: function(args) { + return globoNetworkAPI.getCapability('enabled'); + }, + + createForm: { + title: 'Add GloboNetwork', + + preFilter: function(args) { + if (!globoNetworkAPI.getCapability('supportCustomNetworkDomain')) { + args.$form.find('.form-item[rel=networkdomain]').hide(); + } + $('input[name=isIPv6]:checkbox').change(function(arg){ + var checked = $(this).context.checked + console.log(checked); + var advancedOption = $('input[name=isNetworkAdvanced]'); + + if ( checked ){ + $('.form-item[rel=isNetworkAdvanced]').hide(); + advancedOption.attr('checked', false); + $('.form-item[rel=hostCount]').hide(); + } else { + $('.form-item[rel=isNetworkAdvanced]').show(); + } + }) + }, + + fields: { + name: { + label: 'label.name', + validation: { + required: true + } + }, + description: { + label: 'label.description', + validation: { + required: true + } + }, + + isIPv6: { + label: 'IPv6', + isBoolean: true, + isChecked: false, + onchange: function(args) { + console.log(args) + } + }, + networkdomain: { + label: 'label.network.domain', + docID: 'helpGuestNetworkZoneNetworkDomain' + }, + zoneId: { + label: 'label.zone', + validation: { + required: true + }, + select: function(args) { + if ('zones' in args.context) { + globoNetworkAPI.networkDialog.zoneObjs = args.context.zones; + } else { + $.ajax({ + url: createURL('listZones'), + data: { + networktype: 'Advanced' + }, + async: false, + success: function(json) { + globoNetworkAPI.networkDialog.zoneObjs = []; //reset + var zones = json.listzonesresponse.zone; + if (zones) { + zones.forEach(function(zone) { + globoNetworkAPI.networkDialog.zoneObjs.push(zone); + }); + } + } + }); + } + args.response.success({ + data: $.map(globoNetworkAPI.networkDialog.zoneObjs, function(zone) { + return { + id: zone.id, + description: zone.name + }; + }) + }); + } + }, + napiEnvironmentId: { + label: 'Environment', + dependsOn: 'zoneId', + select: function(args) { + if ('globoNetworkEnvironmentsObjs' in args.context) { + globoNetworkAPI.networkDialog.globoNetworkEnvironmentsObjs = args.context.globoNetworkEnvironmentsObjs; + } else { + var envObjs = []; + if (args.zoneId) { + $.ajax({ + url: createURL('listGloboNetworkEnvironments'), + data: { + zoneid: args.zoneId + }, + async: false, + success: function(json) { + envObjs = json.listglobonetworkenvironmentsresponse.globonetworkenvironment; + } + }); + } + globoNetworkAPI.networkDialog.globoNetworkEnvironmentsObjs = envObjs; + } + args.response.success({ + data: $.map(globoNetworkAPI.networkDialog.globoNetworkEnvironmentsObjs, function(env) { + return { + id: env.napienvironmentid, + description: env.name + }; + }) + }); + } + }, + networkOfferingId: { + label: 'label.network.offering', + dependsOn: ['zoneId', 'scope'], + select: function(args) { + var zoneId = args.zoneId; + var data = { + state: 'Enabled', + zoneid: zoneId + }; + + //Network tab in Guest Traffic Type in Infrastructure menu is only available when it's under Advanced zone. + //zone dropdown in add guest network dialog includes only Advanced zones. + if (args.scope == "zone-wide" || args.scope == "domain-specific") { + $.extend(data, { + guestiptype: 'Shared' + }); + } + + var items = []; + if (zoneId) { + $.ajax({ + url: createURL('listNetworkOfferings'), + data: data, + async: false, + success: function(json) { + globoNetworkAPI.networkDialog.networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; + if (globoNetworkAPI.networkDialog.networkOfferingObjs) { + var selectedZoneObj = {}; + if (globoNetworkAPI.networkDialog.zoneObjs) { + for (var index in globoNetworkAPI.networkDialog.zoneObjs) { + if (globoNetworkAPI.networkDialog.zoneObjs[index].id == zoneId) { + selectedZoneObj = globoNetworkAPI.networkDialog.zoneObjs[index]; + break; + } + } + } + for (var i = 0; i < globoNetworkAPI.networkDialog.networkOfferingObjs.length; i++) { + //for zone-wide network in Advanced SG-enabled zone, list only SG network offerings + if (selectedZoneObj.networktype === 'Advanced' && selectedZoneObj.securitygroupsenabled) { + if (args.scope == "zone-wide") { + var includingSecurityGroup = false; + var serviceObjArray = globoNetworkAPI.networkDialog.networkOfferingObjs[i].service; + for (var k = 0; k < serviceObjArray.length; k++) { + if (serviceObjArray[k].name == "SecurityGroup") { + includingSecurityGroup = true; + break; + } + } + if (!includingSecurityGroup) + continue; //skip to next network offering + } + } + items.push({ + id: globoNetworkAPI.networkDialog.networkOfferingObjs[i].id, + description: globoNetworkAPI.networkDialog.networkOfferingObjs[i].displaytext + }); + } + } + } + }); + } + args.response.success({ + data: items + }); + } + }, + isNetworkAdvanced: { + label: 'label.show.advanced.settings', + isBoolean: true + }, + hostCount: { + label: 'label.max.hosts.supported', + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isNetworkAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + dependsOn: ['isNetworkAdvanced'], + docID: 'helpHostCount', + select: function(args) { + args.response.success({ + data: [ + {id: '', name: '--- Select ---', description: '--- Select ---'}, + {id: '29', name: '2', description: '2'}, + {id: '28', name: '10', description: '10'}, + {id: '27', name: '26', description: '26'}, + {id: '26', name: '58', description: '58'}, + {id: '25', name: '122', description: '122'}, + {id: '24', name: '250', description: '250'} + ] + }) + } + + } + } + }, + + action: function(args) { //Add GloboNetwork network in advanced zone + var $form = args.$form; + + var array1 = []; + array1.push("&zoneId=" + args.data.zoneId); + array1.push("&networkOfferingId=" + args.data.networkOfferingId); + + //Pass physical network ID to addNetworkViaGloboNetworkCmd API only when network offering's guestiptype is Shared. + var selectedNetworkOfferingObj; + if (globoNetworkAPI.networkDialog.networkOfferingObjs) { + for (var index in globoNetworkAPI.networkDialog.networkOfferingObjs) { + if (globoNetworkAPI.networkDialog.networkOfferingObjs[index].id == args.data.networkOfferingId) { + selectedNetworkOfferingObj = globoNetworkAPI.networkDialog.networkOfferingObjs[index]; + break; + } + } + } + + if (selectedNetworkOfferingObj.guestiptype == "Shared") + array1.push("&napienvironmentid=" + args.data.napiEnvironmentId); + + + array1.push("&name=" + todb(args.data.name)); + array1.push("&displayText=" + todb(args.data.description)); + var useIpv6 = (args.data.isIPv6 == "on"); + array1.push("&isipv6=" + useIpv6); + + if (globoNetworkAPI.getCapability('supportCustomNetworkDomain')) { + array1.push("&networkdomain=" + todb(args.data.networkdomain)); + } + + if (args.context.projects && args.context.projects[0].id) { + // Network is created in project, if it's in the context + // Otherwise, create in user's account + array1.push("&projectid=" + args.context.projects[0].id); + } else { + array1.push("&domainId=" + args.context.users[0].domainid); + array1.push("&account=" + args.context.users[0].account); + } + + array1.push("&acltype=account"); + + if ( args.data.hostCount !== '') { + array1.push("&subnet=" + args.data.hostCount); + } + + $.ajax({ + url: createURL("addNetworkViaGloboNetwork" + array1.join(""), { + ignoreProject: true + }), + dataType: "json", + success: function(json) { + var item = json.addnetworkviaglobonetworkresponse.network; + args.response.success({ + data: item + }); + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + } + }; + + + // GloboNetwork provider detail view (used in system.js) + var addGloboNetworkHost = function(args, physicalNetworkObj, apiCmd, apiCmdRes) { + var array1 = []; + array1.push("&physicalnetworkid=" + physicalNetworkObj.id); + array1.push("&username=" + todb(args.data.username)); + array1.push("&password=" + todb(args.data.password)); + array1.push("&url=" + todb(args.data.url)); + + $.ajax({ + url: createURL(apiCmd + array1.join("")), + dataType: "json", + type: "POST", + success: function(json) { + var jid = json[apiCmdRes].jobid; + args.response.success({ + _custom: { + jobId: jid, + } + }); + } + }); + }; + + var addGloboNetworkEnvironment = function(args, physicalNetworkObj, apiCmd, apiCmdRes, apiCmdObj) { + var array1 = []; + array1.push("&physicalnetworkid=" + physicalNetworkObj.id); + array1.push("&napienvironmentid=" + todb(args.data.napiEnvironmentId)); + array1.push("&name=" + todb(args.data.name)); + + $.ajax({ + url: createURL(apiCmd + array1.join("")), + dataType: "json", + type: "POST", + success: function(json) { + var jid = json[apiCmdRes].jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + } + }); + }; + + globoNetworkAPI.provider = function(args) { + var getNspMap = args.getNspMap, + getNspHardcodingArray = args.getNspHardcodingArray, + networkProviderActionFilter = args.networkProviderActionFilter, + refreshNspData = args.refreshNspData, + getSelectedPhysicalNetworkObj = args.getSelectedPhysicalNetworkObj; + + return { + isMaximized: true, + type: 'detailView', + id: 'globoNetworkProvider', + label: 'label.globoNetwork', + tabs: { + details: { + title: 'label.details', + fields: [{ + name: { + label: 'label.name' + }, + state: { + label: 'label.state' + } + }], + dataProvider: function(args) { + refreshNspData("GloboNetwork"); + var providerObj; + $(getNspHardcodingArray()).each(function() { + if (this.id == "GloboNetwork") { + providerObj = this; + return false; //break each loop + } + }); + args.response.success({ + data: providerObj, + actionFilter: networkProviderActionFilter('GloboNetwork') + }); + } + }, + instances: { + title: 'Environments', + listView: { + label: 'Environments', + id: 'napienvironments', + fields: { + name: { + label: 'Local Name' + }, + environmentid: { + label: 'GloboNetwork Environment ID' + } + }, + dataProvider: function(args) { + var filter; + if (args.filterBy.search.value) { + filter = args.filterBy.search.value; + } + + var items = []; + $.ajax({ + url: createURL("listGloboNetworkEnvironments&physicalnetworkid=" + getSelectedPhysicalNetworkObj().id), + success: function(json) { + $(json.listglobonetworkenvironmentsresponse.globonetworkenvironment).each(function() { + if (this.name.match(new RegExp(filter, "i"))) { + items.push({ + name: this.name, + environmentid: this.napienvironmentid, + }); + } + }); + args.response.success({ + data: items + }); + } + }); + }, + actions: { + add: { + label: 'Add Environment', + createForm: { + title: 'Add a GloboNetwork Environment', + fields: { + name: { + label: 'Name', + validation: { + required: true + } + }, + napiEnvironmentId: { + label: 'Environment', + validation: { + required: true + }, + select: function(args) { + $.ajax({ + url: createURL("listAllEnvironmentsFromGloboNetwork&physicalnetworkid=" + getSelectedPhysicalNetworkObj().id), + dataType: "json", + async: false, + success: function(json) { + var items = []; + $(json.listallenvironmentsfromglobonetworkresponse.globonetworkenvironment).each(function() { + items.push({ + id: this.environmentId, + description: this.environmentFullName + }); + }); + args.response.success({ + data: items + }); + } + }); + } + } + } + }, + action: function(args) { + addGloboNetworkEnvironment(args, getSelectedPhysicalNetworkObj(), "addGloboNetworkEnvironment", "addglobonetworkenvironmentresponse", "globonetworkenvironment"); + }, + messages: { + notification: function(args) { + return 'Environment added successfully'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + remove: { + label: 'label.remove', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove environment ' + args.context.napienvironments[0].name + '(' + args.context.napienvironments[0].environmentid + ')?'; + }, + notification: function(args) { + return 'Remove GloboNetwork environment'; + } + }, + action: function(args) { + var physicalnetworkid = args.context.physicalNetworks[0].id; + var environmentid = args.context.napienvironments[0].environmentid; + $.ajax({ + url: createURL("removeGloboNetworkEnvironment&physicalnetworkid=" + physicalnetworkid + "&environmentid=" + environmentid), + dataType: "json", + async: true, + success: function(json) { + args.response.success(); + $(window).trigger('cloudStack.fullRefresh'); + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + }, + detailView: { + tabs: { + lbenvironments: { + title: 'LB Environments', + listView: { + label: 'LB Environments', + id: 'globolbenvironments', + fields: { + name: { + label: 'Local Name' + }, + globonetworklbenvironmentid: { + label: 'Load Balancer Environment ID' + } + }, + dataProvider: function(args) { + var filter; + if (args.filterBy.search.value) { + filter = args.filterBy.search.value; + } + + var items = []; + $.ajax({ + url: createURL("listGloboNetworkLBEnvironments&physicalnetworkid=" + getSelectedPhysicalNetworkObj().id + "&environmentid=" + args.context.napienvironments[0].environmentid), + success: function(json) { + $(json.listglobonetworklbenvironmentsresponse.globonetworklbenvironments).each(function() { + if (this.name.match(new RegExp(filter, "i"))) { + items.push({ + name: this.name, + globonetworklbenvironmentid: this.globonetworklbenvironmentid, + }); + } + }); + args.response.success({ + data: items + }); + } + }); + }, + actions: { + add: { + label: 'Add LB Environment', + createForm: { + title: 'Add LB Environment', + fields: { + name: { + label: 'Name', + validation: { + required: true + } + }, + globolbenvironmentid: { + label: 'LB Environment ID', + validation: { + required: true + } + } + } + }, + action: function(args) { + var physicalnetworkid = getSelectedPhysicalNetworkObj().id; + var environmentid = args.context.napienvironments[0].environmentid; + var globolbenvironmentid = args.data.globolbenvironmentid; + var name = args.data.name; + $.ajax({ + url: createURL("addGloboNetworkLBEnvironment"), + data: { + physicalnetworkid: physicalnetworkid, + napienvironmentid: environmentid, + globolbenvironmentid: globolbenvironmentid, + name: name + }, + dataType: "json", + async: true, + success: function(json) { + args.response.success(); + setTimeout(function() { + $(window).trigger('cloudStack.fullRefresh'); + }, 500); + }, + error: function(errorMessage) { + args.response.error(parseXMLHttpResponse(errorMessage)); + } + }); + }, + messages: { + notification: function(args) { + return 'Add LB Environment'; + } + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + remove: { + label: 'label.remove', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove LB Environment ' + args.context.globolbenvironments[0].name + '(' + args.context.globolbenvironments[0].globonetworklbenvironmentid + ')?'; + }, + notification: function(args) { + return 'Remove Load Balancer Environment'; + } + }, + action: function(args) { + var physicalnetworkid = getSelectedPhysicalNetworkObj().id; + var environmentid = args.context.napienvironments[0].environmentid; + var globolbenvironmentid = args.context.globolbenvironments[0].globonetworklbenvironmentid; + $.ajax({ + url: createURL("removeGloboNetworkLBEnvironment"), + data: { + physicalnetworkid: physicalnetworkid, + napienvironmentid: environmentid, + globolbenvironmentid: globolbenvironmentid + }, + dataType: "json", + success: function(json) { + args.response.success(); + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); + }, + notification: { + poll: function(args) { + args.complete(); + } + } + }, + }, + } + } + } + }, + } + } + }, + actions: { + add: { + label: 'GloboNetwork Configuration', + createForm: { + title: 'GloboNetwork Configuration', + fields: { + username: { + label: 'Username', + validation: { + required: true + } + }, + password: { + label: 'Password', + isPassword: true, + validation: { + required: true + } + }, + url: { + label: 'URL', + validation: { + required: true + } + } + } + }, + action: function(args) { + if (getNspMap()["GloboNetwork"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=GloboNetwork&physicalnetworkid=" + getSelectedPhysicalNetworkObj().id), + dataType: "json", + async: true, + success: function(json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addGloboNetworkProviderIntervalID = 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(addGloboNetworkProviderIntervalID); + if (result.jobstatus == 1) { + getNspMap()["GloboNetwork"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addGloboNetworkHost(args, getSelectedPhysicalNetworkObj(), "addGloboNetworkHost", "addglobonetworkhostresponse"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=GloboNetwork failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=GloboNetwork failed. Error: " + errorMsg); + } + }); + }, g_queryAsyncJobResultInterval); + } + }); + } else { + addGloboNetworkHost(args, getSelectedPhysicalNetworkObj(), "addGloboNetworkHost", "addglobonetworkhostresponse"); + } + }, + messages: { + notification: function(args) { + return 'label.globonetwork.add.device'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + enable: { + label: 'label.enable.provider', + action: function(args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + getNspMap()["GloboNetwork"].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=" + getNspMap()["GloboNetwork"].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=" + getNspMap()["GloboNetwork"].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 + } + } + } + }; + }; +})(globoNetworkAPI, jQuery); \ No newline at end of file diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index a3d735a6860a..419001909312 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -1115,6 +1115,43 @@ jobId: jid, getUpdatedItem: function(json) { var item = json.queryasyncjobresultresponse.jobresult.virtualmachine; + + //Notifying user if some NIC has not the DNS registered properly + for(var i = 0; i < item.nic.length; i++) { + nicHadDnsRegistered = true; + function setNicDncsIsRegistered(result){ + nicHadDnsRegistered = result; + } + + $.ajax({ + url: createURL("getGloboResourceConfiguration"), + data: { + resourceid: item.nic[i].id, + resourcetype: 'VM_NIC', + resourcekey: 'isDNSRegistered' + }, + dataType: "json", + async: false, + success: function(json) { + var conf = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue + if (conf == "false") { + setNicDncsIsRegistered(false); + } else if (conf == "true") { + setNicDncsIsRegistered(true); + } + }, + error: function (errorMessage) { + console.log(errorMessage); + setNicDncsIsRegistered(null); + //args.response.error(errorMessage); + } + }); + + if (nicHadDnsRegistered == false) { + cloudStack.dialog.notice({message: "There are some VM NIC with DNS not registered! Please check each VM NIC details."}); + } + } + if (item.password != null) cloudStack.dialog.notice({ message: "Password of new VM " + item.displayname + " is " + item.password diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 84d60033045d..95261928dc33 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -367,14 +367,19 @@ 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 - }; - }) - }); + var zoneMap = { + data: $.map(zones, + function(zone) { + return { + id: zone.id, + description: zone.name + }; + } + ) + } + zoneMap.data.unshift({ id: null, description: 'All zones'}) + + args.response.success(zoneMap); } }); } @@ -2389,6 +2394,10 @@ }, fields: [{ + id: { + label: 'label.id' + } + }, { displayname: { label: 'label.display.name', isEditable: true @@ -2523,9 +2532,6 @@ }, name: { label: 'label.name' - }, - id: { - label: 'label.id' } }], @@ -2580,6 +2586,98 @@ title: 'label.nics', multiple: true, actions: { + restart: { + label: 'Retry Register DNS for Load Balancer', + custom: { + buttonLabel: 'label.configure' + }, + preFilter: function(args) { + isToShowRegisterDnsButton = true; + function showRegisterDnsButton(result){ + isToShowRegisterDnsButton = result; + } + // $.ajax({ + // url: createURL("getGloboResourceConfiguration"), + // data: { + // resourceid: args.context.loadbalancers[0].id, + // resourcetype: 'LOAD_BALANCER', + // resourcekey: 'isDNSRegistered' + // }, + // dataType: "json", + // async: false, + // success: function(json) { + // var conf = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue + // if(conf == undefined || conf == "true") { + // showRegisterDnsButton(false); + // } else if (conf == "false") { + // showRegisterDnsButton(true); + // } + // }, + // error: function (errorMessage) { + // showRegisterDnsButton(false); + // //args.response.error(errorMessage); + // } + // }); + // return isToShowRegisterDnsButton; + return isToShowRegisterDnsButton; + + }, + action: function(args) { + // var show_error_message = function(json) { + // args.response.error(parseXMLHttpResponse(json)); + // }; + // $.ajax({ + // url: createURL("registerDnsForResource"), + // data: { + // uuid: args.context.loadbalancers[0].id, + // resourcetype: "LOAD_BALANCER" + // }, + // dataType: "json", + // async: false, + // success: function(data) { + // cloudStack.ui.notifications.add({ + // desc: 'message.registry.dns.for.load.balancer.successful', + // section: 'Details', + // poll: pollAsyncJobResult, + // _custom: { + // jobId: data.registerdnsforresourceresponse.jobid + // } + // }, + // function() { + // $(window).trigger('cloudStack.fullRefresh'); + // $('.loading-overlay').remove(); + // }, {}, + // show_error_message, {} + // // job deleteLoadBalancerRule + // ); + + + + // }, + // error: function(data){ + // $('.loading-overlay').remove(); + // $(window).trigger('cloudStack.fullRefresh'); + // } // ajax deleteLoadBalancerRule + // }); + }, + messages: { + confirm: function(args) { + return 'label.action.registry.dns.for.load.balancer'; + }, + notification: function() { + return 'notification for retryRegisterDNS'; + }, + complete: function(args) { + return 'message.registry.dns.for.load.balancer.successfull'; + } + }, + notification: { + label: 'DNS Registered', + poll: pollAsyncJobResult + } + }, + + add: { label: 'label.network.addVM', messages: { @@ -2669,6 +2767,71 @@ } }, + restart: { + label: 'Retry Register DNS for NIC', + messages: { + confirm: function() { + return 'label.action.registry.dns.for.vm.nic'; + }, + notification: function(args) { + return 'message.registry.dns.for.vm.nic.successful' + } + }, + action: function(args) { + $.ajax({ + url: createURL("registerDnsForResource"), + data: { + uuid: args.context.nics[0].id, + resourcetype: "VM_NIC" + }, + dataType: "json", + async: false, + success: function(data) { + cloudStack.ui.notifications.add({ + desc: 'message.registry.dns.for.vm.nic.successful', + section: 'Details', + poll: pollAsyncJobResult, + _custom: { + jobId: data.registerdnsforresourceresponse.jobid + } + }, + function() { + $(window).trigger('cloudStack.fullRefresh'); + $('.loading-overlay').remove(); + }, {}, + {}, {} + // job deleteLoadBalancerRule + ); + }, + error: function(data){ + $('.loading-overlay').remove(); + $(window).trigger('cloudStack.fullRefresh'); + } // ajax deleteLoadBalancerRule + }); + + // $.ajax({ + // url: createURL('updateDefaultNicForVirtualMachine'), + // data: { + // virtualmachineid: args.context.instances[0].id, + // nicid: args.context.nics[0].id + // }, + // success: function(json) { + // args.response.success({ + // _custom: { + // jobId: json.updatedefaultnicforvirtualmachineresponse.jobid + // } + // }); + // cloudStack.dialog.notice({ + // message: _l(dictionary['message.set.default.NIC.manual']) + // }); + // } + // }); + }, + notification: { + poll: pollAsyncJobResult + } + }, + makeDefault: { label: 'label.set.default.NIC', messages: { @@ -2853,6 +3016,9 @@ label: 'label.name', header: true }, + fqdn: { + label: 'label.fqdn' + }, networkname: { label: 'label.network.name' }, @@ -2911,12 +3077,73 @@ async: true, success: function(json) { // Handling the display of network name for a VM under the NICS tabs + // Plus FQDN + $(json.listvirtualmachinesresponse.virtualmachine[0].nic).each(function(index, nic) { + var fqdn = args.context.instances[0].name + '.'; + var name = 'NIC ' + (index + 1); + if (nic.isdefault) { + name += ' (' + _l('label.default') + ')'; + } + $.ajax({ + url: createURL('listNetworks'), + async: false, + data: { + id: nic.networkid + }, + success: function(json2) { + fqdn += json2.listnetworksresponse.network[0].networkdomain; + } + }); + $.extend(json.listvirtualmachinesresponse.virtualmachine[0].nic[index], { + name: name, + fqdn: fqdn + }); + }); + args.response.success({ actionFilter: function(args) { + isToShowRegisterDnsButton = true; + function showRegisterDnsButton(result){ + isToShowRegisterDnsButton = result; + } + + $.ajax({ + url: createURL("getGloboResourceConfiguration"), + data: { + resourceid: args.context.item.id, + resourcetype: 'VM_NIC', + resourcekey: 'isDNSRegistered' + }, + dataType: "json", + async: false, + success: function(json) { + var conf = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue + console.log(conf); + if(conf == undefined || conf == "true") { + showRegisterDnsButton(false); + } else if (conf == "false") { + showRegisterDnsButton(true); + } + }, + error: function (errorMessage) { + showRegisterDnsButton(false); + //args.response.error(errorMessage); + } + }); if (args.context.item.isdefault) { - return ['updateIpaddr']; + //IF aqui pra exibir ou nao o DNS registry button + if (isToShowRegisterDnsButton) { + return ['restart','updateIpaddr']; + } else { + return []; + } } else { - return ['remove', 'makeDefault', 'updateIpaddr']; + //IF aqui pra exibir ou nao o DNS registry button também + if (isToShowRegisterDnsButton) { + return ['remove', 'makeDefault', 'restart', 'updateIpaddr']; + } else { + return ['remove', 'makeDefault']; + } } }, data: $.map(json.listvirtualmachinesresponse.virtualmachine[0].nic, function(nic, index) { diff --git a/ui/scripts/loadbalancer.js b/ui/scripts/loadbalancer.js new file mode 100755 index 000000000000..4e183de7785c --- /dev/null +++ b/ui/scripts/loadbalancer.js @@ -0,0 +1,1231 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function(cloudStack, $) { + + var healthcheckTypes = { + "values": [{id: 'TCP', name: 'TCP', description: 'TCP', layer: 4}, + {id: 'UDP', name: 'UDP', description: 'UDP', layer: 4}, + {id: 'HTTP', name: 'HTTP', description: 'HTTP', layer: 7}, + {id: 'HTTPS', name: 'HTTPS', description: 'HTTPS', layer: 7} + ], + "isLayer4": function(idProtocol) { + return this.isLayer(idProtocol, 4) + }, + "isLayer7": function(idProtocol) { + return this.isLayer(idProtocol, 7) + }, + "isLayer": function(idProtocol, layer) { + for (var i = 0; i < this.values.length; i++ ){ + if ( this.values[i].id === idProtocol && this.values[i].layer == layer){ return true; } + }; + return false; + }, + "validationMsg": "Expected Health Check can not be empty when Health Check Type is HTTP/HTTPS" + }; + var msg_validation_healthcheck_http = healthcheckTypes.validationMsg; + var cascadeAsyncCmds = function(args) { + + var process_command = function(index, last_result) { + var command = args.commands[index]; + + var process_success = function(result, jobId) { + command.result = result; + if (index === args.commands.length-1) { + // runned last command. + args.success(result, jobId); + } else { + // run next command + process_command(index+1, result); + } + }; + + if ($.isFunction(command.data)) { + command.data = command.data(last_result, index, args); + } + + // sometimes, command must be skipped + if (command.data === false) { + process_success(false, null); + return; + } + + if (!command.url) { + command.url = createURL(command.name); + } + + $.ajax({ + url: command.url, + data: command.data, + dataType: "json", + success: function(result) { + var jobId, timerControl; + + // get jobid + for (var prop in result) { + if (result.hasOwnProperty(prop) && prop.match(/response$/)) { + jobId = result[prop].jobid; + break; + } + } + + if (!jobId) { + // jobid not found. Synchronous command + process_success(result, null); + } else { + // pool jobid for completion + timerControl = setInterval(function() { + pollAsyncJobResult({ + _custom: { + jobId: jobId + }, + complete: function(json) { + clearInterval(timerControl); + process_success(result, jobId); + }, + error: function(message) { + clearInterval(timerControl); + args.error(message, index, args); + } + }); + }, g_queryAsyncJobResultInterval); + } + }, + error: function (json) { + args.error(parseXMLHttpResponse(json), index, args); + } + }); + }; + + process_command(0, {}); + }; + + var buildLbs = function(data) { + var loadBalancerData = data.listloadbalancerrulesresponse.loadbalancerrule; + $(loadBalancerData).each(function() { + var that = this; + this.ports = this.publicport + ':' + this.privateport + ', '; + $(this.additionalportmap).each(function() { + that.ports += this + ', '; + }); + this.ports = this.ports.substring(0, this.ports.length - 2); // remove last ', ' + if (typeof(this.linkedparent) != 'undefined') { + this.linkedlb = this.linkedparent.name; + this.isLinked = true; + } else { + this.isLinked = false; + } + + if (typeof(this.linkedchildren) != 'undefined' && this.linkedchildren.length > 0) { + this.hasLinkedChildren = true; + } else { + this.hasLinkedChildren = false; + } + + }); + return loadBalancerData; + } + + var getLoadBalancer = function(lbid) { + var lb = null; + $.ajax({ + url: createURL("listLoadBalancerRules"), + data: { + id: lbid + }, + dataType: "json", + async: false, + success: function(data) { + var lbs = buildLbs(data); + if (lbs.length > 0) { + lb = lbs[0]; + } + } + }); + return lb; + } + + var autoscaleActionfilter = function(args) { + var jsonObj = args.context.item; + + var allowedActions = []; + allowedActions.push("remove"); + + if (jsonObj.state == "enabled") + allowedActions.push("disable"); + else if (jsonObj.state == "disabled") + allowedActions.push("enable"); + + return allowedActions; + }; + + $.validator.addMethod("noUnderscore", function(value, element) { + return !/[_]/.test(value); + }, "Do not use underscore"); + $.validator.addMethod("positiveNumber", function(value, element) { + return (/^[0-9]+/).test(value); + }, "Use positive numbers only"); + + + + cloudStack.sections.loadbalancer = { + title: 'label.load.balancer', + id: 'loadbalancer', + listView: { + id: 'loadbalancers', + fields: { + name: { label: 'label.fqdn' }, + publicip: { label: 'label.ip' }, + ports: { label: 'label.port' }, + algorithm: { label: 'label.algorithm' }, + state: { + converter: function(str) { + // For localization + return str; + }, + label: 'label.state', + indicator: { + 'Add': 'off', + 'Active': 'on', + 'Revoke': 'off', + 'Deleting': 'off' + } + }, + }, + dataProvider: function(args) { + var data = {}; + listViewDataProvider(args, data); + + if (typeof(args.context.linkedchildren) != 'undefined') { + var loadBalancerData = [] + $(args.context.linkedchildren).each(function() { + var lb = getLoadBalancer(this.uuid) + loadBalancerData.push(lb) + }); + args.response.success({ data: loadBalancerData }); + } else { + if (typeof(args.context.linkedlb) != 'undefined') { + data['id'] = args.context.linkedlb.uuid; + } + + $.ajax({ + url: createURL("listLoadBalancerRules"), + data: data, + dataType: "json", + async: true, + success: function(data) { + var loadBalancerData = buildLbs(data); + args.response.success({ data: loadBalancerData }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + } + + + }, + detailView: { + name: 'Load Balancer Details', + isMaximized: true, + noCompact: true, + viewAll: [{ + path: 'loadbalancer', + label: 'Load Balancer Parent', + updateContext: function(args) { + return { 'linkedlb': args.context.loadbalancers[0].linkedparent}; + }, + preFilter: function(args) { + return args.context.loadbalancers[0].isLinked; + } + }, + { + path: 'loadbalancer', + label: 'Load Balancer Children', + updateContext: function(args) { + return { 'linkedchildren': args.context.loadbalancers[0].linkedchildren}; + }, + preFilter: function(args) { + return args.context.loadbalancers[0].hasLinkedChildren; + } + }], + tabFilter: function(args) { + var hiddenTabs = []; + if (args.context.loadbalancers[0].isLinked) { + hiddenTabs.push("vms"); + hiddenTabs.push("autoscale"); + hiddenTabs.push("networks"); + hiddenTabs.push("pools"); + } + return hiddenTabs; + }, + tabs: { + // see loadbalancer/tabDetails.js + details: {}, + networks: { + title: 'Networks', + listView: { + id: 'networks', + fields: { + name: { label: 'label.name' }, + cidr: { label: 'label.cidr' }, + }, + dataProvider: function(args) { + // Make sure load balancer object is always up to date + + $.ajax({ + url: createURL("listLoadBalancerRules"), + data: { + id: args.context.loadbalancers[0].id, + }, + dataType: "json", + async: false, + success: function(json) { + args.context.loadbalancers[0] = buildLbs(json)[0]; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + + var networkidslist = []; + networkidslist.push(args.context.loadbalancers[0].networkid); + networkidslist = networkidslist.concat(args.context.loadbalancers[0].additionalnetworkids); + + var networks = []; + $(networkidslist).each(function() { + $.ajax({ + url: createURL('listGloboLbNetworks'), + data: { + id: this.valueOf() + }, + async: false, + success: function(json) { + var network = json.listnetworksresponse.network[0]; + if(!network.cidr){ + network.cidr = network.ip6cidr; + } + networks.push(network); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }); + args.response.success({ + data: networks + }); + }, + actions: { + remove: { + label: 'label.delete', + messages: { + confirm: function(args) { + return 'Are you sure you want to disassociate network ' + args.context.networks[0].name + ' from load balancer ' + args.context.loadbalancers[0].name + '?'; + }, + notification: function(args) { + return 'Remove Network From Load Balancer'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("removeNetworksFromLoadBalancerRule"), + data: { + id: args.context.loadbalancers[0].id, + networkids: args.context.networks[0].id + }, + dataType: "json", + success: function(json) { + args.response.success({ + _custom: { + jobId: json.removenetworksfromloadbalancerruleresponse.jobid, + fullRefreshAfterComplete: true + }, + }); + }, + error: function(errorMessage) { + args.response.error(parseXMLHttpResponse(errorMessage)); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + }, + add: { + label: 'Associate Network to Load Balancer', + createForm: { + title: 'Associate Network to Load Balancer', + fields: { + network: { + label: 'label.network', + validation: { required: true }, + select: function(args) { + var networks = []; + $.ajax({ + url: createURL('listGloboLbNetworks'), + data: { + supportedservices: 'lb' + }, + dataType: "json", + async: false, + success: function(json) { + var lb = args.context.loadbalancers[0]; + $(json.listnetworksresponse.network).each(function() { + // Remove those that are already associated to load balancer + if (lb.networkid != this.id && lb.additionalnetworkids.indexOf(this.id) === -1) { + networks.push({id: this.id, description: this.name}); + } + }); + } + }); + args.response.success({ + data: networks + }); + } + } + }, + }, + action: function(args) { + var lbruleid = args.context.loadbalancers[0].id; + $.ajax({ + url: createURL("assignNetworksToLoadBalancerRule"), + data: { + id: lbruleid, + networkids: args.data.network + }, + dataType: "json", + success: function(json) { + args.response.success({ + _custom: { + jobId: json.assignnetworkstoloadbalancerruleresponse.jobid, + fullRefreshAfterComplete: true + }, + }); + }, + error: function(errorMessage) { + args.response.error(parseXMLHttpResponse(errorMessage)); + } + }); + }, + messages: { + notification: function(args) { + return 'Assign network to Load Balancer'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + } + }, + // see loadbalancer/tabVms.js + vms: {}, + // see loadbalancer/tabPools.js + pools: {}, + autoscale: { + title: 'label.autoscale', + listView: { + id: 'autoscalegroups', + hideSearchBar: true, + fields: { + id: { label: 'label.id' }, + minmembers: { label: 'Min VMs' }, + deployedvms: { label: 'AutoScale VMs' }, + maxmembers: { label: 'Max VMs' }, + state: { + converter: function(str) { + // For localization + return str; + }, + label: 'label.state', + indicator: { + 'enabled': 'on', + 'disabled': 'off', + 'revoke': 'off', + } + }, + }, + dataProvider: function(args) { + $.ajax({ + url: createURL('listAutoScaleVmGroups'), + data: { + listAll: true, + lbruleid: args.context.loadbalancers[0].id + }, + success: function(json) { + var response = json.listautoscalevmgroupsresponse.autoscalevmgroup ? + json.listautoscalevmgroupsresponse.autoscalevmgroup : []; + + $(response).each(function() { + this.nbscaleuppolicies = this.scaleuppolicies.length; + this.nbscaledownpolicies = this.scaledownpolicies.length; + }); + + $.ajax({ + url: createURL('listLoadBalancerRuleInstances'), + data: { + id: args.context.loadbalancers[0].id, + }, + success: function(data) { + lbinstances = data.listloadbalancerruleinstancesresponse.loadbalancerruleinstance ? + data.listloadbalancerruleinstancesresponse.loadbalancerruleinstance : []; + if (response[0]) { + response[0].deployedvms = response[0].autoscalegroupcountmembers; + } + + args.response.success({ + actionFilter: autoscaleActionfilter, + data: response + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + error: function (errorMessage) { + args.response.error(errorMessage); + } + }); + }, + detailView: { + name: 'AutoScale Details', + isMaximized: true, + noCompact: true, + tabs: { + details: { + title: 'label.details', + fields: [{ + id: { + label: 'label.id' + }, + autoScaleVmGroupName: { + label: 'label.name' + }, + serviceOfferingName: { + label: 'label.menu.service.offerings' + }, + templateName: { + label: 'label.template' + }, + minInstance: { + label: 'Min VMs' + }, + autoscalegroupcountmembers: { + label: 'AutoScale VMs' + }, + maxInstance: { + label: 'Max VMs' + }, + }], + tags: cloudStack.api.tags({ + resourceType: 'AutoScaleVmGroup', + contextId: 'autoscalegroups' + }), + dataProvider: function(args) { + $.ajax({ + url: createURL('listAutoScaleVmGroups'), + data: { + listAll: true, + lbruleid: args.context.loadbalancers[0].id + }, + success: function(json) { + var response = json.listautoscalevmgroupsresponse.autoscalevmgroup ? + json.listautoscalevmgroupsresponse.autoscalevmgroup : []; + + $(response).each(function() { + this.nbscaleuppolicies = this.scaleuppolicies.length; + this.nbscaledownpolicies = this.scaledownpolicies.length; + }); + + var autoscaleVmGroup = response[0]; + + $.ajax({ + url: createURL('listAutoScaleVmProfiles'), + data: { + listAll: true, + id: autoscaleVmGroup.vmprofileid + }, + success: function(json) { + var autoscaleVmProfile = json.listautoscalevmprofilesresponse.autoscalevmprofile[0]; + + //get service offering name + var serviceOfferingName; + function getServiceOfferingName(name) { + serviceOfferingName = name; + } + $.ajax({ + url: createURL("listServiceOfferings&issystem=false"), + data: { + id: autoscaleVmProfile.serviceofferingid + }, + dataType: "json", + async: false, + success: function(json) { + var serviceofferings = json.listserviceofferingsresponse.serviceoffering; + getServiceOfferingName(serviceofferings[0].name); + } + }); + + //get template name + var templateName; + function getTemplateName(name) { + templateName = name; + } + $.ajax({ + url: createURL("listTemplates"), + data: { + templatefilter: 'executable', + id: autoscaleVmProfile.templateid + }, + dataType: "json", + async: false, + success: function(json) { + var templates = json.listtemplatesresponse.template; + getTemplateName(templates[0].name); + } + }); + + var originalAutoscaleData = { + id: autoscaleVmGroup.id, + autoscalegroupcountmembers: autoscaleVmGroup.autoscalegroupcountmembers, + templateName: templateName, + serviceOfferingId: autoscaleVmProfile.serviceofferingid, + serviceOfferingName: serviceOfferingName, + minInstance: autoscaleVmGroup.minmembers, + maxInstance: autoscaleVmGroup.maxmembers, + autoScaleVmGroupName: autoscaleVmGroup.autoscalegroupvmprefixname, + context: { + autoscaleVmGroup: autoscaleVmGroup, + autoscaleVmProfile: autoscaleVmProfile + } + }; + args.response.success({ + data: originalAutoscaleData + }); + } + }); + }, + error: function (errorMessage) { + args.response.error(errorMessage); + } + }); + } + } + } + }, + + actions: { + add: { + label: 'Configure AutoScale', + action: { + custom: function(args) { + args.context.networks = []; + args.context.networks.push({zoneid: args.context.loadbalancers[0].zoneid}); + args.context.multiRules = []; + args.context.multiRules.push(args.context.loadbalancers[0]); + args.context.loadbalancer = args.context.loadbalancers[0]; + var returnFunction = cloudStack.uiCustom.autoscaler(cloudStack.autoscaler); + return returnFunction(args); + } + }, + messages: { + notification: function() { + return 'Update AutoScale'; + } + }, + notification: { + poll: pollAsyncJobResult + }, + }, + enable: { + label: 'label.enable.autoscale', + messages: { + confirm: function(args) { + return 'Are you sure you want to enable AutoScale?'; + }, + notification: function(args) { + return 'label.enable.autoscale'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('enableAutoScaleVmGroup'), + data: { + id: args.context.autoscalegroups[0].id + }, + async: true, + success: function(json) { + var jid = json.enableautoscalevmGroupresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + var response = json.queryasyncjobresultresponse.jobresult.autoscalevmgroup; + if (window.lbinstances !== undefined && response) { + response.deployedvms = lbinstances.length; + } + return response; + }, + fullRefreshAfterComplete: true + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + }, + }, + disable: { + label: 'label.disable.autoscale', + messages: { + confirm: function(args) { + return 'Are you sure you want to disable AutoScale?'; + }, + notification: function(args) { + return 'label.disable.autoscale'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('disableAutoScaleVmGroup'), + data: { + id: args.context.autoscalegroups[0].id + }, + async: true, + success: function(json) { + var jid = json.disableautoscalevmGroupresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + var response = json.queryasyncjobresultresponse.jobresult.autoscalevmgroup; + if (window.lbinstances !== undefined && response) { + response.deployedvms = lbinstances.length; + } + return response; + }, + fullRefreshAfterComplete: true + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + }, + }, + remove: { + label: 'label.delete', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove AutoScale?'; + }, + notification: function(args) { + return 'Remove Autoscale'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('deleteAutoScaleVmGroup'), + data: { + id: args.context.autoscalegroups[0].id, + removedependencies: true, + }, + async: true, + success: function(json) { + var jid = json.deleteautoscalevmgroupresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + fullRefreshAfterComplete: true + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + }, + } + }, + }, + }, + }, + actions: { + restart: { + label: 'Retry Register DNS for Load Balancer', + custom: { + buttonLabel: 'label.configure' + }, + preFilter: function(args) { + isToShowRegisterDnsButton = true; + function showRegisterDnsButton(result){ + isToShowRegisterDnsButton = result; + } + $.ajax({ + url: createURL("getGloboResourceConfiguration"), + data: { + resourceid: args.context.loadbalancers[0].id, + resourcetype: 'LOAD_BALANCER', + resourcekey: 'isDNSRegistered' + }, + dataType: "json", + async: false, + success: function(json) { + var conf = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue + if(conf == undefined || conf == "true") { + showRegisterDnsButton(false); + } else if (conf == "false") { + showRegisterDnsButton(true); + } + }, + error: function (errorMessage) { + showRegisterDnsButton(false); + //args.response.error(errorMessage); + } + }); + return isToShowRegisterDnsButton; + + }, + action: function(args) { + var show_error_message = function(json) { + args.response.error(parseXMLHttpResponse(json)); + }; + $.ajax({ + url: createURL("registerDnsForResource"), + data: { + uuid: args.context.loadbalancers[0].id, + resourcetype: "LOAD_BALANCER" + }, + dataType: "json", + async: false, + success: function(data) { + cloudStack.ui.notifications.add({ + desc: 'message.registry.dns.for.load.balancer.successful', + section: 'Details', + poll: pollAsyncJobResult, + _custom: { + jobId: data.registerdnsforresourceresponse.jobid + } + }, + function() { + $(window).trigger('cloudStack.fullRefresh'); + $('.loading-overlay').remove(); + }, {}, + show_error_message, {} + // job deleteLoadBalancerRule + ); + + + + }, + error: function(data){ + $('.loading-overlay').remove(); + $(window).trigger('cloudStack.fullRefresh'); + } // ajax deleteLoadBalancerRule + }); + }, + messages: { + confirm: function(args) { + return 'label.action.registry.dns.for.load.balancer'; + }, + notification: function() { + return 'Notification for Retry Register DNS'; + }, + complete: function(args) { + return 'message.registry.dns.for.load.balancer.successfull'; + } + }, + notification: { + label: 'DNS Registered', + poll: pollAsyncJobResult + } + }, + editLoadBalancer: { + label: 'Edit Load Balancer', + custom: { + buttonLabel: 'label.configure' + }, + action: function(args) { + var oldStickiness; + // var oldHealthcheck; + + var lb = args.context.loadbalancers[0]; + + $.ajax({ + url: createURL('listLBStickinessPolicies'), + data: { + lbruleid: lb.id + }, + async: false, + success: function(json) { + var response = json.listlbstickinesspoliciesresponse.stickinesspolicies[0]; + if (!response || !response.stickinesspolicy || + !response.stickinesspolicy[0] || !response.stickinesspolicy[0].name) { + oldStickiness = "None"; + } else { + oldStickiness = response.stickinesspolicy[0].name; + } + } + }); + + cloudStack.dialog.createForm({ + form: { + title: 'Edit Load Balancer', + fields: { + stickiness: { + label: 'label.stickiness', + defaultValue: oldStickiness, + select: function(args) { + var network; + $.ajax({ + url: createURL('listGloboLbNetworks'), + data: { + id: lb.networkid + }, + dataType: "json", + async: false, + success: function(json) { + network = json.listnetworksresponse.network[0]; + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + + var lbService = $.grep(network.service, function(service) { + return service.name == 'Lb'; + })[0]; + + var stickinessCapabilities = $.grep( + lbService.capability, + function(capability) { + return capability.name == 'SupportedStickinessMethods'; + } + )[0]; + + var stickinessMethods = jQuery.parseJSON(stickinessCapabilities.value); + var data = []; + // Default None value + data.push({id: 'None', name: 'None', description: 'None'}); + $(stickinessMethods).each(function() { + data.push({id: this.methodname, name: this.methodname, description: this.methodname}); + }); + args.response.success({ + data: data + }); + } + } + } + }, + after: function(args2) { + var lastJobId; + args.response.success({ + _custom: { + getLastJobId: function() { return lastJobId; }, + getUpdatedItem: function() { + var loadbalancer = null; + $.ajax({ + url: createURL("listLoadBalancerRules"), + data: { + id: lb.id + }, + dataType: "json", + async: false, + success: function(data) { + var loadBalancerData = data.listloadbalancerrulesresponse.loadbalancerrule; + $(loadBalancerData).each(function() { + var that = this; + this.ports = this.publicport + ':' + this.privateport + ', '; + $(this.additionalportmap).each(function() { + that.ports += this + ', '; + }); + this.ports = this.ports.substring(0, this.ports.length - 2); // remove last ', ' + }); + loadbalancer = loadBalancerData[0]; + } + }); + return loadbalancer; + } + } + }); + + cascadeAsyncCmds({ + commands: [ + { + name: 'listLBStickinessPolicies', + data: { lbruleid: lb.id } + }, + { + name: 'deleteLBStickinessPolicy', + data: function(last_result) { + // If stickiness existed before and new value is different than old value + listLbStickinessResult = last_result.listlbstickinesspoliciesresponse.stickinesspolicies; + if (listLbStickinessResult && + listLbStickinessResult[0].stickinesspolicy.length > 0 && + listLbStickinessResult[0].stickinesspolicy.name != args2.data.stickiness.valueOf()) { + return { id: last_result.listlbstickinesspoliciesresponse.stickinesspolicies[0].stickinesspolicy[0].id }; + } + // skip this command + return false; + } + }, + { + name: 'createLBStickinessPolicy', + data: function () { + if (args2.data.stickiness.valueOf() != 'None' && args2.data.stickiness.valueOf() != oldStickiness) { + return { + lbruleid: lb.id, + name: args2.data.stickiness.valueOf(), + methodname: args2.data.stickiness.valueOf() + }; + } + return false; + } + } + ], + success: function(data, jobId) { + lastJobId = jobId; + }, + error: function(message) { + lastJobId = -1; + args.response.error(message); + } + }); + } + }); + + $('.create-form').find('.cancel').bind("click", function( event, ui ) { + $('.loading-overlay').remove(); + return true; + }); + }, + messages: { + notification: function() { + return 'Update Load Balancer'; + } + }, + notification: { + poll: function(args) { + var lastJobId = args._custom.getLastJobId(); + if (lastJobId === undefined) { + return; + } else if (lastJobId === null) { + args.complete({ + data: args._custom.getUpdatedItem() + }); + return; + } + args._custom.jobId = lastJobId; + return pollAsyncJobResult(args); + } + } + }, + linkloadbalancer: { + label: 'label.action.link.loadbalancer', + compactLabel: 'label.action.link.loadbalancer', + preFilter: function(args) { + console.log(args) + var lb = args.context.loadbalancers[0]; + + var show = !lb.isLinked && !lb.hasLinkedChildren && (lb.dsr == 'No') + + return show; + }, + createForm: { + title: 'label.action.link.loadbalancer', + desc: 'message.action.link.loadbalancer', + isWarning: true, + fields: { + linkablelb: { + label: 'LB', + select: function(args) { + var lb = args.context.loadbalancers[0]; + var result = []; + $.ajax({ + url: createURL("listGloboLinkableLoadBalancers"), + data: { + lbruleid: lb.id + }, + async: false, + success: function(data) { + $(data.listlinkableloadbalancerresponse.linkableloadbalancerresponse).each(function(){ + result.push({ + id: this.uuid, + name: this.name, + description: this.name + }); + }); + } + }); + + args.response.success({ + data: result + }); + } + } + } + }, + messages: { + notification: function(args) { + return 'label.action.link.loadbalancer.processing'; + } + }, + action: function(args) { + var lastJobId; + var lb = args.context.loadbalancers[0]; + $.ajax({ + url: createURL("linkGloboLoadBalancer"), + async: false, + data: { + childlbid: lb.id, + parentlbid: args.data.linkablelb + }, + success: function(data, jobId) { + lastJobId = data.linkgloboloadbalancerresponse.jobid; + + args.response.success({ + _custom: { + jobId: lastJobId, + getUpdatedItem: function() { + var loadbalancer = getLoadBalancer(lb.id); + + return loadbalancer; + } + } + }); + }, + error: function(message) { + lastJobId = -1; + args.response.error(message); + } + + }) + + }, + notification: { + poll: pollAsyncJobResult + } + }, + unlinkloadbalancer: { + label: 'label.action.unlink.loadbalancer', + custom: { + buttonLabel: 'label.action.unlink.loadbalancer' + }, + preFilter: function(args) { + return args.context.loadbalancers[0].isLinked + }, + action: function(args) { + var lastJobId; + var lb = args.context.loadbalancers[0]; + $.ajax({ + url: createURL("unlinkGloboLoadBalancer"), + async: false, + data: { + lbruleid: lb.id, + }, + success: function(data, jobId) { + lastJobId = data.unlinkgloboloadbalancerresponse.jobid; + + args.response.success({ + _custom: { + jobId: lastJobId, + getUpdatedItem: function() { + var loadbalancer = getLoadBalancer(lb.id); + + return loadbalancer; + } + } + }); + }, + error: function(message) { + lastJobId = -1; + args.response.error(message); + } + + }) + }, + messages: { + confirm: function(args) { + return 'message.action.unlink.loadbalancer'; + }, + notification: function(args) { + return 'label.action.unlink.loadbalancer.processing'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + }, + actions: { + remove: { + label: 'label.delete', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove load balancer ' + args.context.loadbalancers[0].name + '?'; + }, + notification: function(args) { + return 'Removing Ip Address'; + } + }, + action: function(args) { + var ipToBeReleased = args.context.loadbalancers[0].publicipid; + + var show_error_message = function(json) { + args.response.error(parseXMLHttpResponse(json)); + }; + + $.ajax({ + url: createURL("deleteGloboLoadBalancer"), + data: { + id: args.context.loadbalancers[0].id + }, + dataType: "json", + success: function(data) { + cloudStack.ui.notifications.add({ + desc: 'label.action.delete.load.balancer', + section: 'Network', + poll: pollAsyncJobResult, + _custom: { + jobId: data.deletegloboloadbalancerresponse.jobid, + fullRefreshAfterComplete: true + } + }, + function() { + }, {}, + show_error_message, {} // job deleteLoadBalancerRule + ); + }, + error: show_error_message // ajax deleteLoadBalancerRule + }); + }, + notification: { + poll: pollAsyncJobResult + }, + }, + add: {} // see addlb.js + } + }, + utils: {healthcheck: healthcheckTypes} + }; +})(cloudStack, jQuery); diff --git a/ui/scripts/loadbalancer/addlb.js b/ui/scripts/loadbalancer/addlb.js new file mode 100644 index 000000000000..ed6dc8173e06 --- /dev/null +++ b/ui/scripts/loadbalancer/addlb.js @@ -0,0 +1,551 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function(cloudStack, $) { + var healthcheckTypes = cloudStack.sections.loadbalancer.utils.healthcheck; + var msg_validation_healthcheck_http = healthcheckTypes.validationMsg; + + var networks = []; + + cloudStack.sections.loadbalancer.listView.actions["add"] = { + label: 'Create new Load Balancer', + preAction: function() { + var message; + $.ajax({ + url: createURL('listGloboLbNetworks'), + data: { + supportedservices: 'lb' + }, + dataType: "json", + async: false, + success: function(json) { + networks = []; + $(json.listnetworksresponse.network).each(function() { + networks.push({id: this.id, + description: this.name, + service: this.service, + physicalnetworkid: this.physicalnetworkid, + networkofferingid: this.networkofferingid, + }); + }); + }, + error: function(json) { + message = parseXMLHttpResponse(json); + } + }); + + if (networks.length === 0) { + cloudStack.dialog.notice({ + message: message || 'There are no networks. Please create a network before creating a load balancer.' + }); + return false; + } + return true; + }, + createForm: { + fields: { + name: { + label: 'label.name', + validation: { + required: true, + noUnderscore : true + } + }, + lbdomain: { + label: 'LB Domain', + validation: { + required: true + }, + select: function(args) { + var lbdomains = []; + $.ajax({ + url: createURL("listGloboNetworkCapabilities"), + dataType: "json", + async: false, + success: function(json) { + var response = json.listglobonetworkcapabilitiesresponse; + $(json.listglobonetworkcapabilitiesresponse.globoNetworkCapability.allowedLbSuffixes).each(function() { + lbdomains.push({id: this.valueOf(), description: this.valueOf()}); + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + args.response.success({ + data: lbdomains + }); + } + }, + ports: { + label: 'label.port', + docID: 'helpLbPorts', + validation: { + required: true + } + }, + network: { + label: 'label.network', + validation: { + required: true + }, + select: function(args) { + + args.response.success({ + data: networks + }); + } + }, + lbenvironment: { + label: 'LB Environment', + validation: { + required: true + }, + dependsOn: ['network'], + select: function(args) { + var network = findNetwork(args.data.network); + + $.ajax({ + url: createURL("listGloboNetworkLBEnvironments"), + data: { + physicalnetworkid: network.physicalnetworkid, + networkid: network.id + }, + dataType: "json", + async: false, + success: function(json) { + var data = []; + $(json.listglobonetworklbenvironmentsresponse.globonetworklbenvironments).each(function() { + data.push({id: this.id, name: this.name, description: this.name}); + }); + args.response.success({ + data: data + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + }, + algorithm: { + label: 'label.algorithm', + validation: { + required: true + }, + dependsOn: ['network'], + select: function(args) { + var network = findNetwork(args.data.network); + + + var lbService = $.grep(network.service, function(service) { + return service.name == 'Lb'; + })[0]; + + var algorithmCapabilities = $.grep( + lbService.capability, + function(capability) { + return capability.name == 'SupportedLbAlgorithms'; + } + )[0]; + + var algorithms = algorithmCapabilities.value.split(','); + var data = []; + $(algorithms).each(function() { + data.push({id: this.valueOf().trim(), name: this.valueOf().trim(), description: _l('label.lb.algorithm.' + this.valueOf().trim())}); + }); + args.response.success({ + data: data + }); + }, + }, + + isLbAdvanced: { + label: 'label.show.advanced.settings', + dependsOn: ['network'], + isBoolean: true, + defaultValue: false, + isChecked: false, + }, + cachegroup: { + label: 'Cache', + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isLbAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + validation: { + required: true + }, + defaultValue: "(nenhum)", + dependsOn: ['lbenvironment','isLbAdvanced'], + select: function(args) { + var network; + if ( args.data.lbenvironment !== '' ){ + $.ajax({ + url: createURL("listGloboNetworkLBCacheGroups"), + data: { + lbenvironment: args.data.lbenvironment, + networkid: args.data.network + }, + dataType: "json", + async: false, + success: function(json) { + var data = []; + $(json.listglobonetworklbcachegroupsresponse.globonetworkcachegroups).each(function() { + data.push({id: this.name, name: this.name, description: this.name}); + }); + args.response.success({ + data: data + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + }, + + dsr: { + label: 'DSR', + dependsOn: ['isLbAdvanced'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isLbAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + isBoolean: true, + defaultValue: false, + isChecked: false, + }, + + sticky: { + label: 'label.stickiness', + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isLbAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + dependsOn: ['network', 'isLbAdvanced'], + select: function(args) { + var network = networks[0]; + + var lbService = $.grep(network.service, function(service) { + return service.name == 'Lb'; + })[0]; + + var stickinessCapabilities = $.grep( + lbService.capability, + function(capability) { + return capability.name == 'SupportedStickinessMethods'; + } + )[0]; + + var stickinessMethods = jQuery.parseJSON(stickinessCapabilities.value); + var data = []; + // Default None value + data.push({id: 'None', name: 'None', description: 'None'}); + $(stickinessMethods).each(function() { + data.push({id: this.methodname, name: this.methodname, description: this.methodname}); + }); + args.response.success({ + data: data + }); + }, + }, + // #hlb + healthchecktype: { + label: 'Health check type', + docID: 'helpHealthcheckType', + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isLbAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + dependsOn: ['isLbAdvanced'], + select: function(args) { + args.response.success({ + data: healthcheckTypes.values + }); + args.$select.change(function() { + var type = $(this).val() + if (healthcheckTypes.isLayer4(type)) { + $("div[rel='healthcheck']").hide() + $("div[rel='expectedhealthcheck']").hide() + } else { + $("div[rel='healthcheck']").show() + $("div[rel='expectedhealthcheck']").show() + } + }) + } + }, + healthcheck: { + label: 'Health check request' + }, + expectedhealthcheck: { + label: 'Expected health check', + select: function(args) { + var expectedHealthcheck = []; + $.ajax({ + url: createURL("listGloboNetworkExpectedHealthchecks"), + data: { + }, + async: false, + success: function(json) { + expectedHealthchecksR = json.listgloboNetworkexpectedhealthchecksresponse.globonetworkexpectedhealthcheck + $(expectedHealthchecksR).each(function() { + expectedHealthcheck.push({id: this.expected, name: this.expected, description: this.expected}) + }); + args.response.success({ + data: expectedHealthcheck + }); + + } + }) + + + } + } + + }, + }, + action: function(args) { + if (!validateLb(args)) { + return; + }; + + data = { + networkid: args.data.network, + lbenvironmentid: args.data.lbenvironment + }; + + data = buildLoadBalancerData(args); + + $.ajax({ + url: createURL("createGloboLoadBalancer"), + data: data, + dataType: "json", + success: function(json) { + var jobID = json.creategloboloadbalancerresponse.jobid; + args.response.success({ + _custom:{ + jobId: jobID, + getUpdatedItem: function(json) { + var lb = json.queryasyncjobresultresponse.jobresult.loadbalancer; + lb.ports = lb.publicport + ':' + lb.privateport + ', '; + $(lb.additionalportmap).each(function() { + lb.ports += this + ', '; + }); + lb.ports = lb.ports.substring(0, lb.ports.length - 2); // remove last ', ' + + + notifyDnsRegistered(json.queryasyncjobresultresponse.jobresult.loadbalancer); + + return lb; + } + } + }); + }, + }); + }, + messages: { + notification: function(args) { + return 'Create Load Balancer'; + } + }, + notification: { + label: 'Load Balancer created', + poll: pollAsyncJobResult + } + } + + + var show_error_message = function(args, msg) { + if (typeof(msg) == 'string') { + args.response.error(msg); + } else if (typeof(msg) == 'object') { + args.response.error(parseXMLHttpResponse(msg)); + } + }; + + var validateLb = function(args){ + if (args.data.name.indexOf("_") > 0) { + args.response.error("Underscore(_) is not allowed in Load Balancer names"); + return false; + } + + if ( healthcheckTypes.isLayer7(args.data.healthchecktype) ) { + healthcheckPingPath = args.data.healthcheck.valueOf().trim(); + if ( healthcheckPingPath === ''){ + args.response.error(msg_validation_healthcheck_http); + return false; + } + } + + var network = $.grep(networks, function(nettemp){ + return args.data.network == nettemp.id + })[0]; + + var networkoffering = findNetworkOffering(network.networkofferingid); + + var lbService = $.grep(networkoffering.service, function(service) { + return service.name == 'Lb'; + })[0]; + + var provider = lbService.provider[0].name; + if (provider != 'GloboNetwork') { + args.response.error("Your network is provided by " + provider + ". Please, choose only network is provided by GloboNetwork"); + return false; + } + + if (!validatePortsMap(args, args.data.ports, args.data.dsr)) { + return false; + }; + + + return true; + }; + + + + var findNetworkOffering = function(id) { + var networkoffering; + $.ajax({ + url: createURL("listNetworkOfferings"), + data: { + id: id + }, + dataType: "json", + async: false, + success: function(json) { + networkoffering = json.listnetworkofferingsresponse.networkoffering[0]; + + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + return networkoffering; + } + + var validatePortsMap = function(args, portsInString, dsr) { + var portList = portsInString.replace(/[^\d\:\,]/g, "").replace(/\,+$/g, "").split(","); + + var portsMap = []; + //check format + if (portList.length > 0) { + $.each(portList, function() { + if (this.indexOf(":") == -1) { + args.response.error("Invalid ports. It should be in the form \"80:8080,443:8443\""); + return false; + } + }); + } + + //check duplicate public port + var publicPorts = new Set(); + $.each(portList, function() { + publicPorts.add(this.split(":")[0]) + }); + + if (publicPorts.size != portList.length) { + args.response.error("Invalid ports. It should be in the form \"80:8080,443:8443\""); + return false; + } + + if(dsr == 'on'){ + var publicPorts = new Array(); + $.each(portList, function() { + publicPorts.push(this.split(":")[0]) + }); + + var privatePorts = new Array(); + $.each(portList, function() { + privatePorts.push(this.split(":")[1]) + }); + + for(var i=0 ; i < portList.length; i++){ + if(publicPorts[i] != privatePorts[i]){ + args.response.error("In DSR load balancer the public port must always be the same as private port."); + return false; + } + } + } + + return true; + + } + var findNetwork = function(networkuuid) { + for (var i = 0; i < networks.length; i++) { + if (networks[i].id === networkuuid) { + return networks[i]; + } + } + return null; + } + + var notifyDnsRegistered = function(loadbalancer){ + var configs = loadbalancer.globoresourceconfig; + var isLoadBalancerDNSRegistered = false; + var hasDnsRegiteredConfig = false; + for (var i = 0; i < configs.length; i++) { + if ( configs[i].configurationkey == "isDNSRegistered" ) { + hasDnsRegiteredConfig = true; + isLoadBalancerDNSRegistered = configs[i].configurationvalue == "true"; + } + } + + if (hasDnsRegiteredConfig && !isLoadBalancerDNSRegistered) { + cloudStack.dialog.notice({message: "The LoadBalancer DNS was not registered. You can retry to register DNS later."}); + } + } + + var buildLoadBalancerData = function(args) { + var ports =args.data.ports.split(","); + additionalportmap = ports.slice(1, ports.length); + + var firstport = ports[0].split(":"); + + + var healthcheckrequest = ''; + var expectedhealthcheck = ''; + if (healthcheckTypes.isLayer7(args.data.healthchecktype)) { + healthcheckrequest = args.data.healthcheck.valueOf().trim() + expectedhealthcheck = args.data.expectedhealthcheck + } + + var data = { + algorithm: args.data.algorithm, + name: args.data.name + args.data.lbdomain, + publicport: firstport[0], + privateport: firstport[1], + openfirewall: false, + networkid: args.data.network, + cache: args.data.cachegroup, + dsr: args.data.dsr == 'on', + healthcheckType: args.data.healthchecktype, + healthcheckrequest: healthcheckrequest, + expectedhealthcheck: expectedhealthcheck, + additionalportmap: additionalportmap.join(), + skipdnserror: true, + stickinessmethodname: args.data.sticky.valueOf() != 'None' ? args.data.sticky.valueOf() : "", + lbenvironmentid: args.data.lbenvironment + }; + return data; + + } + cloudStack.validatePortsMap = validatePortsMap; + + +}(cloudStack, jQuery)); \ No newline at end of file diff --git a/ui/scripts/loadbalancer/tabDetails.js b/ui/scripts/loadbalancer/tabDetails.js new file mode 100644 index 000000000000..3d47ad2e30e5 --- /dev/null +++ b/ui/scripts/loadbalancer/tabDetails.js @@ -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. + +(function(cloudStack, $) { + + cloudStack.sections.loadbalancer.listView.detailView.tabs["details"] = { + title: 'label.details', + fields: [{ + id: { + label: 'label.id' + } + },{ + linkedlb: { + label: 'Linked with:', + isHidden: function(args) { + if (typeof(args.context.loadbalancers[0].isLinked) == 'undefined') { + return false; + } else { + return true; + } + } + }, + dns_registry:{ + label:'DNS Registered' + }, + name: { + label: 'label.fqdn' + }, + publicip: { + label: 'label.ip' + }, + ports: { + label: 'label.port' + }, + algorithm: { + label: 'label.algorithm' + }, + cache: { + label: 'Cache' + }, + dsr: { + label: 'DSR' + }, + stickiness: { + label: 'label.stickiness' + }, + }], + tags: cloudStack.api.tags({ + resourceType: 'LoadBalancer', + contextId: 'loadbalancers' + }), + dataProvider: function(args) { + if (!args.jsonObj) { + args.jsonObj = args.context.loadbalancers[0]; + } + $.ajax({ + url: createURL("listLBStickinessPolicies"), + data: { + lbruleid: args.jsonObj.id + }, + dataType: "json", + async: false, + success: function(json) { + var response = json.listlbstickinesspoliciesresponse.stickinesspolicies[0]; + var stickiness = ""; + if (!response || !response.stickinesspolicy || + !response.stickinesspolicy[0] || !response.stickinesspolicy[0].name) { + stickiness = "None"; + } else { + stickiness = response.stickinesspolicy[0].name; + } + args.jsonObj.stickiness = stickiness; + }, + error: function (errorMessage) { + args.response.error(errorMessage); + } + }); + + $.ajax({ + url: createURL("getGloboResourceConfiguration"), + data: { + resourceid: args.jsonObj.id, + resourcetype: 'LOAD_BALANCER', + resourcekey: 'isDNSRegistered' + }, + dataType: "json", + async: false, + success: function(json) { + if(json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue == null){ + args.jsonObj["dns_registry"] = "Yes" + } else { + args.jsonObj["dns_registry"] = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue == "true" ? "Yes" : "No"; + } + }, + error: function (errorMessage) { + args.jsonObj["dns_registry"] = "Yes"; + } + }); + + $.ajax({ + url: createURL("getGloboResourceConfiguration"), + data: { + resourceid: args.jsonObj.id, + resourcetype: 'LOAD_BALANCER', + resourcekey: 'dsr' + }, + dataType: "json", + async: false, + success: function(json) { + if(json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue == null){ + args.jsonObj["dsr"] = "No" + args.context.loadbalancers[0]['dsr'] = "No" + } else { + args.jsonObj["dsr"] = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue == "true" ? "Yes" : "No"; + args.context.loadbalancers[0]['dsr'] = json.getgloboresourceconfigurationresponse.globoresourceconfiguration.configurationvalue == "true" ? "Yes" : "No"; + } + } + }); + + args.response.success({ + data: args.jsonObj + }); + }, + } + + + +}(cloudStack, jQuery)); \ No newline at end of file diff --git a/ui/scripts/loadbalancer/tabPools.js b/ui/scripts/loadbalancer/tabPools.js new file mode 100644 index 000000000000..b03ebff21071 --- /dev/null +++ b/ui/scripts/loadbalancer/tabPools.js @@ -0,0 +1,757 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function(cloudStack, $) { + + var healthcheckTypes = cloudStack.sections.loadbalancer.utils.healthcheck; + + function getPool(poolid, lbid , zoneid) { + var data = { + lbruleid: lbid, + zoneid: zoneid + }; + var pools = []; + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: data, + dataType: "json", + async: false, + success: function(data) { + pools = data.listglobonetworkpoolresponse.globonetworkpool; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + var pool; + $(pools).each(function() { + if (this.id == poolid) { + pool = this; + } + }); + return pool; + } + + cloudStack.sections.loadbalancer.listView.detailView.tabs['pools'] = { + title: 'Pools', + + listView: { + id: 'pools', + hideSearchBar: true, + fields: { + ports: { label: 'label.port' }, + healthchecktype: { label: 'Healthcheck Type' }, + l4protocol: { label: 'L4 Protocol' }, + l7protocol: { label: 'L7 Protocol' } + }, + dataProvider: function(args) { + var data = { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid + }; + listViewDataProvider(args, data); + + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: data, + dataType: "json", + async: true, + success: function(data) { + var lbPools = data.listglobonetworkpoolresponse.globonetworkpool; + $(lbPools).each(function() { + this.ports = this.vipport + ':' + this.port; + }); + args.response.success({ + data: lbPools + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + + + }, + detailView: { + name: 'Pool Details', + isMaximized: true, + noCompact: true, + tabs: { + details: { + title: 'label.details', + fields: [{ + name: { + label: 'label.name' + }, + port: { + label: 'label.port' + }, + lbmethod: { + label: 'label.algorithm' + }, + maxconn: { + label: 'Max Connections' + }, + healthchecktype: { + label: 'Healthcheck Type' + }, + healthcheck: { + label: 'Healthcheck' + }, + healthcheckexpect: { + label: 'Expected Healthcheck' + }, + l4protocol: { + label: 'L4 Protocol' + }, + l7protocol: { + label: 'L7 Protocol' + } + }], + dataProvider: function(args) { + var pool = getPool(args.context.pools[0].id, args.context.loadbalancers[0].id, args.context.loadbalancers[0].zoneid); + args.context.poolselected = pool; + + args.response.success({ + data: pool + }); + } + } + }, + + actions: { + editPool: { + label: 'Edit Pool', + custom: { + buttonLabel: 'label.configure' + }, + action: function(args) { + var pool = args.context.pools[0]; + var lb = args.context.loadbalancers[0]; + cloudStack.dialog.createForm({ + form: { + title: 'Edit Pool', + fields: { + maxconn: { + label: 'Max Connections', + defaultValue: pool.maxconn.toString(), + validation: { + required: true, + positiveNumber: true + } + }, + healthchecktype: { + label: 'Healthcheck Type', + docID: 'helpHealthcheckType', + defaultValue: pool.healthchecktype, + dependsOn: ['isLbAdvanced'], + select: function(args) { + args.response.success({ + data: healthcheckTypes.values + }); + args.$select.change(function() { + var type = $(this).val() + + if ( healthcheckTypes.isLayer4(type)) { + $("div[rel='healthcheck']").hide() + $("div[rel='expectedhealthcheck']").hide() + } else { + $("div[rel='healthcheck']").show() + $("div[rel='expectedhealthcheck']").show() + } + }) + } + }, + healthcheck: { + label: 'Healthcheck', + defaultValue: pool.healthcheck + }, + expectedhealthcheck: { + label: 'Expected Healthchecks', + defaultValue: pool.healthcheckexpect, + select: function(args) { + var expectedHealthcheck = []; + $.ajax({ + url: createURL("listGloboNetworkExpectedHealthchecks"), + data: {}, + async: false, + success: function(json) { + expectedHealthchecksR = json.listgloboNetworkexpectedhealthchecksresponse.globonetworkexpectedhealthcheck + $(expectedHealthchecksR).each(function() { + expectedHealthcheck.push({id: this.expected, name: this.expected, description: this.expected}) + }); + args.response.success({ + data: expectedHealthcheck + }); + + } + }) + } + }, + isPoolAdvanced: { + label: 'label.show.advanced.settings', + isBoolean: true, + defaultValue: false, + isChecked: false, + }, + redeploy: { + label: 'Redeploy VIP', + isBoolean: true, + defaultValue: false, + isChecked: false, + dependsOn: ['isPoolAdvanced'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + return !isAdvancedChecked; + } + }, + l4protocol: { + id: 'l4protocol', + label: 'L4 Protocol', + defaultValue: pool.l4protocol, + validation: { + required: false + }, + dependsOn: ['isPoolAdvanced'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + select: function(args) { + args.response.success({ + data: [{id: 'TCP', description: 'TCP'}, + {id: 'UDP', description: 'UDP'}] + }); + } + }, + l7protocol: { + id: 'l7protocol', + label: 'L7 Protocol', + defaultValue: pool.l7protocol, + validation: { + required: false + }, + dependsOn: ['l4protocol', 'isPoolAdvanced'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + select: function(args) { + var data = []; + var l4protocol = $('select[name=l4protocol]').val(); + + if (typeof(l4protocol) == 'undefined') { + l4protocol = pool.l4protocol + } + + if ( l4protocol == 'TCP' ) { + data.push({id: 'HTTP', description: 'HTTP'}); + data.push({id: 'HTTPS', description: 'HTTPS'}); + } + data.push({id: 'Outros', description: 'Outros'}); + + + args.response.success({ + data: data + }); + } + } + } + }, + after: function(args2) { + var hasL4Change = args2.data.l4protocol != pool.l4protocol; + var hasL7Change = args2.data.l7protocol != pool.l7protocol; + + if (args2.data.redeploy != 'on' && (hasL7Change || hasL4Change) ) { + args.response.error("Only can change l4protocol/l7protocol when 'Redeploy VIP' is checked"); + return; + } + + if (args2.data.healthcheck === '' && (healthcheckTypes.isLayer7(args2.data.healthchecktype))) { + args.response.error(msg_validation_healthcheck_http); + return; + } + + + if (healthcheckTypes.isLayer4(args2.data.healthchecktype)) { // Empty healthcheck means TCP + args2.data.expectedhealthcheck = ''; // expecthealthcheck is for HTTP/HTTPS only + args2.data.healthcheck = ''; + } + + var data = { + poolids: pool.id.toString(), + lbruleid: lb.id, + zoneid: lb.zoneid, + healthchecktype: args2.data.healthchecktype, + healthcheck: args2.data.healthcheck, + expectedhealthcheck: args2.data.expectedhealthcheck, + maxconn: args2.data.maxconn + }; + + if (hasL4Change || hasL7Change) { + data['l4protocol'] = args2.data.l4protocol; + data['l7protocol'] = args2.data.l7protocol; + data['redeploy'] = args2.data.redeploy == 'on'; + } + + $.ajax({ + url: createURL('updateGloboNetworkPool'), + dataType: 'json', + async: true, + data: data, + success: function(json) { + var jid = json.updateglobonetworkpoolresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + return; + } + } + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + } + }); + + $('.create-form').find('.cancel').bind("click", function( event, ui ) { + $('.loading-overlay').remove(); + return true; + }); + }, + messages: { + notification: function() { + return 'Update Pool'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + }, + actions: { + add: { + label: 'Add pool', + preAction: function(args) { + var data = { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid + }; + + var pools = []; + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: data, + dataType: "json", + async: false, + success: function(data) { + pools = data.listglobonetworkpoolresponse.globonetworkpool; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + return true; + }, + createForm: { + fields: { + publicPort: { + label: 'Public port', + validation: { + required: true, + positiveNumber: true + } + }, + privatePort: { + label: 'Private port', + validation: { + required: true, + positiveNumber: true + } + }, + isPoolAdvanced: { + label: 'label.show.advanced.settings', + isBoolean: true, + defaultValue: false, + isChecked: false, + }, + l4protocol: { + id: 'l4protocol', + label: 'L4 Protocol', + validation: { + required: false + }, + dependsOn: ['isPoolAdvanced'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + select: function(args) { + args.response.success({ + data: [{id: 'TCP', description: 'TCP'}, + {id: 'UDP', description: 'UDP'}] + }); + } + }, + l7protocol: { + id: 'l7protocol', + label: 'L7 Protocol', + validation: { + required: false + }, + dependsOn: ['isPoolAdvanced', 'l4protocol'], + isHidden: function (args) { + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + return !isAdvancedChecked; + }, + select: function(args) { + var data = []; + var l4protocol = $('select[name=l4protocol]').val(); + + if ( l4protocol == 'TCP' || typeof(l4protocol) == 'undefined') { + data.push({id: 'HTTP', description: 'HTTP'}); + data.push({id: 'HTTPS', description: 'HTTPS'}); + } + data.push({id: 'Outros', description: 'Outros'}); + + + args.response.success({ + data: data + }); + } + }, + + }, + }, + action: function(args) { + var msg = "Are you sure you want to add this pool?

"; + msg += "Public port: " + args.data.publicPort + msg += "
"; + msg += "Private port: " + args.data.privatePort + msg += "
"; + + var isAdvancedChecked = $('input[name=isPoolAdvanced]:checked').length > 0; + + if (isAdvancedChecked) { + msg += "L4 Protocol: " + args.data.l4protocol + msg += "
"; + msg += "L7 Protocol: " + args.data.l7protocol + msg += "
"; + } + + cloudStack.dialog.confirm({ + message: msg, + action: function() { // "Yes" + var poolsList = []; + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid + }, + dataType: "json", + async: false, + success: function(data) { + poolsList = data.listglobonetworkpoolresponse.globonetworkpool; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + + $.ajax({ + url: createURL('createGloboNetworkPool'), + dataType: 'json', + async: true, + data: { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid, + publicPort: args.data.publicPort, + privatePort: args.data.privatePort, + l4protocol: args.data.l4protocol, + l7protocol: args.data.l7protocol, + }, + success: function(json) { + var jid = json.createglobonetworkpoolresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + cancelAction: function() { // "Cancel" + $(window).trigger('cloudStack.fullRefresh'); + } + }); + return; + }, + messages: { + notification: function() { + return 'Add new pool'; + } + }, + notification: { + label: 'Add new pool', + poll: pollAsyncJobResult + }, + }, + editAll: { + label: 'Edit all pools', + isHeader: true, + preAction: function(args) { + var data = { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid + }; + + var pools = []; + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: data, + dataType: "json", + async: false, + success: function(data) { + pools = data.listglobonetworkpoolresponse.globonetworkpool; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + + if (pools.length === 0) { + // No pools + cloudStack.dialog.notice({ + message: 'There are no pools. Please add a VM to this Load Balancer first.' + }); + return false; + } + return true; + }, + createForm: { + fields: { + maxconn: { + label: 'Max Connections', + defaultValue: "0", + validation: { + required: true, + positiveNumber: true + } + }, + healthchecktype: { + label: 'Healthcheck Type', + docID: 'helpHealthcheckType', + dependsOn: ['isLbAdvanced'], + select: function(args) { + args.response.success({ + data: healthcheckTypes.values + }); + args.$select.change(function() { + var type = $(this).val() + if ( healthcheckTypes.isLayer4(type)) { + $("div[rel='healthcheck']").hide() + $("div[rel='expectedhealthcheck']").hide() + } else { + $("div[rel='healthcheck']").show() + $("div[rel='expectedhealthcheck']").show() + } + }) + } + }, + healthcheck: { + label: 'Healthcheck', + docID: 'helpHealthcheck' + }, + expectedhealthcheck: { + label: 'Expected Healthchecks', + select: function(args) { + var expectedHealthcheck = []; + $.ajax({ + url: createURL("listGloboNetworkExpectedHealthchecks"), + data: {}, + async: false, + success: function(json) { + expectedHealthchecksR = json.listgloboNetworkexpectedhealthchecksresponse.globonetworkexpectedhealthcheck + $(expectedHealthchecksR).each(function() { + expectedHealthcheck.push({id: this.expected, name: this.expected, description: this.expected}) + }); + args.response.success({ + data: expectedHealthcheck + }); + + } + }) + + + } + } + }, + }, + action: function(args) { + if (args.data.healthcheck === '' && healthcheckTypes.isLayer7(args.data.healthchecktype)) { + args.response.error(msg_validation_healthcheck_http); + return; + } + if (healthcheckTypes.isLayer4(args.data.healthchecktype)) { // Empty healthcheck means TCP + args.data.expectedhealthcheck = ''; // expecthealthcheck is for HTTP only + args.data.healthcheck = ''; + } + + var msg = "Are you sure you want to apply these configurations to ALL pools?

"; + msg += "Healthcheck: " + args.data.healthchecktype + " " + args.data.healthcheck + if ( args.data.expectedhealthcheck != '') { + msg += " - " + args.data.expectedhealthcheck + } + msg += "
"; + msg += "Maxconn: " + args.data.maxconn + ""; + cloudStack.dialog.confirm({ + message: msg, + action: function() { // "Yes" + var poolsList = []; + $.ajax({ + url: createURL("listGloboNetworkPools"), + data: { + lbruleid: args.context.loadbalancers[0].id, + zoneid: args.context.loadbalancers[0].zoneid + }, + dataType: "json", + async: false, + success: function(data) { + poolsList = data.listglobonetworkpoolresponse.globonetworkpool; + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + + var lb = args.context.loadbalancers[0]; + + var poolIds = []; + $(poolsList).each(function() { + poolIds.push(this.id); + }); + $.ajax({ + url: createURL('updateGloboNetworkPool'), + dataType: 'json', + async: true, + data: { + poolids: poolIds.join(','), + lbruleid: lb.id, + zoneid: lb.zoneid, + healthchecktype: args.data.healthchecktype, + healthcheck: args.data.healthcheck, + expectedhealthcheck: args.data.expectedhealthcheck, + maxconn: args.data.maxconn, + }, + success: function(json) { + var jid = json.updateglobonetworkpoolresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + cancelAction: function() { // "Cancel" + $(window).trigger('cloudStack.fullRefresh'); + } + }); + return; + }, + messages: { + notification: function() { + return 'Edit All Pools'; + } + }, + notification: { + poll: pollAsyncJobResult + }, + }, + remove: { + label : 'label.delete', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove this pool:' + args.name + '?'; + }, + notification: function(args) { + return 'Removing pool'; + } + }, + action: function(args) { + var show_error_message = function(json) { + args.response.error(parseXMLHttpResponse(json)); + }; + var lb = args.context.loadbalancers[0]; + var pool = args.data.jsonObj + + if(lb.publicport == pool.vipport && lb.privateport == pool.port){ + args.response.error("Default load balancer pool cannot be removed"); + return; + } + + $.ajax({ + url: createURL("deleteGloboNetworkPool"), + data: { + id: pool.id, + lbruleid: lb.id, + zoneid: lb.zoneid + }, + dataType: "json", + success: function(data) { + cloudStack.ui.notifications.add({ + desc: 'Removing pool', + section: 'Load balancer', + poll: pollAsyncJobResult, + _custom: { + jobId: data.deleteglobonetworkpoolresponse.jobid, + fullRefreshAfterComplete: true + } + }, + function() { + }, {}, + show_error_message, {} // job deleteLoadBalancerRule + ); + }, + error: show_error_message // ajax deleteLoadBalancerRule + }); + }, + notification: { + poll: pollAsyncJobResult + }, + } + } + } + } +}(cloudStack, jQuery)); \ No newline at end of file diff --git a/ui/scripts/loadbalancer/tabVms.js b/ui/scripts/loadbalancer/tabVms.js new file mode 100644 index 000000000000..8775d5af6968 --- /dev/null +++ b/ui/scripts/loadbalancer/tabVms.js @@ -0,0 +1,338 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function(cloudStack, $) { + + cloudStack.sections.loadbalancer.listView.detailView.tabs['vms'] = { + title: 'label.virtual.machines', + listView: { + id: 'vms', + disableInfiniteScrolling: true, + fields: { + name: { label: 'label.name' }, + ip: { label: 'label.ip' }, + network: { label: 'label.network' }, + }, + dataProvider: function(args) { + var data = {id: args.context.loadbalancers[0].id}; + listViewDataProvider(args, data); + $.ajax({ + url: createURL('listLoadBalancerRuleInstances'), + data: data, + success: function(data) { + lbinstances = []; + response = data.listloadbalancerruleinstancesresponse.loadbalancerruleinstance ? + data.listloadbalancerruleinstancesresponse.loadbalancerruleinstance : []; + $(response).each(function() { + var ipaddress; + var networkname; + $(this.nic).each(function() { + // Find the NIC that is in the load balancer + if (args.context.loadbalancers[0].networkid === this.networkid || + args.context.loadbalancers[0].additionalnetworkids.indexOf(this.networkid) !== -1) { + ipaddress = this.ipaddress; + networkname = this.networkname; + return false; // break 'each' loop since we've found it + } + }); + lbinstances.push({id: this.id, name: this.name, ip: ipaddress, network: networkname }); + }); + args.response.success({ + data: lbinstances + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + actions: { + rm_custom: { + label: 'Remove VMs', + isHeader: true, + listView: { + multiSelect: true, + title: 'Remove VMs from Load Balancer', + hideSelectAction: true, + applyButton: 'Remove Vms', + fields: { + name: { + label: 'label.name', + truncate: true + }, + displayname: { + label: 'label.display.name', + truncate: true + }, + zonename: { + label: 'label.zone.name' + }, + 'instance-vm-lb': { + label: 'instance-vm-lb', + isHidden: true + } + }, + dataProvider: function(args) { + var data = {}; + listViewDataProvider(args, data); + data['id'] = args.context.loadbalancers[0].id; + data['pagesize'] = 499; + //data['applied'] = false; + $.ajax({ + url: createURL("listLoadBalancerRuleInstances"), + data: data, + dataType: "json", + async: false, + success: function(json) { + var lbinstances = []; + var lb = args.context.loadbalancers[0]; + $(json.listloadbalancerruleinstancesresponse.loadbalancerruleinstance).each(function() { + lbinstances.push({id: this.id, 'instance-vm-lb': this.id, name: this.name, displayname: this.displayname, zonename: this.zonename }); + }); + args.response.success({ + data: lbinstances + }); + } + }); + } + }, + action: function(args3) { + trs = $('.instance-vm-lb').parent().slice(1); + vmsIds = []; + $.each(trs, function(columnIndex, tr) { + checkedTrs = $(tr).find(":checked"); + if (checkedTrs.length != 0){ + vmsIds.push($(tr).find('.instance-vm-lb').text()); + } + }); + var jobId; + $.ajax({ + url: createURL("removeFromLoadBalancerRule"), + data: { + id: args3.context.loadbalancers[0].id, + virtualmachineids: vmsIds.join(',') + }, + + dataType: "json", + async: true, + success: function(response) { + putOneLoadingRow(); + checkStatus(args3, response.removefromloadbalancerruleresponse.jobid, 'Removing VM(s) from Load Balancer.'); + //$(window).trigger('cloudStack.fullRefresh'); + }, + error: function(response) { + if ( response.status == 401){ + cloudStack.dialog.notice({message: 'label.session.expired'} ); + } + $(window).trigger('cloudStack.fullRefresh'); + } + }); + + } + }, + add_custom: { + label: 'Add VMs', + isHeader: true, + listView: { + multiSelect: true, + title: 'Add VMs to Load Balancer', + hideSelectAction: true, + fields: { + name: { + label: 'label.name', + truncate: true + }, + displayname: { + label: 'label.display.name', + truncate: true + }, + zonename: { + label: 'label.zone.name' + }, + 'instance-vm-lb': { + label: 'instance-vm-lb', + isHidden: true + } + }, + dataProvider: function(args) { + var data = {}; + listViewDataProvider(args, data); + data['id'] = args.context.loadbalancers[0].id; + data['applied'] = false; + data['pagesize'] = 499; + $.ajax({ + url: createURL("listLoadBalancerRuleInstances"), + data: data, + dataType: "json", + async: false, + success: function(json) { + var lbinstances = []; + var lb = args.context.loadbalancers[0]; + $(json.listloadbalancerruleinstancesresponse.loadbalancerruleinstance).each(function() { + lbinstances.push({id: this.id, 'instance-vm-lb': this.id, name: this.name, displayname: this.displayname, zonename: this.zonename }); + }); + args.response.success({ + data: lbinstances + }); + } + }); + } + }, + action: function(args3) { + trs = $('.instance-vm-lb').parent().slice(1); + vmsIds = []; + $.each(trs, function(columnIndex, tr) { + checkedTrs = $(tr).find(":checked"); + if (checkedTrs.length != 0){ + vmsIds.push($(tr).find('.instance-vm-lb').text()); + } + }); + var jobId; + $.ajax({ + url: createURL("assignToLoadBalancerRule"), + data: { + id: args3.context.loadbalancers[0].id, + virtualmachineids: vmsIds.join(',') + }, + + dataType: "json", + async: true, + success: function(response) { + addLoadingRow(vmsIds); + checkStatus(args3, response.assigntoloadbalancerruleresponse.jobid, 'Adding VM(s) to Load Balancer Rule'); + }, + error: function(response) { + if ( response.status == 401){ + cloudStack.dialog.notice({message: 'label.session.expired'} ); + } + $(window).trigger('cloudStack.fullRefresh'); + } + }); + + } + } + } + } + }; + + var checkStatus = function(args, jobId, msg) { + checkJobStatus({ + jobId: jobId, + msgSuccess: msg, + ok: function(args) { + $(window).trigger('cloudStack.fullRefresh'); + }, + error: function(args) { + $(window).trigger('cloudStack.fullRefresh'); + cloudStack.dialog.notice({message: args.message} ); + } + }); + }; + + var checkJobStatus = function(configs) { + var jobStatus; + var poolNotificaiton = { + desc: configs.msgSuccess, + section: 'loadbalancer', + interval: 1000, + _custom:{jobId: configs.jobId}, + poll: function(args){ + var jobStatus = -1; + var jobMsg = ''; + $.ajax({url: createURL('queryAsyncJobResult'), + data: {jobId: configs.jobId}, + async: false, + success: function(response) { + jobStatus = response.queryasyncjobresultresponse.jobstatus; + if (jobStatus == 1) { + args.complete(); + configs.ok(response); + } else if (jobStatus == 2 || jobStatus == 3){ + jobMsg = response.queryasyncjobresultresponse.jobresult.errortext; + args.error(jobMsg); + configs.error({message: jobMsg, response: response}); + } + }, + error: function(response) { + args.error(); + configs.error(response); + } + }); + } + }; + cloudStack.ui.notifications.add( + poolNotificaiton, + function(args) { }, + {}, + function(args){ }, + {}); + return jobStatus; + }; + + var addLoadingRow = function(vmsIds) { + var table = $('#details-tab-vms').find('table[class=body]'); + + var trs = table.find('tr'); + + + if (trs.length > 0) { + var first = $(trs[0]); + if ( first.attr('class') === 'empty last even') { + first.remove() + } + } + + var tr = createLoadingTr(); + + $.each(vmsIds, function(index, vmId) { + $(table[0]).prepend(tr.clone()); + }); + + }; + + var putOneLoadingRow = function(){ + var table = $('#details-tab-vms').find('table[class=body]'); + $(table[0]).empty(); + var tr = createLoadingTr(); + $(table[0]).prepend(tr.clone()); + }; + + function createLoadingTr() { + var fields = cloudStack.sections.loadbalancer.listView.detailView.tabs.vms.listView.fields; + + var tr = $(''); + tr.addClass('odd'); + + tr.append(createTdLoading('name first')); + tr.append(createTdLoading('ip reduce-hide')); + tr.append(createTdLoading('network reduce-hide')); + + return tr; + } + + function createTdLoading(name){ + var div = $('
').addClass('loading'); + var td = $(''); + td.addClass(name) + td.append(div); + + return td; + } + + + +}(cloudStack, jQuery)); \ No newline at end of file diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 22ddb10d8cfc..53ebbef85b8a 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -396,12 +396,23 @@ } } } + + var data = { + domainid: g_domainid, + listAll: true + } + if (cloudStack.context && cloudStack.context.projects == null){ + data.account = g_account + } + //Ajax call to check if VPN is enabled. $.ajax({ url: createURL('listRemoteAccessVpns'), + data: { listAll: true }, + async: false, success: function(vpnResponse) { var isVPNEnabled = vpnResponse.listremoteaccessvpnsresponse.count; @@ -794,11 +805,12 @@ rootAdminAddGuestNetwork: $.extend({}, addGuestNetworkDialog.def, { isHeader: true }), - + addGloboNetworkNetwork: $.extend({}, globoNetworkAPI.networkDialog.def, { + isHeader: true + }), rootAdminAddL2Network: $.extend({}, addL2GuestNetwork.def, { isHeader: true }) - }, id: 'networks', preFilter: function(args) { @@ -1170,8 +1182,9 @@ } }, action: function(args) { + var command = args.context.networks[0].guruname == "GloboNetworkGuru" ? "deleteNetworkInGloboNetwork" : "deleteNetwork"; $.ajax({ - url: createURL("deleteNetwork&id=" + args.context.networks[0].id), + url: createURL(command + "&id=" + args.context.networks[0].id), dataType: "json", async: true, success: function(json) { @@ -1198,6 +1211,7 @@ var isAdvancedSGZone = false; var hiddenTabs = []; var isSharedNetwork; + var hasGloboACL = false; var thisNetwork = args.context.networks[0]; if (thisNetwork.vpcid != null) { @@ -1221,6 +1235,8 @@ } if (thisService.name == 'Firewall') { + hasGloboACL = true; + $(thisService.provider).each(function() { if (this.name == 'JuniperSRX') { hasSRXFirewall = true; @@ -1247,6 +1263,14 @@ } }); + if (!networkHavingELB) { + hiddenTabs.push("addloadBalancer"); + } + + if(!hasGloboACL){ + hiddenTabs.push("globoACL"); + } + if (isVPC || isAdvancedSGZone || isSharedNetwork) { hiddenTabs.push('egressRules'); } @@ -1745,6 +1769,451 @@ virtualRouters: { title: "label.virtual.appliances", listView: cloudStack.sections.system.subsections.virtualRouters.sections.routerNoGroup.listView + }, + globoACL: { + title: 'ACL Outbound', + custom: function(args) { + var context = args.context; + + return $('
').multiEdit({ + context: context, + noSelect: true, + noHeaderActionsColumn: true, + fields: { + 'cidrlist': { + edit: true, + label: 'Destination', + desc: 'CIDR of destination network', + placeholder: '0.0.0.0/0', + }, + 'protocol': { + label: 'label.protocol', + select: function(args) { + args.$select.change(function() { + var $inputs = args.$form.find('th, td'); + var $icmpFields = $inputs.filter(function() { + var name = $(this).attr('rel'); + + return $.inArray(name, [ + 'icmptype', + 'icmpcode' + ]) > -1; + }); + var $otherFields = $inputs.filter(function() { + var name = $(this).attr('rel'); + + return name != 'cidrlist' && + name != 'icmptype' && + name != 'icmpcode' && + name != 'protocol' && + name != 'add-rule'; + }); + + if ($(this).val() == 'icmp') { + $icmpFields.show(); + $otherFields.hide(); + } else if ($(this).val() == 'all') { + $icmpFields.hide(); + $otherFields.hide(); + } else { + $icmpFields.hide(); + $otherFields.show(); + } + }); + + args.response.success({ + data: [{ + name: 'tcp', + description: 'TCP' + }, { + name: 'udp', + description: 'UDP' + }, { + name: 'icmp', + description: 'ICMP' + }] + }); + } + }, + 'startport': { + edit: true, + label: 'label.start.port', + isOptional: true + }, + 'endport': { + edit: true, + label: 'label.end.port', + isOptional: true + }, + 'icmptype': { + edit: true, + label: 'ICMP.type', + isHidden: true, + isOptional: true + }, + 'icmpcode': { + edit: true, + label: 'ICMP.code', + isHidden: true, + isOptional: true + }, + 'add-rule': { + label: 'label.add', + addButton: true + } + }, + add: { + label: 'label.add', + action: function(args) { + validcidr = /^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$/.test(args.data.cidrlist); + if (!validcidr) { + args.response.error("Not a valid CIDR for destination network"); + return; + } + var data = { + protocol: args.data.protocol, + cidrlist: args.data.cidrlist, + networkid: args.context.networks[0].id + }; + + if (args.data.icmptype && args.data.icmpcode) { // ICMP + $.extend(data, { + icmptype: args.data.icmptype, + icmpcode: args.data.icmpcode + }); + } else { // TCP/UDP + $.extend(data, { + startport: args.data.startport, + endport: args.data.endport + }); + } + + $.ajax({ + url: createURL('createGloboACLRule'), + data: data, + dataType: 'json', + async: true, + success: function(json) { + var jobId = json.creategloboaclrulereponse.jobid; + + args.response.success({ + _custom: { + jobId: jobId + }, + notification: { + label: 'label.add.ACL', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + }, + actions: { + destroy: { + label: 'label.remove.rule', + messages: { + confirm: function(args) { + return 'Are you sure you want to remove this ACL?'; + }, + notification: function(args) { + return 'Remove ACL'; + } + }, + action: function(args) { + $.ajax({ + url: createURL('removeGloboACLRule'), + data: { + id: args.context.multiRule[0].id, + networkid: args.context.networks[0].id + }, + dataType: 'json', + async: true, + success: function(data) { + var jobID = data.removegloboaclruleresponse.jobid; + + args.response.success({ + _custom: { + jobId: jobID + }, + notification: { + label: 'label.remove.ACL', + poll: pollAsyncJobResult + } + }); + }, + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } + } + }, + ignoreEmptyFields: true, + dataProvider: function(args) { + $.ajax({ + url: createURL('listGloboACLRules'), + data: { + listAll: true, + networkid: args.context.networks[0].id + }, + dataType: 'json', + async: true, + success: function(json) { + var response = json.listgloboaclrulesresponse.firewallrule ? + json.listgloboaclrulesresponse.firewallrule : []; + + if(response.length == 0){ + args.response.noData({ text: 'No ACLs found' }) + }else{ + args.response.success({ + data: $.map(response, function(rule) { + if (rule.protocol == 'all') { + $.extend(rule, { + startport: 'All', + endport: 'All' + }); + } else if (rule.protocol == 'tcp' || rule.protocol == 'udp') { + if (!rule.startport) { + rule.startport = ' '; + } + + if (!rule.endport) { + rule.endport = ' '; + } + } + + return rule; + }) + }); + } + } + }); + } + }); + } + }, + + addloadBalancer: { // EIP/ELB Basic zone: Add Load Balancer tab in network detailView + title: 'label.add.load.balancer', + custom: function(args) { + var context = args.context; + + return $('
').addClass('loadBalancer').multiEdit({ + context: context, + listView: $.extend(true, {}, cloudStack.sections.instances, { + listView: { + filters: false, + + dataProvider: function(args) { + var data = { + page: args.page, + pageSize: pageSize, + domainid: g_domainid, + account: g_account, + networkid: args.context.networks[0].id, + listAll: true + }; + + $.ajax({ + url: createURL('listVirtualMachines'), + data: data, + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + data: $.grep( + data.listvirtualmachinesresponse.virtualmachine ? + data.listvirtualmachinesresponse.virtualmachine : [], + function(instance) { + var nonAutoScale = 0; + if (instance.displayname == null) + nonAutoScale = 1; + else { + if (instance.displayname.match(/AutoScale-LB-/) == null) + nonAutoScale = 1; + else { + if (instance.displayname.match(/AutoScale-LB-/).length) + nonAutoScale = 0; + } + } + var isActiveState = $.inArray(instance.state, ['Destroyed', 'Expunging']) == -1; + return nonAutoScale && isActiveState; + } + ) + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } + } + }), + multipleAdd: true, + fields: { + 'name': { + edit: true, + label: 'label.name' + }, + 'publicport': { + edit: true, + label: 'label.public.port' + }, + 'privateport': { + edit: true, + label: 'label.private.port' + }, + 'algorithm': { + label: 'label.algorithm', + select: function(args) { + var data = getLBAlgorithms(args.context.networks[0]); + args.response.success({ + data: data + }); + } + }, + 'sticky': { + label: 'label.stickiness', + custom: { + buttonLabel: 'label.configure', + action: cloudStack.lbStickyPolicy.dialog() + } + }, + 'autoScale': { + label: 'label.autoscale', + custom: { + requireValidation: true, + buttonLabel: 'label.configure', + action: cloudStack.uiCustom.autoscaler(cloudStack.autoscaler) + } + }, + 'add-vm': { + label: 'label.add.vms', + addButton: true + }, + 'state' : { + edit: 'ignore', + label: 'label.state' + } + }, + + add: { //basic zone - elastic IP - Add Load Balancer tab - Add VMs button + label: 'label.add.vms', + action: function(args) { + var data = { + algorithm: args.data.algorithm, + name: args.data.name, + privateport: args.data.privateport, + publicport: args.data.publicport, + openfirewall: false, + domainid: g_domainid, + account: g_account + }; + + if ('vpc' in args.context) { //from VPC section + if (args.data.tier == null) { + args.response.error('Tier is required'); + return; + } + $.extend(data, { + networkid: args.data.tier + }); + } else { //from Guest Network section + $.extend(data, { + networkid: args.context.networks[0].id + }); + } + + var stickyData = $.extend(true, {}, args.data.sticky); + + $.ajax({ + url: createURL('createLoadBalancerRule'), + data: data, + dataType: 'json', + async: true, + success: function(data) { + var itemData = args.itemData; + //var jobID = data.createloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule + + $.ajax({ + url: createURL('assignToLoadBalancerRule'), + data: { + id: data.createloadbalancerruleresponse.id, + virtualmachineids: $.map(itemData, function(elem) { + return elem.id; + }).join(',') + }, + dataType: 'json', + async: true, + success: function(data) { + var jobID = data.assigntoloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule + var lbCreationComplete = false; + + args.response.success({ + _custom: { + jobId: jobID + }, + notification: { + label: 'label.add.load.balancer', + poll: function(args) { + var complete = args.complete; + var error = args.error; + + pollAsyncJobResult({ + _custom: args._custom, + complete: function(args) { + if (lbCreationComplete) { + return; + } + + lbCreationComplete = true; + cloudStack.dialog.notice({ + message: _l('message.add.load.balancer.under.ip') + args.data.loadbalancer.publicip + }); + + if (stickyData && + stickyData.methodname && + stickyData.methodname != 'None') { + cloudStack.lbStickyPolicy.actions.add( + args.data.loadbalancer.id, + stickyData, + complete, // Complete + complete // Error + ); + } else { + complete(); + } + }, + error: error + }); + } + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } + }, + + + dataProvider: function(args) { + args.response.success({ //no LB listing in AddLoadBalancer tab + data: [] + }); + } + }); + } } } } @@ -3345,23 +3814,7 @@ label: 'label.algorithm', isEditable: true, select: function(args) { - var data = [{ - id: 'roundrobin', - name: 'roundrobin', - description: _l('label.lb.algorithm.roundrobin') - }, { - id: 'leastconn', - name: 'leastconn', - description: _l('label.lb.algorithm.leastconn') - }, { - id: 'source', - name: 'source', - description: _l('label.lb.algorithm.source') - }]; - if (typeof args.context != 'undefined') { - var lbAlgs = getLBAlgorithms(args.context.networks[0]); - data = (lbAlgs.length == 0) ? data : lbAlgs; - } + var data = getLBAlgorithms(args.context.networks[0]); args.response.success({ data: data }); @@ -3458,26 +3911,36 @@ 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; - lbProviderIsNetscaler = checkIfNetScalerProviderIsEnabled(services); - } - }); - 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) - } + 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' || providers[k].name == 'GloboNetwork') { + lbProviderIsNetscaler = true; + break; + } + } + } + break; + } + } + } + } + }); } else { //from VPC section var lbProviderIsNetscaler; var services = args.context.vpc[0].service; diff --git a/ui/scripts/oauth2.js b/ui/scripts/oauth2.js new file mode 100644 index 000000000000..f98a332a4e31 --- /dev/null +++ b/ui/scripts/oauth2.js @@ -0,0 +1,131 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +(function($, cloudStack, window) { + $.extend(cloudStack, { + + OAuth2: { + authenticate: function(loginArgs, code, redirectUri) { + var haveSuccess = false, self = this; + // remove parameter code from url + history.replaceState(null, null, window.location.origin + window.location.pathname); + + $.ajax({ + url: clientApiUrl + '?command=oAuth2Login&response=json&code=' + code + '&redirect_uri=' + escape(redirectUri), + dataType: "json", + async: false, + success: function(json) { + if (loginArgs.processLogin(json.loginresponse)) { + g_loginResponse = json.loginresponse; + var bypass = loginArgs.bypassLoginCheck(); + self.postAuthentication(loginArgs, bypass); + haveSuccess = true; + } + }, + error: function(jqXHR) { + var notice; + if (jqXHR.status == 531 || jqXHR.status == 431) { + var error = $.parseJSON(jqXHR.responseText); + notice = error.oauth2urlresponse.errortext; + } else { + notice = "Error authenticating with OAuth2. Use login/password credentials"; + } + cloudStack.dialog.notice({ + message: notice, + clickAction: onLogoutCallback + }); + }, + beforeSend: function(XMLHttpRequest) { + return true; + } + }); + return haveSuccess; + }, + + redirectUser: function() { + var haveSuccess = false; + // check if user is already logged + + // don't put querystring in url + var redirectUri = window.location.origin + window.location.pathname; + $.cookie('oauth_redirect', redirectUri); + $.ajax({ + url: clientApiUrl + "?command=oauthRedirect&response=json&redirect_uri=" + escape(redirectUri), + dataType: "json", + async: false, + success: function(json) { + // to ensure never will break + redirectUri = json && json.oauth2urlresponse && + json.oauth2urlresponse.authenticationurl && json.oauth2urlresponse.authenticationurl.redirectUri; + if (redirectUri) { + // + haveSuccess = true; + $.cookie('logout_redirect', json.oauth2urlresponse.authenticationurl.logoutUri); + window.location.assign(redirectUri); + } + }, + beforeSend: function(XMLHttpRequest) { + return true; + } + }); + return haveSuccess; + }, + + postAuthentication: function(loginArgs, bypass) { + var logoutUrl = $.cookie('logout_redirect'); + $.cookie('logout_redirect', null); // remove cookie + var old_onLogoutCallback = onLogoutCallback; + onLogoutCallback = function() { + old_onLogoutCallback(); + if (logoutUrl) { + window.location.assign(logoutUrl); + return false; + } else { + return true; + } + }; + + loginArgs.complete({ + user: bypass.user + }); + $(window).trigger('cloudStack.init'); + }, + + login: function(loginArgs) { + var code = $.urlParam("code"), + redirectUri = $.cookie('oauth_redirect'), + bypass; + if (code && redirectUri) { + // if there are code and redirectUri, make OAuth 2 authentication + return this.authenticate(loginArgs, code, redirectUri); + } else { + // otherwise, check if already exists an authentication (other window) + bypass = loginArgs.bypassLoginCheck(); + if (bypass) { + this.postAuthentication(loginArgs, bypass); + return true; + } else { + // to avoid double call to bypassLoginCheck, set it to null + loginArgs.bypassLoginCheck = null; + // redirect user to authentication + return this.redirectUser(); + } + } + } + } + }); +}($, cloudStack, window)); diff --git a/ui/scripts/projects.js b/ui/scripts/projects.js index 4f992ec4049b..939bec1417b7 100644 --- a/ui/scripts/projects.js +++ b/ui/scripts/projects.js @@ -305,7 +305,13 @@ account: args.context.users[0].account, domainId: args.context.users[0].domainid, name: args.data['project-name'], - displayText: args.data['project-display-text'] + displayText: args.data['project-display-text'], + businessserviceid: args.data['project-businessservice'], + clientid: args.data['project-client'], + componentid: args.data['project-component'], + subcomponentid: args.data['project-subcomponent'], + productid: args.data['project-product'], + detailedusage: (args.data['project-detailedusage'] == "on") }, dataType: 'json', async: true, @@ -625,7 +631,8 @@ var user = args.context.users[0]; var data1 = { accountId: user.userid, - listAll: true + listAll: true, + simple: true }; if (args.projectName) { data1.keyword = args.projectName; @@ -768,7 +775,7 @@ }, dataProvider: function(args) { - var data = {}; + var data = {"simple": true}; listViewDataProvider(args, data); if (isDomainAdmin()) { @@ -816,11 +823,13 @@ if (isDomainAdmin()) { url += '&domainid=' + args.context.users[0].domainid; } + url += "&simple=true" $.ajax({ url: createURL(url), data: { listAll: true, - id: projectID + id: projectID, + simple: true }, async: false, success: function(json) { @@ -834,26 +843,115 @@ }; }, actions: { - edit: { - label: 'label.edit', + update: { + label: 'label.update', action: function(args) { - $.ajax({ - url: createURL('updateProject'), - data: $.extend(true, {}, args.context.projects[0], args.data), - success: function(json) { - args.response.success(); + var project = args.context.projects[0]; + cloudStack.dialog.createForm({ + form: { + title: 'Update Project', + fields: { + displaytext: { + label: 'label.display.name', + defaultValue: project.displaytext, + required: true + }, + detailedusage: { + label: 'label.project.detailedusage', + isBoolean: true, + defaultValue: false, + isChecked: (project.detailedusage) + }, + businessserviceid: { + label: 'label.project.businessservice', + defaultValue: project.businessserviceid, + select: function(args) { + createDictionaryEntityOption('listBusinessServices', 'listbusinessservicesresponse', 'businessservice', args) + } + }, + clientid: { + label: 'label.project.client', + defaultValue: project.clientid, + select: function(args) { + createDictionaryEntityOption('listClients', 'listclientsresponse', 'client', args) + } + }, + componentid: { + label: 'label.project.component', + defaultValue: project.componentid, + select: function(args) { + createDictionaryEntityOption('listComponents', 'listcomponentsresponse', 'component', args) + } + }, + subcomponentid: { + label: 'label.project.subcomponent', + defaultValue: project.subcomponentid, + select: function(args) { + createDictionaryEntityOption('listSubComponents', 'listsubcomponentsresponse', 'subcomponent', args) + } + }, + productid: { + label: 'label.project.product', + defaultValue: project.productid, + select: function(args) { + createDictionaryEntityOption('listProducts', 'listproductsresponse', 'product', args) + } + } + } }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } + after: function(args2) { + cloudStack.dialog.confirm({ + message: "Confirm to update project", + action: function() { // "Yes" + args2.data.id = project.id; + args2.data.detailedusage = args2.data.detailedusage == "on" + + $.ajax({ + url: createURL('updateProject'), + dataType: 'json', + async: true, + data: args2.data, + success: function(json) { + var jid = json.updateprojectresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function(json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + }, + error: function(errorMessage) { + args.response.error(errorMessage); + } + }); + }, + cancelAction: function() { // "Cancel" + $(window).trigger('cloudStack.fullRefresh'); + } + }); + }, + }); + + $('.create-form').find('.cancel').bind("click", function( event, ui ) { + $('.loading-overlay').remove(); + return true; }); }, messages: { notification: function(args) { return 'label.edit.project.details'; } + }, + notification: { + poll: pollAsyncJobResult + }, + cancelAction: function() { // "Cancel" + $(window).trigger('cloudStack.fullRefresh'); } }, + disable: { label: 'label.suspend.project', action: function(args) { @@ -1024,6 +1122,26 @@ }, state: { label: 'label.state' + }, + detailedusage: { + label: 'label.project.detailedusage', + isBoolean: true, + converter: cloudStack.converters.toBooleanText + }, + businessservice: { + label: 'label.project.businessservice' + }, + client: { + label: 'label.project.client' + }, + component: { + label: 'label.project.component' + }, + subcomponent: { + label: 'label.project.subcomponent' + }, + product: { + label: 'label.project.product' } }, { vmtotal: { @@ -1062,6 +1180,7 @@ if (isDomainAdmin()) { url += '&domainid=' + args.context.users[0].domainid; } + url += "&simple=true" $.ajax({ url: createURL(url), data: { @@ -1069,8 +1188,80 @@ id: projectID }, success: function(json) { + var project = json.listprojectsresponse.project ? json.listprojectsresponse.project[0] : {}; + + if(project.businessserviceid){ + $.ajax({ + url: createURL('listBusinessServices'), + data: { id: project.businessserviceid }, + async: false, + success: function(json) { + project.businessservice = json.listbusinessservicesresponse.businessservice[0].name; + }, + error: function(jqXHR, text, error){ + project.businessservice = {} + } + }); + } + + if(project.clientid){ + $.ajax({ + url: createURL('listClients'), + data: { id: project.clientid }, + async: false, + success: function(json) { + project.client = json.listclientsresponse.client[0].name; + }, + error: function(jqXHR, text, error){ + project.client = {} + } + }); + } + + if(project.componentid){ + $.ajax({ + url: createURL('listComponents'), + data: { id: project.componentid }, + async: false, + success: function(json) { + project.component = json.listcomponentsresponse.component[0].name; + }, + error: function(jqXHR, text, error){ + project.component = {} + } + }); + } + + if(project.subcomponentid){ + $.ajax({ + url: createURL('listSubComponents'), + data: { id: project.subcomponentid }, + async: false, + success: function(json) { + project.subcomponent = json.listsubcomponentsresponse.subcomponent[0].name; + }, + error: function(jqXHR, text, error){ + project.subcomponent = {} + } + }); + } + + if(project.productid){ + $.ajax({ + url: createURL('listProducts'), + data: { id: project.productid }, + async: false, + success: function(json) { + project.product = json.listproductsresponse.product[0].name; + }, + error: function(jqXHR, text, error){ + project.product = {} + } + }); + } + args.response.success({ - data: json.listprojectsresponse.project ? json.listprojectsresponse.project[0] : {}, + data: project, actionFilter: projectsActionFilter }); } @@ -1317,8 +1508,26 @@ } }; + function createDictionaryEntityOption(uri, collectionName, objectName, args){ + $.ajax({ + url: createURL(uri), + async: false, + success: function(json) { + options = Array() + options.push({id: "", description: ""}) + $.each(json[collectionName][objectName], function( index, value ) { + options.push({id: value.id, description: value.name}) + }); + args.response.success({ data: options }); + }, + error: function(jqXHR, text, error){ + args.response.success({ data: [{id: "", description: ""}] }); + } + }); + } + var projectsActionFilter = function(args) { - var allowedActions = ['remove', 'edit']; + var allowedActions = ['remove', 'edit', 'update']; if (args.context.item.account == cloudStack.context.users[0].account || isAdmin() || isDomainAdmin()) { diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index 606fed45d272..939c22d6d783 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -514,7 +514,7 @@ var addGuestNetworkDialog = { select: function(args) { var items = []; $.ajax({ - url: createURL("listProjects&listAll=true"), + url: createURL("listProjects&listAll=true&simple=true"), dataType: "json", async: false, success: function(json) { @@ -1172,6 +1172,16 @@ cloudStack.validate = { cloudStack.dialog.notice({ message: 'message.validate.instance.name' }); + + // Only lower case letters for GloboDNS (DNSAPI) + // FIXME Convert this code to use only regexp set as global option. + if (args.toLowerCase() !== args) { + cloudStack.dialog.notice({ + message: 'Only lower case letters!' + }); + return false; + } + return b; } } @@ -1216,6 +1226,9 @@ cloudStack.actionFilter = { allowedActions.push('edit'); //only Isolated network is allowed to upgrade to a different network offering (Shared network is not allowed to) allowedActions.push('restart'); allowedActions.push('remove'); + } else if (jsonObj.type == 'Shared' && jsonObj.guruname == "GloboNetworkGuru") { + allowedActions.push('restart'); + allowedActions.push('remove'); } else if (jsonObj.type == 'Shared') { if (isAdmin()) { allowedActions.push('restart'); @@ -2447,8 +2460,8 @@ cloudStack.api = { data: { 'tags[0].key': data.key, 'tags[0].value': data.value, - resourceIds: resourceId, - resourceType: resourceType + resourceids: resourceId, + resourcetype: resourceType }, success: function(json) { args.response.success({ @@ -2473,8 +2486,8 @@ cloudStack.api = { data: { 'tags[0].key': data.key, 'tags[0].value': data.value, - resourceIds: resourceId, - resourceType: resourceType + resourceids: resourceId, + resourcetype: resourceType }, success: function(json) { args.response.success({ diff --git a/ui/scripts/system.js b/ui/scripts/system.js index ef944d958dbb..c1f24990a4f8 100755 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -1475,7 +1475,12 @@ //scope: { label: 'label.scope' } }, actions: { - add: addGuestNetworkDialog.def + rootAdminAddGuestNetwork: $.extend({}, addGuestNetworkDialog.def, { + isHeader: true + }), + addGloboNetworkNetwork: $.extend({}, globoNetworkAPI.networkDialog.def, { + isHeader: true + }) }, dataProvider: function (args) { @@ -7440,7 +7445,7 @@ // GloboDns provider detail view GloboDns: { - isMaximized: true, + // isMaximized: true, type: 'detailView', id: 'globoDnsProvider', label: 'label.globo.dns', @@ -7639,6 +7644,262 @@ poll: pollAsyncJobResult } } + }, + }, + + // Globo ACL API provider detail view + GloboACLAPI: { + // isMaximized: true, + type: 'detailView', + id: 'globoAclProvider', + label: 'label.globo.aclapi', + tabs: { + details: { + title: 'label.details', + fields: [{ + name: { + label: 'label.name' + } + }, { + state: { + label: 'label.state' + } + }], + dataProvider: function(args) { + refreshNspData("GloboACLAPI"); + var providerObj; + $(nspHardcodingArray).each(function() { + if (this.id == "GloboACLAPI") { + providerObj = this; + return false; //break each loop + } + }); + args.response.success({ + data: providerObj, + actionFilter: networkProviderActionFilter('GloboACLAPI') + }); + } + }, + }, + actions: { + add: { + label: 'label.globo.aclapi.configuration', + createForm: { + title: 'label.globo.aclapi.configuration', + preFilter: function(args) {}, + fields: { + username: { + label: 'label.user', + validation: { + required: true + } + }, + password: { + label: 'label.password', + isPassword: true, + validation: { + required: true + } + }, + url: { + label: 'label.url', + validation: { + required: true + } + } + } + }, + action: function(args) { + if (nspMap["GloboACLAPI"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=GloboACLAPI&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + dataType: "json", + async: true, + success: function(json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addGloboDnsProviderIntervalID = 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(addGloboDnsProviderIntervalID); + if (result.jobstatus == 1) { + nspMap["GloboACLAPI"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addGloboAclApiHost(args, selectedPhysicalNetworkObj, "addGloboAclApiHost", "addgloboaclapihostresponse"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=GloboACLAPI failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function(XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=GloboACLAPI failed. Error: " + errorMsg); + } + }); + }, g_queryAsyncJobResultInterval); + } + }); + } else { + addGloboAclApiHost(args, selectedPhysicalNetworkObj, "addGloboAclApiHost", "addgloboaclapihostresponse"); + } + }, + messages: { + notification: function(args) { + return 'label.add.globo.aclapi'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + enable: { + label: 'label.enable.provider', + action: function(args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["GloboACLAPI"].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["GloboACLAPI"].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["GloboACLAPI"].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 + } + } + }, + }, + + + // GloboNetwork plugin + // wrap functions to use clousure context + GloboNetwork: globoNetworkAPI.provider({ + getNspMap: function() { + return nspMap; + }, + getNspHardcodingArray: function() { + return nspHardcodingArray; + }, + networkProviderActionFilter: function() { + return networkProviderActionFilter.apply(null, arguments); + }, + refreshNspData: function() { + return refreshNspData.apply(null, arguments); + }, + getSelectedPhysicalNetworkObj: function() { + return selectedPhysicalNetworkObj; + } + }) + } + } + }, + show: cloudStack.uiCustom.physicalResources({ + sections: { + physicalResources: { + type: 'select', + title: 'label.menu.physical.resources', + listView: { + zones: { + id: 'physicalResources', + label: 'label.menu.physical.resources', + fields: { + name: { + label: 'label.zone' + }, + networktype: { + label: 'label.network.type' + }, + domainid: { + label: 'label.public', + converter: function (args) { + if (args == null) + return "Yes"; + else + return "No"; + } + } + } } }, @@ -7757,7 +8018,7 @@ } } } - }, + }), physicalResourceSection: { sections: { physicalResources: { @@ -10437,6 +10698,15 @@ ipaddress: { label: 'label.ip.address' }, + ip6address: { + label: 'label.ipv6.address' + }, + ip6gateway: { + label: 'label.ipv6.gateway' + }, + ip6cidr: { + label: 'label.ipv6.CIDR' + }, id: { label: 'label.id' }, @@ -21574,7 +21844,27 @@ } }); } + function addGloboDnsHost(args, physicalNetworkObj, apiCmd, apiCmdRes) { + var array1 = []; + array1.push("&physicalnetworkid=" + physicalNetworkObj.id); + array1.push("&username=" + todb(args.data.username)); + array1.push("&password=" + todb(args.data.password)); + array1.push("&url=" + todb(args.data.url)); + $.ajax({ + url: createURL(apiCmd + array1.join("")), + dataType: "json", + type: "POST", + success: function(json) { + var jid = json[apiCmdRes].jobid; + args.response.success({ + _custom: { + jobId: jid, + } + }); + } + }); + } function addGloboDnsHost(args, physicalNetworkObj, apiCmd, apiCmdRes) { var array1 = []; array1.push("&physicalnetworkid=" + physicalNetworkObj.id); @@ -21597,9 +21887,34 @@ }); } + function addGloboAclApiHost(args, physicalNetworkObj, apiCmd, apiCmdRes) { + var array1 = []; + array1.push("&physicalnetworkid=" + physicalNetworkObj.id); + array1.push("&username=" + todb(args.data.username)); + array1.push("&password=" + todb(args.data.password)); + array1.push("&url=" + todb(args.data.url)); + + + $.ajax({ + url: createURL(apiCmd + array1.join("")), + dataType: "json", + type: "POST", + success: function(json) { + var jid = json[apiCmdRes].jobid; + args.response.success({ + _custom: { + jobId: jid, + } + }); + } + }); + } + + + var afterCreateZonePhysicalNetworkTrafficTypes = function (args, newZoneObj, newPhysicalnetwork) { - $.ajax({ + $.ajax({ url: createURL("updatePhysicalNetwork&state=Enabled&id=" + newPhysicalnetwork.id), dataType: "json", success: function (json) { @@ -22370,6 +22685,9 @@ case "BigSwitchBcf": nspMap[ "bigswitchBcf"] = items[i]; break; + case "GloboDns": + nspMap["GloboDns"] = items[i]; + break; case "Ovs": nspMap[ "Ovs"] = items[i]; break; @@ -22382,6 +22700,12 @@ case "GloboDns": nspMap["GloboDns"] = items[i]; break; + case "GloboNetwork": + nspMap["GloboNetwork"] = items[i]; + break; + case "GloboACLAPI": + nspMap["GloboACLAPI"] = items[i]; + break; case "ConfigDrive": nspMap["ConfigDrive"] = items[i]; break; @@ -22430,6 +22754,16 @@ id: 'Opendaylight', name: 'OpenDaylight (Experimental)', state: nspMap.Opendaylight ? nspMap.Opendaylight.state: 'Disabled' + }, + { + id: 'GloboNetwork', + name: 'GloboNetwork', + state: nspMap.GloboNetwork ? nspMap.GloboNetwork.state : 'Disabled' + }, + { + id: 'GloboACLAPI', + name: 'Globo ACL API', + state: nspMap.GloboACLAPI ? nspMap.GloboACLAPI.state : 'Disabled' }]; $(window).trigger('cloudStack.system.serviceProviders.makeHarcodedArray', { diff --git a/ui/scripts/ui-custom/autoscaler.js b/ui/scripts/ui-custom/autoscaler.js index 52eb5ea334b1..f267a74d4dfa 100644 --- a/ui/scripts/ui-custom/autoscaler.js +++ b/ui/scripts/ui-custom/autoscaler.js @@ -24,6 +24,7 @@ var bottomfields = forms.bottomFields; var scaleuppolicy = forms.scaleUpPolicy; var scaledownpolicy = forms.scaleDownPolicy; + var networks = forms.networks; var dataProvider = cloudStack.autoscaler.dataProvider; var actions = cloudStack.autoscaler.autoscaleActions; var actionFilter = cloudStack.autoscaler.actionFilter; @@ -54,11 +55,15 @@ .html("Scale Up Policy"); var $scaleDownPolicyTitle = $('
').addClass('scale-down-policy-title') .html("Scale Down Policy"); + + var $networks = $('
').addClass('networks'); + var topFieldForm, $topFieldForm, bottomFieldForm, $bottomFieldForm, scaleUpPolicyTitleForm, $scaleUpPolicyTitleForm, scaleDownPolicyTitleForm, $scaleDownPolicyTitleForm, - scaleUpPolicyForm, scaleDownPolicyForm; + scaleUpPolicyForm, scaleDownPolicyForm, + netorksForm; var renderActions = function(args) { var targetActionFilter = args.actionFilter ? args.actionFilter : actionFilter; @@ -199,9 +204,32 @@ fields: { scaleUpDuration: { label: 'label.duration.in.sec', + docID: 'helpAutoscaleDuration', validation: { required: true } + }, + scaleUpStep: { + label: 'label.scaleup.step', + docID: 'helpAutoscaleStep', + validation: { + required: true + } + }, + scaleUpLogicalOperator: { + label: 'label.scale.logicaloperator', + docID: 'helpScaleLogicalOperator', + select: function(args) { + args.response.success({ + data: [{ + name: 'AND', + description: 'AND' + }, { + name: 'OR', + description: 'OR' + }] + }); + } } } } @@ -217,10 +245,33 @@ title: '', fields: { scaleDownDuration: { - label: 'label.duration.in.sec', + label: 'label.duration.in.sec', + docID: 'helpAutoscaleDuration', validation: { required: true } + }, + scaleDownStep: { + label: 'label.scaledown.step', + docID: 'helpAutoscaleStep', + validation: { + required: true + } + }, + scaleDownLogicalOperator: { + label: 'label.scale.logicaloperator', + docID: 'helpScaleLogicalOperator', + select: function(args) { + args.response.success({ + data: [{ + name: 'AND', + description: 'AND' + }, { + name: 'OR', + description: 'OR' + }] + }); + } } } } @@ -242,6 +293,14 @@ ); } + $scaleUpPolicyTitleForm.find('input[name=scaleUpStep]').val( + data.scaleUpPolicy ? data.scaleUpPolicy.step : 1 + ); + + $scaleUpPolicyTitleForm.find('select[name=scaleUpLogicalOperator]').val( + data.scaleUpPolicy ? data.scaleUpPolicy.logicaloperator : 'AND' + ); + scaleuppolicy.context = context; scaleUpPolicyForm = $scaleUpPolicy.multiEdit(scaleuppolicy); @@ -257,9 +316,43 @@ ); } + $scaleDownPolicyTitleForm.find('input[name=scaleDownStep]').val( + data.scaleDownPolicy ? data.scaleDownPolicy.step : 1 + ); + + $scaleDownPolicyTitleForm.find('select[name=scaleDownLogicalOperator]').val( + data.scaleDownPolicy ? data.scaleDownPolicy.logicaloperator : 'AND' + ); + scaledownpolicy.context = context; scaleDownPolicyForm = $scaleDownPolicy.multiEdit(scaledownpolicy); + // Networks + if (data.context && $.isArray(data.context.autoscaleVmProfile.networkids)) { + $.ajax({ + url: createURL("listNetworks"), + dataType: "json", + async: false, + success: function(json) { + var networks = json.listnetworksresponse.network; + var networksIds = data.context.autoscaleVmProfile.networkids; + + var addedNetworks = []; + + $(networks).each(function(){ + if($.inArray(this.id, networksIds) >= 0){ + addedNetworks.push({ networkid : this.id }); + } + }); + + $autoscalerDialog.data('autoscaler-networks-data', addedNetworks); + } + }); + } + + networks.context = context; + networksForm = $networks.multiEdit(networks); + // Create and append bottom fields bottomFieldForm = cloudStack.dialog.createForm({ context: context, @@ -275,6 +368,7 @@ // Append main div elements $autoscalerDialog.append( $topFields, + $networks, $scaleUpPolicyTitle, $scaleUpPolicy, $scaleDownPolicyTitle, @@ -295,7 +389,7 @@ /* Hide effects for multi-edit table*/ $autoscalerDialog.find('div.scale-up-policy').prepend($hideScaleUp); - $autoscalerDialog.find('div.scale-down-policy ').prepend($hideScaleDown); + $autoscalerDialog.find('div.scale-down-policy').prepend($hideScaleDown); $autoscalerDialog.find('div.scale-up-policy').prepend($scaleUpHideLabel); $autoscalerDialog.find('div.scale-down-policy').prepend($scaleDownHideLabel); @@ -314,6 +408,10 @@ $(this).toggleClass('expand hide'); }); + $('div.ui-dialog div.autoscaler div.networks div.hide').click(function() { + $(this).toggleClass('expand hide'); + }); + /*Toggling the labels and data-item table - SCALE DOWN POLICY*/ $('div.ui-dialog div.autoscaler div.scale-down-policy div.hide').click(function() { $('div.ui-dialog div.autoscaler div.scale-down-policy div.multi-edit div.data div.data-item').slideToggle(); @@ -341,7 +439,7 @@ overflow: 'auto', open: function() { $("button").each(function() { - $(this).attr("style", "left: 600px; position: relative; margin-right: 5px; "); + $(this).attr("style", "left: 285px; position: relative; margin-right: 5px; "); }); }, buttons: [{ @@ -350,6 +448,7 @@ click: function() { $autoscalerDialog.dialog('destroy'); $('.overlay').remove(); + $('.loading-overlay').remove() } }, { text: _l('Apply'), @@ -373,6 +472,7 @@ response: { success: function() { $loading.remove(); + $('.loading-overlay').remove() $autoscalerDialog.dialog('destroy'); $autoscalerDialog.closest(':ui-dialog').remove(); $('.overlay').remove(); @@ -407,6 +507,7 @@ $autoscalerDialog.find('select[name=serviceOfferingId]').attr('disabled', true); $autoscalerDialog.find('select[name=securityGroups]').attr('disabled', true); $autoscalerDialog.find('select[name=diskOfferingId]').attr('disabled', true); + $autoscalerDialog.find('input[name=autoScaleVmGroupName]').attr('disabled', true); if (args.data.isAdvanced != null) { $autoscalerDialog.find('input[type=checkbox]').trigger('click'); diff --git a/ui/scripts/ui-custom/projects.js b/ui/scripts/ui-custom/projects.js index f6a2321ae6f3..deba74ad37a8 100644 --- a/ui/scripts/ui-custom/projects.js +++ b/ui/scripts/ui-custom/projects.js @@ -318,6 +318,7 @@ name: 'project-name', id: 'project-name' })); + var $projectDesc = $('
').addClass('field desc') .append($('