You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2022/09/12 07:07:42 UTC

[GitHub] [cloudstack] weizhouapache commented on a diff in pull request #6571: [WIP] VM Autoscaling with virtual router

weizhouapache commented on code in PR #6571:
URL: https://github.com/apache/cloudstack/pull/6571#discussion_r965206314


##########
api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java:
##########
@@ -22,13 +22,31 @@
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.Displayable;
 import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.commons.lang3.StringUtils;
 
 public interface AutoScaleVmGroup extends ControlledEntity, InternalIdentity, Displayable {
 
-    String State_New = "new";
-    String State_Revoke = "revoke";
-    String State_Enabled = "enabled";
-    String State_Disabled = "disabled";
+    enum State {
+        New, Revoke, Enabled, Disabled, Scaling;

Review Comment:
   chanegd to upper case



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/ListCountersCmd.java:
##########
@@ -51,6 +51,9 @@ public class ListCountersCmd extends BaseListCmd {
     @Parameter(name = ApiConstants.SOURCE, type = CommandType.STRING, description = "Source of the counter.")
     private String source;
 
+    @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "Provider of the counter.", since = "4.18.0")
+    private String provider;

Review Comment:
   changed to "Network provider"
   



##########
scripts/vm/hypervisor/xenserver/perfmon.py:
##########
@@ -135,15 +135,15 @@ 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
+        for host in login.host.get_all():
+            #print "http://" + str(login.host.get_address(host)) + "/rrd_updates?%s" % paramstr

Review Comment:
   this file is not used by this PR
   
   



##########
systemvm/debian/opt/cloud/bin/vpc_netusage.sh:
##########
@@ -124,8 +124,8 @@ if [ "$cflag" == "1" ]
 then
   if [ "$ethDev" != "" ]
   then
-    create_usage_rules
-    create_vpn_usage_rules
+    #create_usage_rules
+    #create_vpn_usage_rules

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java:
##########
@@ -94,6 +96,17 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
                description = "counterparam list. Example: counterparam[0].name=snmpcommunity&counterparam[0].value=public&counterparam[1].name=snmpport&counterparam[1].value=161")
     private Map counterParamList;
 
+    @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 4KB of data after base64 encoding. " +
+                    "Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding." +
+                    "You also need to change vm.userdata.max.length value",

Review Comment:
   well, this is copied from `DeployVMCmd.java` and `UpdateVMCmd.java`



##########
api/src/main/java/com/cloud/network/router/VirtualRouterAutoScale.java:
##########
@@ -0,0 +1,117 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network.router;
+
+/**
+ *  bridge internal and external traffic.
+ */
+public interface VirtualRouterAutoScale {
+
+    enum VirtualRouterAutoScaleCounter {
+        NetworkReceivedBps ("virtual.network.received.Bps"),
+        NetworkTransmitBps ("virtual.network.transmit.Bps"),
+        LbAverageConnections ("virtual.network.lb.average.connections");

Review Comment:
   done



##########
api/src/main/java/com/cloud/network/as/AutoScalePolicy.java:
##########
@@ -21,9 +21,26 @@
 
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.commons.lang3.StringUtils;
 
 public interface AutoScalePolicy extends ControlledEntity, InternalIdentity {
 
+    enum Action {
+        ScaleUp, ScaleDown;

Review Comment:
   changed to upper case



##########
api/src/main/java/com/cloud/network/as/Counter.java:
##########
@@ -20,19 +20,27 @@
 import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 
+import java.util.Arrays;
+import java.util.List;
+
 public interface Counter extends InternalIdentity, Identity {
 
-    public static enum Source {
+    enum Source {

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java:
##########
@@ -80,9 +80,11 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
     private Long templateId;
 
     @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS,
-               type = CommandType.STRING,
-               description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine")
-    private String otherDeployParams;
+               type = CommandType.MAP,
+               description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine. "
+                       + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 ."
+                       + "possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\", \"networkids\".")

Review Comment:
   done, thanks 



##########
api/src/main/java/com/cloud/network/router/VirtualRouterAutoScale.java:
##########
@@ -0,0 +1,117 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network.router;
+
+/**
+ *  bridge internal and external traffic.
+ */
+public interface VirtualRouterAutoScale {
+
+    enum VirtualRouterAutoScaleCounter {
+        NetworkReceivedBps ("virtual.network.received.Bps"),
+        NetworkTransmitBps ("virtual.network.transmit.Bps"),
+        LbAverageConnections ("virtual.network.lb.average.connections");
+
+        String _value;
+        VirtualRouterAutoScaleCounter(String value) {
+            _value = value;
+        }
+
+        String getValue() {
+            return _value;
+        }

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java:
##########
@@ -75,6 +83,25 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd {
                description = "counterparam list. Example: counterparam[0].name=snmpcommunity&counterparam[0].value=public&counterparam[1].name=snmpport&counterparam[1].value=161")
     private Map counterParamList;
 
+    @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS,
+            type = CommandType.MAP,
+            description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine. "
+                    + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 ."
+                    + "possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\", \"networkids\".",

Review Comment:
   done



##########
api/src/main/java/com/cloud/network/as/Counter.java:
##########
@@ -20,19 +20,27 @@
 import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 
+import java.util.Arrays;
+import java.util.List;
+
 public interface Counter extends InternalIdentity, Identity {
 
-    public static enum Source {
+    enum Source {
         netscaler,
         snmp,
         cpu,
-        memory
+        memory,
+        virtualrouter
     }
 
+    List<Source> NativeSources = Arrays.asList(Source.cpu, Source.memory);

Review Comment:
   we will not backport it to java 8, since currently it works well, I will not change it



##########
plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java:
##########
@@ -4020,7 +4051,12 @@ public VDI mount(final Connection conn, final String vmName, final DiskTO volume
         }
     }
 
+
     public String networkUsage(final Connection conn, final String privateIpAddress, final String option, final String vif) {
+        return networkUsage(conn, privateIpAddress, option, vif, null);
+    }
+
+    public String networkUsage(final Connection conn, final String privateIpAddress, final String option, final String vif, final String publicIp) {

Review Comment:
   this method is overrided in other classes
   `XenServer56Resource` and `XcpServerResource`



##########
api/src/main/java/com/cloud/network/router/VirtualRouterAutoScale.java:
##########
@@ -0,0 +1,117 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.network.router;
+
+/**
+ *  bridge internal and external traffic.
+ */
+public interface VirtualRouterAutoScale {
+
+    enum VirtualRouterAutoScaleCounter {
+        NetworkReceivedBps ("virtual.network.received.Bps"),
+        NetworkTransmitBps ("virtual.network.transmit.Bps"),
+        LbAverageConnections ("virtual.network.lb.average.connections");
+
+        String _value;
+        VirtualRouterAutoScaleCounter(String value) {
+            _value = value;
+        }
+
+        String getValue() {
+            return _value;
+        }
+        public static VirtualRouterAutoScaleCounter fromValue(String value) {
+            VirtualRouterAutoScaleCounter[] values = VirtualRouterAutoScaleCounter.values();
+            for(VirtualRouterAutoScaleCounter v : values) {
+                if(v.getValue().equals(value)) {
+                    return v;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return _value;

Review Comment:
   done



##########
engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmGroupStatisticsVO.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.cloud.network.as;
+
+import java.util.Date;
+
+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;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import com.cloud.network.router.VirtualRouterAutoScale;
+import com.cloud.server.ResourceTag;
+import org.apache.cloudstack.api.InternalIdentity;
+
+@Entity
+@Table(name = "autoscale_vmgroup_statistics")
+public class AutoScaleVmGroupStatisticsVO implements InternalIdentity {
+
+    public enum State {
+        Active, Inactive

Review Comment:
   changed to upper case



##########
engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql:
##########
@@ -27,3 +27,279 @@ WHERE so.default_use = 1 AND so.vm_type IN ('domainrouter', 'secondarystoragevm'
 -- Add cidr_list column to load_balancing_rules
 ALTER TABLE `cloud`.`load_balancing_rules`
 ADD cidr_list VARCHAR(4096);
+
+-- VM autoscaling
+
+-- Idempotent ADD COLUMN
+DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`;
+CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` (
+    IN in_table_name VARCHAR(200)
+, IN in_column_name VARCHAR(200)
+, IN in_column_definition VARCHAR(1000)
+)
+BEGIN
+    DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END;
+
+-- Idempotent ADD UNIQUE KEY
+DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`;
+CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY` (
+    IN in_table_name VARCHAR(200)
+, IN in_key_name VARCHAR(200)
+, IN in_key_definition VARCHAR(1000)
+)
+BEGIN
+    DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD UNIQUE KEY ', in_key_name); SET @ddl = CONCAT(@ddl, ' ', in_key_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END;

Review Comment:
   `IDEMPOTENT_ADD_UNIQUE_KEY` is a new procedure defined in this PR.
   
   `IDEMPOTENT_ADD_COLUMN` is not new.
   we could trust it. although the procedure might be changed in different versions (I have not compared them).
   
   the change on `cidr_list` does not belong to this PR,  i will not touch it
   



##########
scripts/vm/hypervisor/xenserver/perfmon.py:
##########
@@ -87,8 +87,8 @@ 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)
+        #pp = pprint.PrettyPrinter(indent=4)
+        #pp.pprint(self.vm_reports)

Review Comment:
   this file is not used by this PR
   
   I just reformat the file (no other changes)



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));

Review Comment:
   done



##########
server/src/main/java/com/cloud/vm/UserVmManagerImpl.java:
##########
@@ -4532,7 +4543,7 @@ public void validateRootDiskResize(final HypervisorType hypervisorType, Long roo
         // rootdisksize must be larger than template.
         boolean isIso = ImageFormat.ISO == templateVO.getFormat();
         if ((rootDiskSize << 30) < templateVO.getSize()) {
-            String error = "Unsupported: rootdisksize override is smaller than template size " + toHumanReadableSize(templateVO.getSize());
+            String error = String.format("Unsupported: rootdisksize override (%s GB) is smaller than template size %s", rootDiskSize, toHumanReadableSize(templateVO.getSize()));

Review Comment:
   this is used to display "(rootDiskSize GB)"



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(XcpServerResource.getConnection()).thenReturn(conn);
+        when(XcpServerResource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(XcpServerResource.getConnection()).thenReturn(conn);
+        when(XcpServerResource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));

Review Comment:
   done



##########
core/src/main/java/com/cloud/agent/api/routing/GetAutoScaleMetricsAnswer.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.cloud.agent.api.routing;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.LogLevel;
+import com.cloud.agent.api.LogLevel.Log4jLevel;
+import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleMetricsValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@LogLevel(Log4jLevel.Debug)

Review Comment:
   because I want to display the answer in management-server.log (by default it does not) , it will be helpful for troubleshooting



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java:
##########
@@ -75,6 +83,25 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd {
                description = "counterparam list. Example: counterparam[0].name=snmpcommunity&counterparam[0].value=public&counterparam[1].name=snmpport&counterparam[1].value=161")
     private Map counterParamList;
 
+    @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS,
+            type = CommandType.MAP,
+            description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine. "
+                    + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 ."
+                    + "possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\", \"networkids\".",
+            since = "4.18.0")
+    private Map otherDeployParams;
+
+    @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 4KB of data after base64 encoding. " +
+                    "Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding." +
+                    "You also need to change vm.userdata.max.length value",

Review Comment:
   it is copied from `UpdateVMCmd.java`
   let's discuss afterwards



##########
server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java:
##########
@@ -0,0 +1,223 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES 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.exception.InvalidParameterValueException;
+import com.cloud.network.as.dao.AutoScalePolicyDao;
+import com.cloud.network.as.dao.ConditionDao;
+import com.cloud.network.as.dao.CounterDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.AccountDao;
+import org.apache.cloudstack.api.ApiCmdTestUtil;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateConditionCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.lang.reflect.Field;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AutoScaleManagerImplTest {
+
+    final static String INVALID = "invalid";
+
+    @Spy
+    @InjectMocks
+    AutoScaleManagerImpl autoScaleManagerImplMock;
+
+    @Mock
+    CounterDao _counterDao;
+
+    @Mock
+    CounterVO counterMock;
+
+    @Mock
+    ConditionDao _conditionDao;
+
+    @Mock
+    ConditionVO conditionMock;
+
+    @Mock
+    AutoScalePolicyDao _asPolicyDao;
+
+    @Mock
+    AutoScalePolicyVO asPolicyMock;
+
+    @Mock
+    AccountManager _accountMgr;
+
+    @Mock
+    AccountDao _accountDao;
+
+    @Mock
+    AccountVO accountMock;
+
+    AccountVO account;
+    UserVO user;
+
+    @Before
+    public void setUp() {
+
+        account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
+        account.setId(2L);
+        user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
+                UUID.randomUUID().toString(), User.Source.UNKNOWN);
+        CallContext.register(user, account);
+
+        when(_counterDao.persist(any(CounterVO.class))).thenReturn(counterMock);
+        when(_counterDao.findById(anyLong())).thenReturn(counterMock);
+        when(_conditionDao.persist(any(ConditionVO.class))).thenReturn(conditionMock);
+
+        when(_accountMgr.finalizeOwner(nullable(Account.class), nullable(String.class), nullable(Long.class), nullable(Long.class))).thenReturn(account);
+
+        when(_asPolicyDao.persist(any(AutoScalePolicyVO.class))).thenReturn(asPolicyMock);
+    }
+
+    @After
+    public void tearDown() {
+        CallContext.unregister();
+    }
+
+    @Test
+    public void testCreateCounterCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, "VirtualRouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, "virtualrouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testCreateCounterCmdWithInvalidSource() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, "VirtualRouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, INVALID);
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testCreateCounterCmdWithInvalidProvider() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, INVALID);
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, "virtualrouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    private void seCommandField(BaseCmd cmd, String fieldName, Object value) throws IllegalAccessException {
+        Field field = null;
+        try {
+            field = cmd.getClass().getDeclaredField(fieldName);
+        } catch (NoSuchFieldException exception) {
+            System.out.println(String.format("Cannot find field %s in command %s", fieldName, cmd.getClass().getName()));
+        }
+        if (field == null) {
+            try {
+                field = cmd.getClass().getSuperclass().getDeclaredField(fieldName);
+            } catch (NoSuchFieldException exception) {
+                System.out.println(String.format("Cannot find field %s in command %s", fieldName, cmd.getClass().getSuperclass().getName()));
+                return;
+            }
+        }
+        field.setAccessible(true);
+        field.set(cmd, value);
+    }
+
+    @Test
+    public void testCreateConditionCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateConditionCmd cmd = new CreateConditionCmd();
+
+        seCommandField(cmd, "counterId", 1L);
+        seCommandField(cmd, "relationalOperator", "LT");
+        seCommandField(cmd, "threshold", 100L);
+
+        autoScaleManagerImplMock.createCondition(cmd);
+    }

Review Comment:
   unit test is still in progress. 
   I will add it



##########
server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java:
##########
@@ -0,0 +1,223 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES 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.exception.InvalidParameterValueException;
+import com.cloud.network.as.dao.AutoScalePolicyDao;
+import com.cloud.network.as.dao.ConditionDao;
+import com.cloud.network.as.dao.CounterDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.AccountDao;
+import org.apache.cloudstack.api.ApiCmdTestUtil;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateConditionCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.lang.reflect.Field;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AutoScaleManagerImplTest {
+
+    final static String INVALID = "invalid";
+
+    @Spy
+    @InjectMocks
+    AutoScaleManagerImpl autoScaleManagerImplMock;
+
+    @Mock
+    CounterDao _counterDao;
+
+    @Mock
+    CounterVO counterMock;
+
+    @Mock
+    ConditionDao _conditionDao;
+
+    @Mock
+    ConditionVO conditionMock;
+
+    @Mock
+    AutoScalePolicyDao _asPolicyDao;
+
+    @Mock
+    AutoScalePolicyVO asPolicyMock;
+
+    @Mock
+    AccountManager _accountMgr;
+
+    @Mock
+    AccountDao _accountDao;
+
+    @Mock
+    AccountVO accountMock;
+
+    AccountVO account;
+    UserVO user;
+
+    @Before
+    public void setUp() {
+
+        account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
+        account.setId(2L);
+        user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
+                UUID.randomUUID().toString(), User.Source.UNKNOWN);
+        CallContext.register(user, account);
+
+        when(_counterDao.persist(any(CounterVO.class))).thenReturn(counterMock);
+        when(_counterDao.findById(anyLong())).thenReturn(counterMock);
+        when(_conditionDao.persist(any(ConditionVO.class))).thenReturn(conditionMock);
+
+        when(_accountMgr.finalizeOwner(nullable(Account.class), nullable(String.class), nullable(Long.class), nullable(Long.class))).thenReturn(account);
+
+        when(_asPolicyDao.persist(any(AutoScalePolicyVO.class))).thenReturn(asPolicyMock);
+    }
+
+    @After
+    public void tearDown() {
+        CallContext.unregister();
+    }
+
+    @Test
+    public void testCreateCounterCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, "VirtualRouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, "virtualrouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testCreateCounterCmdWithInvalidSource() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, "VirtualRouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, INVALID);
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testCreateCounterCmdWithInvalidProvider() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, INVALID);
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, "virtualrouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }
+
+    private void seCommandField(BaseCmd cmd, String fieldName, Object value) throws IllegalAccessException {
+        Field field = null;
+        try {
+            field = cmd.getClass().getDeclaredField(fieldName);
+        } catch (NoSuchFieldException exception) {
+            System.out.println(String.format("Cannot find field %s in command %s", fieldName, cmd.getClass().getName()));
+        }
+        if (field == null) {
+            try {
+                field = cmd.getClass().getSuperclass().getDeclaredField(fieldName);
+            } catch (NoSuchFieldException exception) {
+                System.out.println(String.format("Cannot find field %s in command %s", fieldName, cmd.getClass().getSuperclass().getName()));
+                return;
+            }
+        }
+        field.setAccessible(true);
+        field.set(cmd, value);
+    }
+
+    @Test
+    public void testCreateConditionCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateConditionCmd cmd = new CreateConditionCmd();
+
+        seCommandField(cmd, "counterId", 1L);
+        seCommandField(cmd, "relationalOperator", "LT");
+        seCommandField(cmd, "threshold", 100L);
+
+        autoScaleManagerImplMock.createCondition(cmd);
+    }
+
+    @Test(expected = InvalidParameterValueException.class)
+    public void testCreateConditionCmdWithInvalidOperator() throws IllegalArgumentException, IllegalAccessException {
+        CreateConditionCmd cmd = new CreateConditionCmd();
+
+        seCommandField(cmd, "counterId", 1L);
+        seCommandField(cmd, "relationalOperator", INVALID);
+        seCommandField(cmd, "threshold", 100L);
+
+        autoScaleManagerImplMock.createCondition(cmd);
+    }
+
+    @Test
+    public void testCreateAutoScalePolicyCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateAutoScalePolicyCmd cmd = new CreateAutoScalePolicyCmd() {
+            @Override
+            public long getEntityOwnerId() {
+                return 2;
+            }
+
+            @Override
+            public long getDomainId() {
+                return 1L;
+            }
+
+            @Override
+            public long getAccountId() {
+                return 2L;
+            }
+        };
+
+        seCommandField(cmd, "action", "ScaleUp");
+        seCommandField(cmd, "duration", 300);
+
+        autoScaleManagerImplMock.createAutoScalePolicy(cmd);

Review Comment:
   unit test is still in progress.
   I will add it



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(XcpServerResource.getConnection()).thenReturn(conn);
+        when(XcpServerResource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java:
##########
@@ -0,0 +1,223 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES 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.exception.InvalidParameterValueException;
+import com.cloud.network.as.dao.AutoScalePolicyDao;
+import com.cloud.network.as.dao.ConditionDao;
+import com.cloud.network.as.dao.CounterDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.AccountDao;
+import org.apache.cloudstack.api.ApiCmdTestUtil;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd;
+import org.apache.cloudstack.api.command.user.autoscale.CreateConditionCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.lang.reflect.Field;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AutoScaleManagerImplTest {
+
+    final static String INVALID = "invalid";
+
+    @Spy
+    @InjectMocks
+    AutoScaleManagerImpl autoScaleManagerImplMock;
+
+    @Mock
+    CounterDao _counterDao;
+
+    @Mock
+    CounterVO counterMock;
+
+    @Mock
+    ConditionDao _conditionDao;
+
+    @Mock
+    ConditionVO conditionMock;
+
+    @Mock
+    AutoScalePolicyDao _asPolicyDao;
+
+    @Mock
+    AutoScalePolicyVO asPolicyMock;
+
+    @Mock
+    AccountManager _accountMgr;
+
+    @Mock
+    AccountDao _accountDao;
+
+    @Mock
+    AccountVO accountMock;
+
+    AccountVO account;
+    UserVO user;
+
+    @Before
+    public void setUp() {
+
+        account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
+        account.setId(2L);
+        user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
+                UUID.randomUUID().toString(), User.Source.UNKNOWN);
+        CallContext.register(user, account);
+
+        when(_counterDao.persist(any(CounterVO.class))).thenReturn(counterMock);
+        when(_counterDao.findById(anyLong())).thenReturn(counterMock);
+        when(_conditionDao.persist(any(ConditionVO.class))).thenReturn(conditionMock);
+
+        when(_accountMgr.finalizeOwner(nullable(Account.class), nullable(String.class), nullable(Long.class), nullable(Long.class))).thenReturn(account);
+
+        when(_asPolicyDao.persist(any(AutoScalePolicyVO.class))).thenReturn(asPolicyMock);
+    }
+
+    @After
+    public void tearDown() {
+        CallContext.unregister();
+    }
+
+    @Test
+    public void testCreateCounterCmd() throws IllegalArgumentException, IllegalAccessException {
+        CreateCounterCmd cmd = new CreateCounterCmd();
+        ApiCmdTestUtil.set(cmd, ApiConstants.NAME, "test-name");
+        ApiCmdTestUtil.set(cmd, ApiConstants.PROVIDER, "VirtualRouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.SOURCE, "virtualrouter");
+        ApiCmdTestUtil.set(cmd, ApiConstants.VALUE, "test-value");
+
+        autoScaleManagerImplMock.createCounter(cmd);
+    }

Review Comment:
   unit test is still in progress. 
   I will add it



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(XcpServerResource.getConnection()).thenReturn(conn);
+        when(XcpServerResource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[1]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java:
##########
@@ -60,6 +66,10 @@ public Long getId() {
         return id;
     }
 
+    public Boolean getCleanup() {
+        return cleanup != null ? cleanup : false;

Review Comment:
   good, done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.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 org.apache.cloudstack.api.command.user.autoscale;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.api.ACL;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+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.ConditionResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ResourceInUseException;
+import com.cloud.network.as.Condition;
+import com.cloud.user.Account;
+
+@APICommand(name = "updateCondition", description = "Updates a condition for VM auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class},
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0")
+public class UpdateConditionCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = Logger.getLogger(UpdateConditionCmd.class.getName());
+    private static final String s_name = "updateconditionresponse";

Review Comment:
   changed to `API_NAME` (same as other apis)



##########
test/integration/smoke/test_deploy_vm_root_resize.py:
##########
@@ -386,7 +386,7 @@ def test_02_deploy_vm_root_resize(self):
                         rootdisksize=newrootsize
                 )
             except Exception as ex:
-                    if "rootdisksize override is smaller than template size" in str(ex):
+                    if "rootdisksize override (" + str(newrootsize) + " GB) is smaller than template size" in str(ex):

Review Comment:
   newrootsize is a value in GB, no need to use toHumanReadable



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java:
##########
@@ -75,6 +83,25 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd {
                description = "counterparam list. Example: counterparam[0].name=snmpcommunity&counterparam[0].value=public&counterparam[1].name=snmpport&counterparam[1].value=161")
     private Map counterParamList;
 
+    @Parameter(name = ApiConstants.OTHER_DEPLOY_PARAMS,
+            type = CommandType.MAP,
+            description = "parameters other than zoneId/serviceOfferringId/templateId of the auto deployed virtual machine. "
+                    + "Example: otherdeployparams[0].name=serviceofferingid&otherdeployparams[0].value=a7fb50f6-01d9-11ed-8bc1-77f8f0228926&otherdeployparams[1].name=rootdisksize&otherdeployparams[1].value=10 ."
+                    + "possible parameters are \"rootdisksize\", \"diskofferingid\",\"size\", \"securitygroupids\", \"overridediskofferingid\", \"keypairs\", \"affinitygroupids'\", \"networkids\".",
+            since = "4.18.0")
+    private Map otherDeployParams;

Review Comment:
   well, actually the value of Map is not String, but HashMap<String, String>
   
   let me try with
   Map<String, HashMap<String, String>> otherDeployParams
   



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java:
##########
@@ -109,14 +136,26 @@ public Long getId() {
         return id;
     }
 
+    public Long getServiceOfferingId() {
+        return serviceOfferingId;
+    }
+
     public Long getTemplateId() {
         return templateId;
     }
 
+    public Map getOtherDeployParams() {

Review Comment:
   will try with
   `Map<String, HashMap<String, String>>`



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java:
##########
@@ -1141,10 +1151,63 @@ protected NetworkUsageAnswer VPCNetworkUsage(NetworkUsageCommand cmd) {
                     stats[0] += Long.parseLong(splitResult[i++]);
                     stats[1] += Long.parseLong(splitResult[i++]);
                 }
-                return new NetworkUsageAnswer(cmd, "success", stats[0], stats[1]);
+                return stats;
             }
         }
-        return new NetworkUsageAnswer(cmd, "success", 0L, 0L);
+        return new long[2];
+    }
+
+    public long[] getNetworkLbStats(final String privateIp, final String publicIp, final Integer port) {
+        String args = publicIp + " " + port;
+        ExecutionResult callResult = executeInVR(privateIp, "get_haproxy_stats.sh", args);
+
+        if (!callResult.isSuccess()) {

Review Comment:
   the original code looks ok, I think



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateConditionCmd.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 org.apache.cloudstack.api.command.user.autoscale;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.api.ACL;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+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.ConditionResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ResourceInUseException;
+import com.cloud.network.as.Condition;
+import com.cloud.user.Account;
+
+@APICommand(name = "updateCondition", description = "Updates a condition for VM auto scaling", responseObject = SuccessResponse.class, entityType = {Condition.class},
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.18.0")
+public class UpdateConditionCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = Logger.getLogger(UpdateConditionCmd.class.getName());

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));

Review Comment:
   done



##########
api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/DeleteAutoScaleVmGroupCmd.java:
##########
@@ -60,6 +66,10 @@ public Long getId() {
         return id;
     }
 
+    public Boolean getCleanup() {
+        return cleanup != null ? cleanup : false;

Review Comment:
   just realize it should be `cleanup != null && cleanup`
   



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(xenServer56Resource.getConnection()).thenReturn(conn);
+        when(xenServer56Resource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[1]));

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +463,75 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "netusage.sh", args)).thenReturn(executionResult);
+
+        args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        PowerMockito.when(_resource.executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args)).thenReturn(executionResult);
+
+        Answer answer = _resource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
plugins/network-elements/netscaler/src/main/java/com/cloud/network/element/NetscalerElement.java:
##########
@@ -210,6 +209,8 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl
     NetworkOfferingDao _networkOfferingDao = null;
     @Inject
     NetScalerVMManager _netScalerVMManager;
+    @Inject
+    LoadBalancingRulesManager _lbRulesManager;

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(xenServer56Resource.getConnection()).thenReturn(conn);
+        when(xenServer56Resource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[1]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(xenServer56Resource.getConnection()).thenReturn(conn);
+        when(xenServer56Resource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java:
##########
@@ -213,6 +214,8 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
     PhysicalNetworkServiceProviderDao _physicalProviderDao;
     @Inject
     VirtualRouterProviderDao _vrProviderDao;
+    @Inject
+    private LoadBalancingRulesManager _lbRulesManager;

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -178,11 +237,81 @@
     @Inject
     UserVmService _userVmService;
     @Inject
-    UserVmManager _userVmManager;
-    @Inject
     LoadBalancerVMMapDao _lbVmMapDao;
     @Inject
     LoadBalancingRulesService _loadBalancingRulesService;
+    @Inject
+    private VMInstanceDao _vmInstanceDao;
+    @Inject
+    private AgentManager _agentMgr;
+    @Inject
+    private ServiceOfferingDao _serviceOfferingDao;
+    @Inject
+    NetworkOrchestrationService _networkMgr;
+    @Inject
+    private UserVmManager _userVmMgr;
+    @Inject
+    private UserVmDao _userVmDao;
+    @Inject
+    private HostDao _hostDao;
+    @Inject
+    private AutoScaleVmGroupStatisticsDao _asGroupStatisticsDao;
+    @Inject
+    private DomainRouterDao _routerDao;
+    @Inject
+    private AnnotationDao _annotationDao;
+    @Inject
+    protected RouterControlHelper _routerControlHelper;
+    @Inject
+    private DiskOfferingDao _diskOfferingDao;
+    @Inject
+    private SSHKeyPairDao _sshKeyPairDao;
+    @Inject
+    private AffinityGroupDao _affinityGroupDao;

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManager.java:
##########
@@ -16,11 +16,42 @@
 // under the License.
 package com.cloud.network.as;
 
+import org.apache.cloudstack.framework.config.ConfigKey;
+
 public interface AutoScaleManager extends AutoScaleService {
 
+    ConfigKey<Integer> AutoScaleStatsInterval = new ConfigKey<>("Advanced", Integer.class,
+            "autoscale.stats.interval",
+            "60000",
+            "The interval (in milliseconds) when VM auto scaling statistics are processed to determine and perform scale action. Less than 1 means disabled.",
+            false);
+
+    ConfigKey<Integer> AutoScaleStatsCleanupDelay = new ConfigKey<>(Integer.class,

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer56WrapperTest.java:
##########
@@ -157,6 +170,76 @@ public void testNetworkUsageCommandCreateVpcFailure() {
         assertFalse(answer.getResult());
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(xenServer56Resource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", false, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Connection conn = Mockito.mock(Connection.class);
+        when(xenServer56Resource.getConnection()).thenReturn(conn);
+        when(xenServer56Resource.getNetworkStats(conn, getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(networkStats);
+        when(xenServer56Resource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, xenServer56Resource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -246,7 +375,7 @@ private <VO extends ControlledEntity> VO getEntityInDatabase(Account caller, Str
     }
 
     private boolean isAutoScaleScaleUpPolicy(AutoScalePolicy policyVO) {
-        return policyVO.getAction().equals("scaleup");
+        return policyVO.getAction().equals(AutoScalePolicy.Action.ScaleUp);
     }
 
     private List<AutoScalePolicyVO> getAutoScalePolicies(String paramName, List<Long> policyIds, List<Counter> counters, int interval, boolean scaleUpPolicies) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long overrideDiskOfferingId = null;     // override ROOT disk offering
+            if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) {
+                String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID);
+                DiskOffering overrideDiskOfferingInParam = _diskOfferingDao.findByUuid(overrideDiskOfferingUuid);
+                if (overrideDiskOfferingInParam != null) {
+                    overrideDiskOfferingId = overrideDiskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long diskOfferingId = null;     // DATA disk offering ID
+            if (deployParams.get(PARAM_DISK_OFFERING_ID) != null) {
+                String diskOfferingUuid = deployParams.get(PARAM_DISK_OFFERING_ID);
+                DiskOffering diskOfferingInParam = _diskOfferingDao.findByUuid(diskOfferingUuid);
+                if (diskOfferingInParam != null) {
+                    diskOfferingId = diskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by diskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long dataDiskSize = null;       // DATA disk size
+            if (deployParams.get(PARAM_DISK_SIZE) != null) {
+                String dataDiskSizeInParam = deployParams.get(PARAM_DISK_SIZE);
+                try {
+                    dataDiskSize = Long.parseLong(dataDiskSizeInParam);
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse size from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+
+            if (deployParams.get(PARAM_SSH_KEYPAIRS) != null) {   // SSH keypairs
+                String[] keypairs = deployParams.get(PARAM_SSH_KEYPAIRS).split(",");
+                for (String keypair : keypairs) {
+                    SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), keypair);
+                    if (s != null) {
+                        sshKeyPairs.add(s.getName());
+                    } else {
+                        s_logger.warn("Cannot find ssh keypair by name in sshkeypairs from otherdeployparams in AutoScale Vm profile");
+                    }
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -390,15 +553,22 @@ public AutoScaleVmProfile createAutoScaleVmProfile(CreateAutoScaleVmProfileCmd c
     @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMPROFILE_UPDATE, eventDescription = "updating autoscale vm profile")
     public AutoScaleVmProfile updateAutoScaleVmProfile(UpdateAutoScaleVmProfileCmd cmd) {
         Long profileId = cmd.getId();
+        Long serviceOfferingId = cmd.getServiceOfferingId();
         Long templateId = cmd.getTemplateId();
         Long autoscaleUserId = cmd.getAutoscaleUserId();
+        Map otherDeployParams = cmd.getOtherDeployParams();

Review Comment:
   as said, I will use `Map<String, HashMap<String, String>>`



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (VirtualRouterAutoScale.AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));

Review Comment:
   done



##########
plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XcpServerWrapperTest.java:
##########
@@ -113,4 +124,75 @@ public void testNetworkUsageCommandExceptiopn() {
 
         assertFalse(answer.getResult());
     }
+
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+        List<VirtualRouterAutoScale.AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new VirtualRouterAutoScale.AutoScaleMetrics(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        final GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.10", true, "10.10.10.10", 8080, metrics);
+
+        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        when(XcpServerResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP())).thenReturn(vpcStats);
+        when(XcpServerResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(),  getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort())).thenReturn(lbStats);
+
+        final Answer answer = wrapper.execute(getAutoScaleMetricsCommand, XcpServerResource);
+        assertTrue(answer.getResult());
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<VirtualRouterAutoScale.AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long overrideDiskOfferingId = null;     // override ROOT disk offering
+            if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) {
+                String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID);
+                DiskOffering overrideDiskOfferingInParam = _diskOfferingDao.findByUuid(overrideDiskOfferingUuid);
+                if (overrideDiskOfferingInParam != null) {
+                    overrideDiskOfferingId = overrideDiskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long diskOfferingId = null;     // DATA disk offering ID
+            if (deployParams.get(PARAM_DISK_OFFERING_ID) != null) {
+                String diskOfferingUuid = deployParams.get(PARAM_DISK_OFFERING_ID);
+                DiskOffering diskOfferingInParam = _diskOfferingDao.findByUuid(diskOfferingUuid);
+                if (diskOfferingInParam != null) {
+                    diskOfferingId = diskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by diskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long dataDiskSize = null;       // DATA disk size
+            if (deployParams.get(PARAM_DISK_SIZE) != null) {
+                String dataDiskSizeInParam = deployParams.get(PARAM_DISK_SIZE);
+                try {
+                    dataDiskSize = Long.parseLong(dataDiskSizeInParam);
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse size from otherdeployparams in AutoScale Vm profile");
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -824,18 +1019,36 @@ private boolean configureAutoScaleVmGroup(long vmGroupid, String currentState) t
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_DELETE, eventDescription = "deleting autoscale vm group", async = true)
-    public boolean deleteAutoScaleVmGroup(final long id) {
+    public boolean deleteAutoScaleVmGroup(final long id, final Boolean cleanup) {
         AutoScaleVmGroupVO autoScaleVmGroupVO = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", id, _autoScaleVmGroupDao);
 
-        if (autoScaleVmGroupVO.getState().equals(AutoScaleVmGroup.State_New)) {
+        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);
+
+        if (!autoScaleVmGroupVO.getState().equals(AutoScaleVmGroup.State.Disabled) && !Boolean.TRUE.equals(cleanup)) {
+            throw new InvalidParameterValueException(String.format("Cannot delete autoscale vm group id : %d because it is in %s state. Please disable it or pass cleanup=true flag which will destroy all VMs.", id, autoScaleVmGroupVO.getState()));
+        }
+
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(id);
+        if (currentVM > 0 && !Boolean.TRUE.equals(cleanup)) {
+            throw new InvalidParameterValueException(String.format("Cannot delete autoscale vm group id : %d because there are %d VMs. Please remove the VMs or pass cleanup=true flag which will destroy all VMs.", id, currentVM));
+        }
+
+        AutoScaleVmGroup.State bakupState = autoScaleVmGroupVO.getState();
+        autoScaleVmGroupVO.setState(AutoScaleVmGroup.State.Revoke);
         _autoScaleVmGroupDao.persist(autoScaleVmGroupVO);
         boolean success = false;
 
+        if (cleanup) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }

Review Comment:
   done. removed this method



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -258,22 +387,12 @@ private List<AutoScalePolicyVO> getAutoScalePolicies(String paramName, List<Long
         sc.setParameters("ids", policyIds.toArray(new Object[0]));
         List<AutoScalePolicyVO> policies = _autoScalePolicyDao.search(sc, null);
 
-        int prevQuietTime = 0;
-
         for (AutoScalePolicyVO policy : policies) {
-            int quietTime = policy.getQuietTime();
-            if (prevQuietTime == 0) {
-                prevQuietTime = quietTime;
-            }
             int duration = policy.getDuration();
             if (duration < interval) {
                 throw new InvalidParameterValueException("duration : " + duration + " specified in a policy cannot be less than vm group's interval : " + interval);
             }
 
-            if (quietTime != prevQuietTime) {
-                throw new InvalidParameterValueException("quietTime should be same for all the policies specified in " + paramName);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1448,27 +1906,37 @@ public void doScaleUp(long groupId, Integer numVm) {
         if (!checkConditionUp(asGroup, numVm)) {
             return;
         }
+        AutoScaleVmGroup.State oldState = asGroup.getState();
+        AutoScaleVmGroup.State newState = AutoScaleVmGroup.State.Scaling;
+        if (!_autoScaleVmGroupDao.updateState(groupId, oldState, newState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s to %s, groupId: %s", oldState, newState, groupId));
+            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 (startNewVM(vmId)) {
+            // persist to DB
+            AutoScaleVmGroupVmMapVO GroupVmVO = new AutoScaleVmGroupVmMapVO(asGroup.getId(), vmId);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long overrideDiskOfferingId = null;     // override ROOT disk offering
+            if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) {
+                String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID);
+                DiskOffering overrideDiskOfferingInParam = _diskOfferingDao.findByUuid(overrideDiskOfferingUuid);
+                if (overrideDiskOfferingInParam != null) {
+                    overrideDiskOfferingId = overrideDiskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long diskOfferingId = null;     // DATA disk offering ID
+            if (deployParams.get(PARAM_DISK_OFFERING_ID) != null) {
+                String diskOfferingUuid = deployParams.get(PARAM_DISK_OFFERING_ID);
+                DiskOffering diskOfferingInParam = _diskOfferingDao.findByUuid(diskOfferingUuid);
+                if (diskOfferingInParam != null) {
+                    diskOfferingId = diskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by diskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long dataDiskSize = null;       // DATA disk size
+            if (deployParams.get(PARAM_DISK_SIZE) != null) {
+                String dataDiskSizeInParam = deployParams.get(PARAM_DISK_SIZE);
+                try {
+                    dataDiskSize = Long.parseLong(dataDiskSizeInParam);
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse size from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+
+            if (deployParams.get(PARAM_SSH_KEYPAIRS) != null) {   // SSH keypairs
+                String[] keypairs = deployParams.get(PARAM_SSH_KEYPAIRS).split(",");
+                for (String keypair : keypairs) {
+                    SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), keypair);
+                    if (s != null) {
+                        sshKeyPairs.add(s.getName());
+                    } else {
+                        s_logger.warn("Cannot find ssh keypair by name in sshkeypairs from otherdeployparams in AutoScale Vm profile");
+                    }
+                }
+            }
+
+            List<Long> affinityGroupIdList = new ArrayList<>();
+            if (deployParams.get(PARAM_AFFINITY_GROUP_IDS) != null) {   // Affinity groups
+                String[] affinityGroupIds = deployParams.get(PARAM_AFFINITY_GROUP_IDS).split(",");
+                for (String affinityGroupId : affinityGroupIds) {
+                    AffinityGroupVO affintyGroup = _affinityGroupDao.findByUuid(affinityGroupId);
+                    if (affintyGroup != null) {
+                        affinityGroupIdList.add(affintyGroup.getId());
+                    } else {
+                        s_logger.warn("Cannot find affinity group by affinitygroupids from otherdeployparams in AutoScale Vm profile");
+                    }
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long overrideDiskOfferingId = null;     // override ROOT disk offering
+            if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) {
+                String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID);
+                DiskOffering overrideDiskOfferingInParam = _diskOfferingDao.findByUuid(overrideDiskOfferingUuid);
+                if (overrideDiskOfferingInParam != null) {
+                    overrideDiskOfferingId = overrideDiskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long diskOfferingId = null;     // DATA disk offering ID
+            if (deployParams.get(PARAM_DISK_OFFERING_ID) != null) {
+                String diskOfferingUuid = deployParams.get(PARAM_DISK_OFFERING_ID);
+                DiskOffering diskOfferingInParam = _diskOfferingDao.findByUuid(diskOfferingUuid);
+                if (diskOfferingInParam != null) {
+                    diskOfferingId = diskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by diskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1325,23 +1690,112 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) {
                 }
             }
 
+            String userData = profileVo.getUserData();
+
             UserVm vm = null;
             IpAddresses addrs = new IpAddresses(null, null);
+            HypervisorType hypervisorType = template.getHypervisorType();
+            final Network network = getNetwork(asGroup.getLoadBalancerId());
+            final List<Long> networkIds = new ArrayList<>();
+            networkIds.add(network.getId());
+            Map<String, String> customParameters = new HashMap<>();
+            List<String> sshKeyPairs = new ArrayList<>();
+
+            Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams());
+
+            if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid
+                String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(",");
+                for (String networkid : networkids) {
+                    Network otherNetwork = _networkDao.findByUuid(networkid);
+                    if (otherNetwork != null && otherNetwork.getId() != network.getId()) {
+                        networkIds.add(otherNetwork.getId());
+                    }
+                }
+            }
+            if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) {     // ROOT disk size
+                String value = deployParams.get(PARAM_ROOT_DISK_SIZE);
+                try {
+                    Long rootDiskSize = Long.parseLong(value);
+                    customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize));
+                } catch (NumberFormatException ex) {
+                    s_logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile");
+                }
+            }
+            Long overrideDiskOfferingId = null;     // override ROOT disk offering
+            if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) {
+                String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID);
+                DiskOffering overrideDiskOfferingInParam = _diskOfferingDao.findByUuid(overrideDiskOfferingUuid);
+                if (overrideDiskOfferingInParam != null) {
+                    overrideDiskOfferingId = overrideDiskOfferingInParam.getId();
+                } else {
+                    s_logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile");
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy

Review Comment:
   removed



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +465,107 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        when(vmwareResource.getVPCNetworkStats(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(vpcStats);
+        when(vmwareResource.getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class))).thenReturn(lbStats);
+
+        Answer answer = vmwareResource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        when(vmwareResource.getNetworkStats(Mockito.anyString(), Mockito.anyString())).thenReturn(networkStats);
+        when(vmwareResource.getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class))).thenReturn(lbStats);
+
+        Answer answer = vmwareResource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetStatsForVpcStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("vpc_netusage.sh"), Mockito.eq(args))).thenReturn(executionResult);
+
+        long[] stats = vmwareResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP(), "get", "");
+        assertEquals(stats.length, 2);
+        assertEquals(stats[0], vpcStats[0]);
+        assertEquals(stats[1], vpcStats[1]);
+    }
+
+    @Test
+    public void testGetStatsForNetworkStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, null);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("netusage.sh"), Mockito.eq(args))).thenReturn(executionResult);
+
+        long[] stats = vmwareResource.getNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP());
+        assertEquals(stats.length, 2);
+        assertEquals(stats[0], networkStats[0]);
+        assertEquals(stats[1], networkStats[1]);
+    }
+
+    @Test
+    public void testGetStatsForLbStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);
+
+        String args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        ExecutionResult executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("get_haproxy_stats.sh"), Mockito.eq(args))).thenReturn(executionResult);
+
+        long[] stats = vmwareResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort());
+
+        assertEquals(stats.length, 1);

Review Comment:
   done



##########
plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java:
##########
@@ -435,4 +465,107 @@ public void testConfigNestedHVSupportFlagFalse() throws Exception{
         verify(vmMo, never()).getRunningHost();
     }
 
+    @Test
+    public void testGetAutoScaleMetricsCommandForVpc() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);
+
+        when(vmwareResource.getVPCNetworkStats(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(vpcStats);
+        when(vmwareResource.getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class))).thenReturn(lbStats);
+
+        Answer answer = vmwareResource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(vpcStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetAutoScaleMetricsCommandForNetwork() {
+
+        List<AutoScaleMetrics> metrics = new ArrayList<>();
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LbAverageConnections, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkReceivedBps, 1L, 2L, 3L, 4));
+        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NetworkTransmitBps, 1L, 2L, 3L, 4));
+
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);
+
+        when(vmwareResource.getNetworkStats(Mockito.anyString(), Mockito.anyString())).thenReturn(networkStats);
+        when(vmwareResource.getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class))).thenReturn(lbStats);
+
+        Answer answer = vmwareResource.execute(getAutoScaleMetricsCommand);
+        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);
+
+        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
+        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();
+
+        assertEquals(values.size(), 3);
+        for (AutoScaleMetricsValue value : values) {
+            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LbAverageConnections)) {
+                assertEquals(value.getValue(), Double.valueOf(lbStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkTransmitBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[0]));
+            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NetworkReceivedBps)) {
+                assertEquals(value.getValue(), Double.valueOf(networkStats[1]));
+            }
+        }
+    }
+
+    @Test
+    public void testGetStatsForVpcStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);
+
+        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
+        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("vpc_netusage.sh"), Mockito.eq(args))).thenReturn(executionResult);
+
+        long[] stats = vmwareResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP(), "get", "");
+        assertEquals(stats.length, 2);
+        assertEquals(stats[0], vpcStats[0]);
+        assertEquals(stats[1], vpcStats[1]);
+    }
+
+    @Test
+    public void testGetStatsForNetworkStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, null);
+
+        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
+        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("netusage.sh"), Mockito.eq(args))).thenReturn(executionResult);
+
+        long[] stats = vmwareResource.getNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP());
+        assertEquals(stats.length, 2);
+        assertEquals(stats[0], networkStats[0]);
+        assertEquals(stats[1], networkStats[1]);
+    }
+
+    @Test
+    public void testGetStatsForLbStats() {
+        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);
+
+        String args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
+        ExecutionResult executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
+        when(vmwareResource.executeInVR(Mockito.eq(getAutoScaleMetricsCommand.getPrivateIP()), Mockito.eq("get_haproxy_stats.sh"), Mockito.eq(args))).thenReturn(executionResult);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {

Review Comment:
   this method is copied from old code, therefore no changes on it.
   I can make changes  if you want



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;
+        String[] lstCounter = new String[lstPair.size()];
+        for (int i = 0; i < lstPair.size(); i++) {
+            Pair<String, Integer> 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));

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;
+        String[] lstCounter = new String[lstPair.size()];
+        for (int i = 0; i < lstPair.size(); i++) {
+            Pair<String, Integer> 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));
+    }
+
+    private void updateCountersMapWithInstantData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+
+        /* initialize if data is not set */
+        countersMap.computeIfAbsent(key, k -> Double.valueOf(0));
+        countersNumberMap.computeIfAbsent(key, k -> 0);
+
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        if (Counter.Source.memory.equals(counter.getSource())) {
+            // calculate memory in percent
+            AutoScaleVmProfileTO profile = groupTO.getProfile();
+            ServiceOfferingVO serviceOff = _serviceOfferingDao.findByUuidIncludingRemoved(profile.getServiceOfferingId());
+            int maxRAM = serviceOff.getRamSize();
+
+            // get current RAM percent
+            coVal = coVal / maxRAM * 100;
+        } else if (Counter.Source.cpu.equals(counter.getSource())) {
+            // cpu
+            coVal = coVal * 100;
+        }
+
+        // update data entry
+        countersMap.put(key, countersMap.get(key) + coVal);
+        countersNumberMap.put(key, countersNumberMap.get(key) + 1);
+    }
+
+    private void updateCountersMapWithAggregatedData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        countersMap.put(key, coVal);
+        countersNumberMap.put(key, 1);
+    }
+
+    private void monitorVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        s_logger.debug("[AutoScale] Collecting performance data ...");
+
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (is_native(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from hosts ...");
+            getVmStatsFromHosts(groupTO);
+        }
+
+        if (has_source_virtual_router(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from virtual router ...");
+            getNetworkStatsFromVirtualRouter(groupTO);
+        }
+    }
+
+    private void checkVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        Map<String, Double> countersMap = new HashMap<>();
+        Map<String, Integer> countersNumberMap = new HashMap<>();
+
+        // update counter maps in memory
+        if (!updateCountersMap(groupTO, countersMap, countersNumberMap)) {
+            s_logger.error("Failed to update counters map, existing");
+            return;
+        }
+
+        // get scale action
+        AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+        if (scaleAction != null) {
+            s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+            if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                doScaleUp(asGroup.getId(), 1);
+            } else {
+                doScaleDown(asGroup.getId());
+            }
+        }
+
+        // Remove old statistics from database
+        cleanupAsVmGroupStatistics(groupTO);
+    }
+
+    private void getVmStatsFromHosts(AutoScaleVmGroupTO groupTO) {
+        // group vms by host id
+        Map<Long, List<Long>> hostAndVmIdsMap = new HashMap<>();
+        final Long defaultHostId = -1L;
+
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.listByGroup(groupTO.getId());
+        for (AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs) {
+            Long vmId = asGroupVmVO.getInstanceId();
+            UserVmVO vm = _userVmDao.findById(vmId);
+            Long vmHostId = vm.getHostId() != null ? vm.getHostId() : defaultHostId;
+            List<Long> vmIds = hostAndVmIdsMap.get(vmHostId);
+            if (vmIds == null) {
+                vmIds = new ArrayList<>();
+            }
+            vmIds.add(vmId);
+            hostAndVmIdsMap.put(vmHostId, vmIds);
+        }
+
+        Map<Long, List<CounterTO>> policyCountersMap = getPolicyCounters(groupTO);
+
+        // get vm stats from each host and update database
+        for (Map.Entry<Long, List<Long>> hostAndVmIds : hostAndVmIdsMap.entrySet()) {
+            Long hostId = hostAndVmIds.getKey();
+            List<Long> vmIds = hostAndVmIds.getValue();
+
+            Date timestamp = new Date();
+
+            Map<Long, VmStatsEntry> vmStatsById = new HashMap<>();
+            if (!defaultHostId.equals(hostId)) {
+                HostVO host = _hostDao.findById(hostId);
+                try {
+                    vmStatsById = _userVmMgr.getVirtualMachineStatistics(host.getId(), host.getName(), vmIds);
+                    if (MapUtils.isEmpty(vmStatsById)) {
+                        s_logger.warn("Got empty result for virtual machine statistics from host: " + host);
+                    }
+                } catch (Exception e) {
+                    s_logger.debug("Failed to get VM stats from host : " + host.getName());
+                }
+            }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;
+        String[] lstCounter = new String[lstPair.size()];
+        for (int i = 0; i < lstPair.size(); i++) {
+            Pair<String, Integer> 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));
+    }
+
+    private void updateCountersMapWithInstantData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+
+        /* initialize if data is not set */
+        countersMap.computeIfAbsent(key, k -> Double.valueOf(0));
+        countersNumberMap.computeIfAbsent(key, k -> 0);
+
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        if (Counter.Source.memory.equals(counter.getSource())) {
+            // calculate memory in percent
+            AutoScaleVmProfileTO profile = groupTO.getProfile();
+            ServiceOfferingVO serviceOff = _serviceOfferingDao.findByUuidIncludingRemoved(profile.getServiceOfferingId());
+            int maxRAM = serviceOff.getRamSize();
+
+            // get current RAM percent
+            coVal = coVal / maxRAM * 100;
+        } else if (Counter.Source.cpu.equals(counter.getSource())) {
+            // cpu
+            coVal = coVal * 100;
+        }
+
+        // update data entry
+        countersMap.put(key, countersMap.get(key) + coVal);
+        countersNumberMap.put(key, countersNumberMap.get(key) + 1);
+    }
+
+    private void updateCountersMapWithAggregatedData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        countersMap.put(key, coVal);
+        countersNumberMap.put(key, 1);
+    }
+
+    private void monitorVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        s_logger.debug("[AutoScale] Collecting performance data ...");
+
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (is_native(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from hosts ...");
+            getVmStatsFromHosts(groupTO);
+        }
+
+        if (has_source_virtual_router(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from virtual router ...");
+            getNetworkStatsFromVirtualRouter(groupTO);
+        }
+    }
+
+    private void checkVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        Map<String, Double> countersMap = new HashMap<>();
+        Map<String, Integer> countersNumberMap = new HashMap<>();
+
+        // update counter maps in memory
+        if (!updateCountersMap(groupTO, countersMap, countersNumberMap)) {
+            s_logger.error("Failed to update counters map, existing");
+            return;
+        }
+
+        // get scale action
+        AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+        if (scaleAction != null) {
+            s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+            if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                doScaleUp(asGroup.getId(), 1);
+            } else {
+                doScaleDown(asGroup.getId());
+            }
+        }
+
+        // Remove old statistics from database
+        cleanupAsVmGroupStatistics(groupTO);
+    }
+
+    private void getVmStatsFromHosts(AutoScaleVmGroupTO groupTO) {
+        // group vms by host id
+        Map<Long, List<Long>> hostAndVmIdsMap = new HashMap<>();
+        final Long defaultHostId = -1L;
+
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.listByGroup(groupTO.getId());
+        for (AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs) {
+            Long vmId = asGroupVmVO.getInstanceId();
+            UserVmVO vm = _userVmDao.findById(vmId);
+            Long vmHostId = vm.getHostId() != null ? vm.getHostId() : defaultHostId;
+            List<Long> vmIds = hostAndVmIdsMap.get(vmHostId);
+            if (vmIds == null) {
+                vmIds = new ArrayList<>();
+            }
+            vmIds.add(vmId);
+            hostAndVmIdsMap.put(vmHostId, vmIds);
+        }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;
+        String[] lstCounter = new String[lstPair.size()];
+        for (int i = 0; i < lstPair.size(); i++) {
+            Pair<String, Integer> 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]);

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }
+                    }
+                }
+
+                AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+                if (scaleAction != null) {
+                    s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+                    if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                        doScaleUp(asGroup.getId(), 1);
+                    } else {
+                        doScaleDown(asGroup.getId());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            s_logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e);
+        }
     }
 
+    private void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) {
+        // setup parameters phase: duration and counter
+        // list pair [counter, duration]
+        List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO);
+        int total_counter = 0;
+        String[] lstCounter = new String[lstPair.size()];
+        for (int i = 0; i < lstPair.size(); i++) {
+            Pair<String, Integer> 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));
+    }
+
+    private void updateCountersMapWithInstantData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+
+        /* initialize if data is not set */
+        countersMap.computeIfAbsent(key, k -> Double.valueOf(0));
+        countersNumberMap.computeIfAbsent(key, k -> 0);
+
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        if (Counter.Source.memory.equals(counter.getSource())) {
+            // calculate memory in percent
+            AutoScaleVmProfileTO profile = groupTO.getProfile();
+            ServiceOfferingVO serviceOff = _serviceOfferingDao.findByUuidIncludingRemoved(profile.getServiceOfferingId());
+            int maxRAM = serviceOff.getRamSize();
+
+            // get current RAM percent
+            coVal = coVal / maxRAM * 100;
+        } else if (Counter.Source.cpu.equals(counter.getSource())) {
+            // cpu
+            coVal = coVal * 100;
+        }
+
+        // update data entry
+        countersMap.put(key, countersMap.get(key) + coVal);
+        countersNumberMap.put(key, countersNumberMap.get(key) + 1);
+    }
+
+    private void updateCountersMapWithAggregatedData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, Long counterId, Long conditionId, Long policyId, Double coVal) {
+        // Summary of all counter by counterId key
+        String key = generateKeyFromPolicyAndConditionAndCounter(policyId, conditionId, counterId);
+        CounterVO counter = _counterDao.findById(counterId);
+        if (counter == null) {
+            return;
+        }
+        countersMap.put(key, coVal);
+        countersNumberMap.put(key, 1);
+    }
+
+    private void monitorVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        s_logger.debug("[AutoScale] Collecting performance data ...");
+
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (is_native(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from hosts ...");
+            getVmStatsFromHosts(groupTO);
+        }
+
+        if (has_source_virtual_router(groupTO)) {
+            s_logger.debug("[AutoScale] Collecting performance data from virtual router ...");
+            getNetworkStatsFromVirtualRouter(groupTO);
+        }
+    }
+
+    private void checkVirtualRouterAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        Map<String, Double> countersMap = new HashMap<>();
+        Map<String, Integer> countersNumberMap = new HashMap<>();
+
+        // update counter maps in memory
+        if (!updateCountersMap(groupTO, countersMap, countersNumberMap)) {
+            s_logger.error("Failed to update counters map, existing");
+            return;
+        }
+
+        // get scale action
+        AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO);
+        if (scaleAction != null) {
+            s_logger.debug("[AutoScale] Doing scale action: " + scaleAction + " for group " + asGroup.getId());
+            if (AutoScalePolicy.Action.ScaleUp.equals(scaleAction)) {
+                doScaleUp(asGroup.getId(), 1);
+            } else {
+                doScaleDown(asGroup.getId());
+            }
+        }
+
+        // Remove old statistics from database
+        cleanupAsVmGroupStatistics(groupTO);
+    }
+
+    private void getVmStatsFromHosts(AutoScaleVmGroupTO groupTO) {

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            //get duration
+            Integer duration = policyTO.getDuration();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counter = conditionTO.getCounter();
+                StringBuilder buff = new StringBuilder();
+                buff.append(counter.getName());
+                buff.append(",");
+                buff.append(conditionTO.getId());
+                // add to result
+                Pair<String, Integer> pair = new Pair<String, Integer>(buff.toString(), duration);
+                result.add(pair);
+            }
+        }
+        return result;
+    }
+
+    private Network getNetwork(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        Network network = _networkDao.findById(loadBalancer.getNetworkId());
+        if (network == null) {
+            throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", loadBalancer.getNetworkId()));
+        }
+        return network;
+    }
+
+    private Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        IPAddressVO ipAddress = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
+        if (ipAddress == null) {
+            throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s ", loadBalancer.getSourceIpAddressId()));
+        }
+        return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart());
+    }
+
+    private Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) {
+        final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
+        if (loadBalancer == null) {
+            throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId));
+        }
+        return _lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer);
+    }
+
+    private void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) {
+        AutoScaleVmGroupTO groupTO = _lbRulesMgr.toAutoScaleVmGroupTO(asGroup);
+
+        if (!is_native(groupTO)) {
+            return;
+        }
+        // check minimum vm of group
+        Integer currentVM = _autoScaleVmGroupVmMapDao.countByGroup(asGroup.getId());
+        if (currentVM < asGroup.getMinMembers()) {
+            doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM);
+            return;
+        }
+
+        //check interval
+        long now = (new Date()).getTime();
+        if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) {
+            return;
+        }
+
+        // update last_interval
+        asGroup.setLastInterval(new Date());
+        _autoScaleVmGroupDao.persist(asGroup);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("[Netscaler AutoScale] Collecting RRDs data...");
+        }
+        Map<String, String> params = new HashMap<>();
+        List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = _autoScaleVmGroupVmMapDao.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 = _vmInstanceDao.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 = _vmInstanceDao.findById(vmId);
+        Long receiveHost = vmVO.getHostId();
+
+        setPerformanceMonitorCommandParams(groupTO, params);
+
+        PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20);
+
+        try {
+            PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) _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<String, Double> countersMap = new HashMap<>();
+                HashMap<String, Integer> countersNumberMap = 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]));
+                            Integer duration = Integer.parseInt(params.get("duration" + counter_vm[1]));
+                            Long policyId = 0L; // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer
+
+                            Double coVal = Double.parseDouble(counterVals[1]);
+
+                            updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, conditionId, policyId, coVal);
+
+                        } catch (Exception e) {
+                            s_logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e);
+                        }

Review Comment:
   done



##########
server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java:
##########
@@ -1521,19 +2002,775 @@ public void doScaleDown(final long groupId) {
                     public void run() {
                         try {
 
-                            _userVmManager.destroyVm(vmId, false);
+                            _userVmMgr.destroyVm(vmId, false);
 
-                        } catch (ResourceUnavailableException e) {
-                            e.printStackTrace();
-                        } catch (ConcurrentOperationException e) {
-                            e.printStackTrace();
+                        } catch (ResourceUnavailableException | ConcurrentOperationException ex) {
+                            s_logger.error("Cannot destroy vm with id: " + vmId + "due to Exception: ", ex);
                         }
                     }
                 }, destroyVmGracePeriod, TimeUnit.SECONDS);
             }
         } else {
             s_logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more.");
         }
+        if (!_autoScaleVmGroupDao.updateState(groupId, newState, oldState)) {
+            s_logger.error(String.format("Can not update vmgroup state from %s back to %s, groupId: %s", newState, oldState, groupId));
+        }
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return AutoScaleManager.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {
+                AutoScaleStatsInterval,
+                AutoScaleStatsCleanupDelay,
+                AutoScaleStatsWorker
+        };
+    }
+
+    @Override
+    public void checkAllAutoScaleVmGroups() {
+        // list all AS VMGroups
+        List<AutoScaleVmGroupVO> asGroups = _autoScaleVmGroupDao.listAll();
+        for (AutoScaleVmGroupVO asGroup : asGroups) {
+            _completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup));
+        }
+        for (int i = 0; i < asGroups.size(); i++) {
+            try {
+                Future<Pair<Long, Boolean>> future = _completionService.take();
+                Pair<Long, Boolean> result = future.get();
+                s_logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second());
+            } catch (ExecutionException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+            } catch (InterruptedException ex) {
+                s_logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex);
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> {
+        AutoScaleVmGroupVO asGroup;
+
+        public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) {
+            this.asGroup = asGroup;
+        }
+
+        @Override
+        public Pair<Long, Boolean> call() {
+            try {
+                s_logger.debug("Checking AutoScale vm group " + asGroup);
+                checkAutoScaleVmGroup(asGroup);
+            } catch (Exception ex) {
+                s_logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex);
+                return new Pair<>(asGroup.getId(), false);
+            }
+            return new Pair<>(asGroup.getId(), true);
+        }
+    }
+
+    @Override
+    public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) {
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                checkNetScalerAsGroup(asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                checkVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+
+    private void monitorAutoScaleVmGroup(Long groupId) {
+        AutoScaleVmGroupVO asGroup = _autoScaleVmGroupDao.findById(groupId);
+        if (asGroup == null) {
+            s_logger.error("Can not find the groupid " + groupId + " for monitoring");
+            return;
+        }
+        s_logger.debug("Start monitoring on AutoScale VmGroup " + asGroup);
+        // check group state
+        if (asGroup.getState().equals(AutoScaleVmGroup.State.Enabled)) {
+            Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId());
+            if (Network.Provider.Netscaler.equals(provider)) {
+                s_logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup);
+            } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider)) {
+                monitorVirtualRouterAsGroup(asGroup);
+            }
+        }
+    }
+    private boolean is_native(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.NativeSources.contains(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean has_source_virtual_router(AutoScaleVmGroupTO groupTO) {
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                if (Counter.Source.virtualrouter.equals(counterTO.getSource())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) {
+        Map<Long, List<CounterTO>> counters = new HashMap<>();
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            List<CounterTO> counterTOs = new ArrayList<>();
+            for (ConditionTO conditionTO : policyTO.getConditions()) {
+                CounterTO counterTO = conditionTO.getCounter();
+                counterTOs.add(counterTO);
+            }
+            counters.put(policyTO.getId(), counterTOs);
+        }
+        return counters;
+    }
+
+    private AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) {
+        s_logger.debug("[AutoScale] Getting autoscale action for group : " + groupTO.getId());
+
+        Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId());
+
+        for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) {
+            int quiettime = policyTO.getQuietTime();
+            Date quiettimeDate = policyTO.getLastQuietTime();
+            long last_quiettime = 0L;
+            if (quiettimeDate != null) {
+                last_quiettime = policyTO.getLastQuietTime().getTime();
+            }
+            long current_time = (new Date()).getTime();
+
+            // check quiet time for this policy
+            if ((current_time - last_quiettime) >= quiettime) {
+                // check whole conditions of this policy
+                boolean bValid = true;
+                for (ConditionTO conditionTO : policyTO.getConditions()) {
+                    CounterTO counter = conditionTO.getCounter();
+                    long thresholdValue = conditionTO.getThreshold();
+                    Double thresholdPercent = (double)thresholdValue;
+
+                    String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId());
+                    if (Network.Provider.Netscaler.equals(provider)) {
+                        key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId());
+                    }
+                    Double sum = countersMap.get(key);
+                    Integer number = countersNumberMap.get(key);
+                    s_logger.debug(String.format("policyId = %d, conditionId = %d, counter = %s, sum = %f, number = %s", policyTO.getId(), conditionTO.getId(), counter.getSource(), sum, number));
+                    if (number == null || number == 0) {
+                        bValid = false;
+                        break;
+                    }
+                    Double avg = sum / number;
+                    Condition.Operator op = conditionTO.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 policyTO.getAction();
+                }
+            }
+        }
+        return null;
+    }
+
+    public List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) {
+        List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>();

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org