Merge branch '4.18'
diff --git a/.asf.yaml b/.asf.yaml
index a0ba915..e16eb20 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -27,11 +27,16 @@
     - infrastructure
     - java
     - python
-    - hosting
     - kvm
+    - libvirt
     - vsphere
+    - vmware
     - xenserver
     - xcp-ng
+    - orchestration
+    - virtualization
+    - virtual-machine
+    - kubernetes
 
   features:
     wiki: true
@@ -44,6 +49,11 @@
     rebase: false
 
   collaborators:
+    - kiranchavala
+    - rajujith
+    - alexandremattioli
+    - vishesh92
+    - soreana
     - acs-robot
 
   protected_branches: ~
diff --git a/.github/linters/.flake8 b/.github/linters/.flake8
index 6a2235d..1c239ac 100644
--- a/.github/linters/.flake8
+++ b/.github/linters/.flake8
@@ -20,6 +20,8 @@
 # E242 Tab after ','
 # E273 Tab after keyword
 # E274 Tab before keyword
+# E742 Do not define classes named 'I', 'O', or 'l'
+# E743 Do not define functions named 'I', 'O', or 'l'
 # E901 SyntaxError or IndentationError
 # E902 IOError
 # W291 Trailing whitespace
@@ -28,4 +30,4 @@
 # W391 Blank line at end of file
 
 [flake8]
-select = E223,E224,E242,E273,E274,E901,E902,W291,W292,W293,W391
+select = E223,E224,E242,E273,E274,E742,E743,E901,E902,W291,W292,W293,W391
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 94ebf10..d0061cb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -80,7 +80,8 @@
                   smoke/test_metrics_api
                   smoke/test_migration
                   smoke/test_multipleips_per_nic
-                  smoke/test_nested_virtualization",
+                  smoke/test_nested_virtualization
+                  smoke/test_set_sourcenat",
                 "smoke/test_network
                   smoke/test_network_acl
                   smoke/test_network_ipv6
@@ -124,6 +125,7 @@
                   smoke/test_usage
                   smoke/test_usage_events
                   smoke/test_vm_deployment_planner
+                  smoke/test_vm_schedule
                   smoke/test_vm_life_cycle
                   smoke/test_vm_lifecycle_unmanage_import
                   smoke/test_vm_snapshot_kvm
diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml
index 778acf0..1ef7b65 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/codecov.yml
@@ -33,6 +33,8 @@
     runs-on: ubuntu-22.04
     steps:
       - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
 
       - name: Set up JDK11
         uses: actions/setup-java@v3
diff --git a/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml
index d036577..f82819a 100644
--- a/.github/workflows/main-sonar-check.yml
+++ b/.github/workflows/main-sonar-check.yml
@@ -22,6 +22,10 @@
     branches:
       - main
 
+permissions:
+  contents: read # to fetch code (actions/checkout)
+  pull-requests: write # for sonar to comment on pull-request
+
 jobs:
   build:
     if: github.repository == 'apache/cloudstack'
diff --git a/.github/workflows/sonar-check.yml b/.github/workflows/sonar-check.yml
index 196cbc9..53c7802 100644
--- a/.github/workflows/sonar-check.yml
+++ b/.github/workflows/sonar-check.yml
@@ -20,7 +20,8 @@
 on: [pull_request]
 
 permissions:
-  contents: read
+  contents: read # to fetch code (actions/checkout)
+  pull-requests: write # for sonar to comment on pull-request
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ec3bb87..cdfbfe7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,7 +42,7 @@
 
 On your computer, follow these steps to setup a local repository for working on ACS:
 
-``` bash
+```bash
 $ git clone https://github.com/YOUR_ACCOUNT/cloudstack.git
 $ cd cloudstack
 $ git remote add upstream https://github.com/apache/cloudstack.git
@@ -60,7 +60,7 @@
 
 It is best practice to create a new branch each time you want to contribute to the project and only track the changes for that pull request in this branch.
 
-``` bash
+```bash
 $ git checkout -b feature_x
    (make your changes)
 $ git status
@@ -82,7 +82,7 @@
 2. Synchronize your local `main` branch with the `upstream/main` so you have all the latest changes from the project
 3. Rebase the latest project code into your `feature_x` branch so it is up-to-date with the upstream code
 
-``` bash
+```bash
 $ git checkout main
 $ git fetch upstream
 $ git rebase upstream/main
@@ -102,7 +102,7 @@
 
 > **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` _before_ you do this.
 
-``` bash
+```bash
 $ git push origin main
 $ git push origin feature_x
 ```
@@ -128,7 +128,7 @@
 
 You can delete these deprecated branches with the following:
 
-``` bash
+```bash
 $ git checkout main
 $ git branch -D feature_x
 $ git push origin :feature_x
diff --git a/README.md b/README.md
index e56857e..f5a80b8 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@
 reside may have restrictions on the import, possession, use, and/or re-export to another
 country, of encryption software. BEFORE using any encryption software, please check your
 country's laws, regulations and policies concerning the import, possession, or use, and
-re-export of encryption software, to see if this is permitted. See http://www.wassenaar.org/
+re-export of encryption software, to see if this is permitted. See [The Wassenaar Arrangement](http://www.wassenaar.org/) 
 for more information.
 
 The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has
diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 27c0e387..9174da7 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -398,3 +398,7 @@
 
 # The number of iothreads. There should be only 1 or 2 IOThreads per VM CPU (default is 1). The recommended number of iothreads is 1
 # iothreads=1
+
+# The path of an executable file/script for host health check for CloudStack to Auto Disable/Enable the host
+# depending on the return value of the file/script
+# agent.health.check.script.path=
diff --git a/agent/pom.xml b/agent/pom.xml
index c93b4e2..5fe88c8 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/agent/src/main/java/com/cloud/agent/dhcp/DhcpProtocolParserServer.java b/agent/src/main/java/com/cloud/agent/dhcp/DhcpProtocolParserServer.java
index 5d75acf..0ee9fd6 100644
--- a/agent/src/main/java/com/cloud/agent/dhcp/DhcpProtocolParserServer.java
+++ b/agent/src/main/java/com/cloud/agent/dhcp/DhcpProtocolParserServer.java
@@ -52,7 +52,6 @@
                     byte[] buf = new byte[bufferSize];
                     DatagramPacket dgp = new DatagramPacket(buf, buf.length);
                     dhcpSocket.receive(dgp);
-                    // _executor.execute(new DhcpPacketParser(buf));
                 }
             } catch (IOException e) {
                 s_logger.debug(e.getMessage());
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
index 5c7f4ed..75248fb 100644
--- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -312,6 +312,9 @@
      */
     public static final Property<String> OPENVSWITCH_DPDK_OVS_PATH = new Property<>("openvswitch.dpdk.ovs.path", null, String.class);
 
+    public static final Property<String> HEALTH_CHECK_SCRIPT_PATH =
+            new Property<>("agent.health.check.script.path", null, String.class);
+
     /**
      * Sets the hypervisor type.<br>
      * Possible values: kvm | lxc <br>
diff --git a/agent/src/test/java/com/cloud/agent/AgentShellTest.java b/agent/src/test/java/com/cloud/agent/AgentShellTest.java
index 27bc3dc..f715177 100644
--- a/agent/src/test/java/com/cloud/agent/AgentShellTest.java
+++ b/agent/src/test/java/com/cloud/agent/AgentShellTest.java
@@ -26,20 +26,21 @@
 
 import com.cloud.agent.properties.AgentProperties;
 import com.cloud.agent.properties.AgentPropertiesFileHandler;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 import com.cloud.utils.StringUtils;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.Spy;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
-@RunWith(PowerMockRunner.class)
+@RunWith(MockitoJUnitRunner.class)
 public class AgentShellTest {
 
     @InjectMocks
@@ -58,6 +59,18 @@
     @Mock
     UUID uuidMock;
 
+    MockedStatic<AgentPropertiesFileHandler> agentPropertiesFileHandlerMocked;
+
+    @Before
+    public void setUp() throws Exception {
+         agentPropertiesFileHandlerMocked = Mockito.mockStatic(AgentPropertiesFileHandler.class, Mockito.CALLS_REAL_METHODS);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        agentPropertiesFileHandlerMocked.close();
+    }
+
     @Test
     public void parseCommand() throws ConfigurationException {
         AgentShell shell = new AgentShell();
@@ -106,44 +119,35 @@
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getPortOrWorkersTestValueIsNullGetFromProperty() {
         int expected = 195;
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
 
         int result = agentShellSpy.getPortOrWorkers(null, propertyIntegerMock);
         Assert.assertEquals(expected, result);
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getPortOrWorkersTestValueIsNotAValidIntegerReturnDefaultFromProperty() {
         int expected = 42;
-
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
         Mockito.doReturn(expected).when(propertyIntegerMock).getDefaultValue();
 
         int result = agentShellSpy.getPortOrWorkers("test", propertyIntegerMock);
         Assert.assertEquals(expected, result);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getPortOrWorkersTestValueIsAValidIntegerReturnValue() {
         int expected = 42;
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
         Mockito.doReturn(79).when(propertyIntegerMock).getDefaultValue();
 
         int result = agentShellSpy.getPortOrWorkers(String.valueOf(expected), propertyIntegerMock);
         Assert.assertEquals(expected, result);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
     }
 
     @Test
@@ -183,41 +187,34 @@
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getZoneOrPodTestValueIsNullAndPropertyStartsAndEndsWithAtSignReturnPropertyDefaultValue() {
         String expected = "default";
-
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn("test");
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn("test");
 
         Mockito.doReturn(true).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
         Mockito.doReturn(expected).when(propertyStringMock).getDefaultValue();
 
         String result = agentShellSpy.getZoneOrPod(null, propertyStringMock);
         Assert.assertEquals(expected, result);
+
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getZoneOrPodTestValueIsNullAndPropertyDoesNotStartAndEndWithAtSignReturnPropertyDefaultValue() {
         String expected = "test";
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
 
         Mockito.doReturn(false).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        Mockito.doReturn("default").when(propertyStringMock).getDefaultValue();
 
         String result = agentShellSpy.getZoneOrPod(null, propertyStringMock);
         Assert.assertEquals(expected, result);
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getZoneOrPodTestValueIsNotNullAndStartsAndEndsWithAtSignReturnPropertyDefaultValue() {
         String expected = "default";
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
 
         Mockito.doReturn(true).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
         Mockito.doReturn(expected).when(propertyStringMock).getDefaultValue();
@@ -225,25 +222,20 @@
         String result = agentShellSpy.getZoneOrPod("test", propertyStringMock);
         Assert.assertEquals(expected, result);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getZoneOrPodTestValueIsNotNullAndDoesNotStartAndEndWithAtSignReturnPropertyDefaultValue() {
         String expected = "test";
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
 
         Mockito.doReturn(false).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        Mockito.doReturn("default").when(propertyStringMock).getDefaultValue();
 
         String result = agentShellSpy.getZoneOrPod(expected, propertyStringMock);
         Assert.assertEquals(expected, result);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
     }
 
     @Test
@@ -255,12 +247,10 @@
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getGuidTestGuidIsNullReturnProperty() throws ConfigurationException {
         String expected = "test";
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
 
         String result = agentShellSpy.getGuid(null);
 
@@ -268,34 +258,34 @@
     }
 
     @Test
-    @PrepareForTest({AgentShell.class, AgentPropertiesFileHandler.class})
     public void getGuidTestGuidAndPropertyAreNullIsDeveloperGenerateNewUuid() throws ConfigurationException {
         String expected = "test";
 
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class, UUID.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(null, true);
-        PowerMockito.when(UUID.randomUUID()).thenReturn(uuidMock);
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(null, true);
+        MockedStatic<UUID> uuidMocked = Mockito.mockStatic(UUID.class);
+        uuidMocked.when(() -> UUID.randomUUID()).thenReturn(uuidMock);
         Mockito.doReturn(expected).when(uuidMock).toString();
 
         String result = agentShellSpy.getGuid(null);
 
         Assert.assertEquals(expected, result);
+        uuidMocked.close();
     }
 
     @Test(expected = ConfigurationException.class)
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void getGuidTestGuidAndPropertyAreNullIsNotDeveloperThrowConfigurationException() throws ConfigurationException {
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(null, false);
+
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(null, false);
 
         agentShellSpy.getGuid(null);
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void setHostTestValueIsNotNullAndStartsAndEndsWithAtSignThrowConfigurationException(){
         Mockito.doReturn(true).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
+
+
+
 
         boolean error = false;
 
@@ -309,16 +299,14 @@
             throw new AssertionError("This test expects a ConfigurationException.");
         }
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void setHostTestValueIsNullPropertyStartsAndEndsWithAtSignThrowConfigurationException(){
         Mockito.doReturn(true).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn("test");
+
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn("test");
 
         boolean error = false;
 
@@ -332,37 +320,32 @@
             throw new AssertionError("This test expects a ConfigurationException.");
         }
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class);
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()));
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void setHostTestValueIsNotNullAndDoesNotStartAndEndWithAtSignSetHosts() throws ConfigurationException {
         String expected = "test";
         Mockito.doReturn(false).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
 
         agentShellSpy.setHost(expected);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class, Mockito.never());
-        AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()), Mockito.never());
 
         Mockito.verify(agentShellSpy).setHosts(expected);
     }
 
     @Test
-    @PrepareForTest(AgentPropertiesFileHandler.class)
     public void setHostTestValueIsNullPropertyDoesNotStartAndEndWithAtSignSetHosts() throws ConfigurationException {
         String expected = "test";
 
         Mockito.doReturn(false).when(agentShellSpy).isValueStartingAndEndingWithAtSign(Mockito.any());
-        PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
-        PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
+
+        agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any())).thenReturn(expected);
 
         agentShellSpy.setHost(null);
 
-        PowerMockito.verifyStatic(AgentPropertiesFileHandler.class);
+        agentPropertiesFileHandlerMocked.verify(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.any()));
         AgentPropertiesFileHandler.getPropertyValue(Mockito.any());
 
         Mockito.verify(agentShellSpy).setHosts(expected);
diff --git a/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java b/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java
index 1a73bf2..749f0f1 100644
--- a/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java
+++ b/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java
@@ -24,18 +24,17 @@
 import java.io.IOException;
 import java.util.Properties;
 import junit.framework.TestCase;
-import org.apache.commons.beanutils.ConvertUtils;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({PropertiesUtil.class, ConvertUtils.class})
+@RunWith(MockitoJUnitRunner.class)
 public class AgentPropertiesFileHandlerTest extends TestCase {
 
     @Mock
@@ -53,14 +52,27 @@
     @Mock
     Properties propertiesMock;
 
+    MockedStatic<PropertiesUtil> propertiesUtilMocked;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        propertiesUtilMocked = Mockito.mockStatic(PropertiesUtil.class);
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        propertiesUtilMocked.close();
+    }
+
     @Test
     public void getPropertyValueTestFileNotFoundReturnDefaultValueNull() throws Exception{
         String expectedResult = null;
 
         AgentProperties.Property<String> agentPropertiesStringMock = new AgentProperties.Property<String>("Test-null", null, String.class);
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(null).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(null);
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
 
@@ -72,8 +84,7 @@
         String expectedResult = "default value";
         Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(null).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(null);
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
 
@@ -85,9 +96,8 @@
         String expectedResult = "default value";
         Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doThrow(new IOException()).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenThrow(new IOException());
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
 
@@ -100,10 +110,9 @@
         Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
         Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
-        PowerMockito.doReturn("").when(propertiesMock).getProperty(Mockito.anyString());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
+        propertiesUtilMocked.when(() -> propertiesMock.getProperty(Mockito.anyString())).thenReturn("");
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
 
@@ -116,10 +125,9 @@
         Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
         Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
-        PowerMockito.doReturn(null).when(propertiesMock).getProperty(Mockito.anyString());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
+        propertiesUtilMocked.when(() -> propertiesMock.getProperty(Mockito.anyString())).thenReturn(null);
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
 
@@ -132,9 +140,8 @@
         Mockito.doReturn("default value").when(agentPropertiesStringMock).getDefaultValue();
         Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
         Mockito.doReturn(expectedResult).when(propertiesMock).getProperty(Mockito.anyString());
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
@@ -148,9 +155,8 @@
 
         AgentProperties.Property<String> agentPropertiesStringMock = new AgentProperties.Property<String>("Test-null", null, String.class);
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
         Mockito.doReturn(expectedResult).when(propertiesMock).getProperty(Mockito.anyString());
 
         String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
@@ -165,9 +171,8 @@
         Mockito.doReturn("name").when(agentPropertiesIntegerMock).getName();
         Mockito.doReturn(Integer.class).when(agentPropertiesIntegerMock).getTypeClass();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
         Mockito.doReturn(String.valueOf(expectedResult)).when(propertiesMock).getProperty(Mockito.anyString());
 
         Integer result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesIntegerMock);
@@ -182,9 +187,8 @@
         Mockito.doReturn("name").when(agentPropertiesLongMock).getName();
         Mockito.doReturn(Long.class).when(agentPropertiesLongMock).getTypeClass();
 
-        PowerMockito.mockStatic(PropertiesUtil.class);
-        PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
-        PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+        propertiesUtilMocked.when(() -> PropertiesUtil.findConfigFile(Mockito.anyString())).thenReturn(fileMock);
+        propertiesUtilMocked.when(() -> PropertiesUtil.loadFromFile(Mockito.any())).thenReturn(propertiesMock);
         Mockito.doReturn(String.valueOf(expectedResult)).when(propertiesMock).getProperty(Mockito.anyString());
 
         Long result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesLongMock);
diff --git a/agent/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/agent/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d4
--- /dev/null
+++ b/agent/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/api/pom.xml b/api/pom.xml
index 1428bee..ff9083a 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index f7678d9..484dc9b 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -84,6 +84,7 @@
 import com.cloud.vm.Nic;
 import com.cloud.vm.NicSecondaryIp;
 import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
 
 public class EventTypes {
 
@@ -111,6 +112,17 @@
     public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE";
     public static final String EVENT_VM_RECOVER = "VM.RECOVER";
 
+    // VM Schedule
+    public static final String EVENT_VM_SCHEDULE_CREATE = "VM.SCHEDULE.CREATE";
+    public static final String EVENT_VM_SCHEDULE_UPDATE = "VM.SCHEDULE.UPDATE";
+    public static final String EVENT_VM_SCHEDULE_DELETE = "VM.SCHEDULE.DELETE";
+
+    public static final String EVENT_VM_SCHEDULE_START = "VM.SCHEDULE.START";
+    public static final String EVENT_VM_SCHEDULE_STOP = "VM.SCHEDULE.STOP";
+    public static final String EVENT_VM_SCHEDULE_REBOOT = "VM.SCHEDULE.REBOOT";
+    public static final String EVENT_VM_SCHEDULE_FORCE_STOP = "VM.SCHEDULE.FORCE.STOP";
+    public static final String EVENT_VM_SCHEDULE_FORCE_REBOOT = "VM.SCHEDULE.FORCE.REBOOT";
+
     // Domain Router
     public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
     public static final String EVENT_ROUTER_DESTROY = "ROUTER.DESTROY";
@@ -655,6 +667,7 @@
     public static final String EVENT_GUEST_OS_MAPPING_ADD = "GUEST.OS.MAPPING.ADD";
     public static final String EVENT_GUEST_OS_MAPPING_REMOVE = "GUEST.OS.MAPPING.REMOVE";
     public static final String EVENT_GUEST_OS_MAPPING_UPDATE = "GUEST.OS.MAPPING.UPDATE";
+    public static final String EVENT_GUEST_OS_HYPERVISOR_NAME_FETCH = "GUEST.OS.HYPERVISOR.NAME.FETCH";
 
     public static final String EVENT_NIC_SECONDARY_IP_ASSIGN = "NIC.SECONDARY.IP.ASSIGN";
     public static final String EVENT_NIC_SECONDARY_IP_UNASSIGN = "NIC.SECONDARY.IP.UNASSIGN";
@@ -716,6 +729,16 @@
         entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
         entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class);
 
+        // VMSchedule
+        entityEventDetails.put(EVENT_VM_SCHEDULE_CREATE, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_DELETE, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_UPDATE, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_START, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, VMSchedule.class);
+        entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, VMSchedule.class);
+
         entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
         entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);
         entityEventDetails.put(EVENT_ROUTER_START, VirtualRouter.class);
@@ -1092,6 +1115,7 @@
         entityEventDetails.put(EVENT_GUEST_OS_MAPPING_ADD, GuestOSHypervisor.class);
         entityEventDetails.put(EVENT_GUEST_OS_MAPPING_REMOVE, GuestOSHypervisor.class);
         entityEventDetails.put(EVENT_GUEST_OS_MAPPING_UPDATE, GuestOSHypervisor.class);
+        entityEventDetails.put(EVENT_GUEST_OS_HYPERVISOR_NAME_FETCH, GuestOSHypervisor.class);
         entityEventDetails.put(EVENT_NIC_SECONDARY_IP_ASSIGN, NicSecondaryIp.class);
         entityEventDetails.put(EVENT_NIC_SECONDARY_IP_UNASSIGN, NicSecondaryIp.class);
         entityEventDetails.put(EVENT_NIC_SECONDARY_IP_CONFIGURE, NicSecondaryIp.class);
diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java
index a2633ec7..458169c 100644
--- a/api/src/main/java/com/cloud/network/Network.java
+++ b/api/src/main/java/com/cloud/network/Network.java
@@ -256,7 +256,7 @@
 
     public static class Capability {
 
-        private static List<Capability> supportedCapabilities = new ArrayList<Capability>();
+        private static List<Capability> supportedCapabilities = new ArrayList<>();
 
         public static final Capability SupportedProtocols = new Capability("SupportedProtocols");
         public static final Capability SupportedLBAlgorithms = new Capability("SupportedLbAlgorithms");
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java b/api/src/main/java/com/cloud/network/vpc/VpcService.java
index 362a110..2cdc034 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java
@@ -23,7 +23,9 @@
 import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd;
 import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
 import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd;
+import org.apache.cloudstack.api.command.user.vpc.ListVPCsCmd;
 import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd;
+import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
 
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -37,7 +39,6 @@
 
 public interface VpcService {
 
-    public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException;
     /**
      * Persists VPC record in the database
      *
@@ -48,15 +49,26 @@
      * @param displayText
      * @param cidr
      * @param networkDomain TODO
+     * @param ip4Dns1
+     * @param ip4Dns2
      * @param displayVpc TODO
      * @return
      * @throws ResourceAllocationException TODO
      */
-    public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
-                         String dns1, String dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu)
+    Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
+                  String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu)
             throws ResourceAllocationException;
 
     /**
+     * Persists VPC record in the database
+     *
+     * @param cmd the command with specification data for the new vpc
+     * @return a data object describing the new vpc
+     * @throws ResourceAllocationException the resources for this VPC cannot be allocated
+     */
+    Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException;
+
+    /**
      * Deletes a VPC
      *
      * @param vpcId
@@ -65,48 +77,48 @@
      * @throws ResourceUnavailableException
      * @throws ConcurrentOperationException
      */
-    public boolean deleteVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
+    boolean deleteVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
+
+    /**
+     * Persists VPC record in the database
+     *
+     * @param cmd the command with specification data for updating the vpc
+     * @return a data object describing the new vpc state
+     * @throws ResourceUnavailableException if during restart some resources may not be available
+     * @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available
+     */
+    Vpc updateVpc(UpdateVPCCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException;
 
     /**
      * Updates VPC with new name/displayText
      *
-     * @param vpcId
-     * @param vpcName
-     * @param displayText
-     * @param customId    TODO
-     * @param displayVpc  TODO
-     * @param mtu
-     * @return
+     * @param vpcId the ID of the Vpc to update
+     * @param vpcName The new name to give the vpc
+     * @param displayText the new display text to use for describing the VPC
+     * @param customId A new custom (external) ID to associate this VPC with
+     * @param displayVpc should this VPC be displayed on public lists
+     * @param mtu what maximal transfer unit to us in this VPCs networks
+     * @param sourceNatIp the source NAT address to use for this VPC (must already be associated with the VPC)
+     * @return an object describing the current state of the VPC
+     * @throws ResourceUnavailableException if during restart some resources may not be available
+     * @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available
      */
-    public Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu);
+    Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException;
+
+    /**
+     * Lists VPC(s) based on the parameters passed to the API call
+     *
+     * @param cmd object containing the search specs
+     * @return the List of VPCs
+     */
+    Pair<List<? extends Vpc>, Integer> listVpcs(ListVPCsCmd cmd);
 
     /**
      * Lists VPC(s) based on the parameters passed to the method call
-     *
-     * @param id
-     * @param vpcName
-     * @param displayText
-     * @param supportedServicesStr
-     * @param cidr
-     * @param state TODO
-     * @param accountName
-     * @param domainId
-     * @param keyword
-     * @param startIndex
-     * @param pageSizeVal
-     * @param zoneId TODO
-     * @param isRecursive TODO
-     * @param listAll TODO
-     * @param restartRequired TODO
-     * @param tags TODO
-     * @param projectId TODO
-     * @param display TODO
-     * @param vpc
-     * @return
      */
-    public Pair<List<? extends Vpc>, Integer> listVpcs(Long id, String vpcName, String displayText, List<String> supportedServicesStr, String cidr, Long vpcOffId, String state,
-            String accountName, Long domainId, String keyword, Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired,
-            Map<String, String> tags, Long projectId, Boolean display);
+    Pair<List<? extends Vpc>, Integer> listVpcs(Long id, String vpcName, String displayText, List<String> supportedServicesStr, String cidr, Long vpcOffId, String state,
+                                                String accountName, Long domainId, String keyword, Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired,
+                                                Map<String, String> tags, Long projectId, Boolean display);
 
     /**
      * Starts VPC which includes starting VPC provider and applying all the networking rules on the backend
@@ -130,17 +142,17 @@
      */
     boolean shutdownVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
 
+    boolean restartVpc(RestartVPCCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
+
     /**
      * Restarts the VPC. VPC gets shutdown and started as a part of it
      *
-     * @param id
-     * @param cleanUp
-     * @param makeredundant
-     * @return
-     * @throws InsufficientCapacityException
+     * @param networkId the network to restart
+     * @param cleanup throw away the existing VR and rebuild a new one?
+     * @param makeRedundant create two VRs for this network
+     * @return success or not
+     * @throws InsufficientCapacityException when there is no suitable deployment plan possible
      */
-    boolean restartVpc(RestartVPCCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
-
     boolean restartVpc(Long networkId, boolean cleanup, boolean makeRedundant, boolean livePatch, User user) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
 
     /**
@@ -154,23 +166,12 @@
     /**
      * Persists VPC private gateway in the Database.
      *
-     *
-     * @param vpcId TODO
-     * @param physicalNetworkId
-     * @param vlan
-     * @param ipAddress
-     * @param gateway
-     * @param netmask
-     * @param gatewayOwnerId
-     * @param networkOfferingId
-     * @param isSourceNat
-     * @param aclId
-     * @return
+     * @return data object describing the private gateway
      * @throws InsufficientCapacityException
      * @throws ConcurrentOperationException
      * @throws ResourceAllocationException
      */
-    public PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException;
+    PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException;
 
     /**
      * Applies VPC private gateway on the backend, so it becomes functional
@@ -181,12 +182,12 @@
      * @throws ResourceUnavailableException
      * @throws ConcurrentOperationException
      */
-    public PrivateGateway applyVpcPrivateGateway(long gatewayId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException;
+    PrivateGateway applyVpcPrivateGateway(long gatewayId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException;
 
     /**
      * Deletes VPC private gateway
      *
-     * @param id
+     * @param gatewayId
      * @return
      * @throws ResourceUnavailableException
      * @throws ConcurrentOperationException
@@ -199,7 +200,7 @@
      * @param listPrivateGatewaysCmd
      * @return
      */
-    public Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd);
+    Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd);
 
     /**
      * Returns Static Route found by Id
@@ -216,7 +217,7 @@
      * @return
      * @throws ResourceUnavailableException
      */
-    public boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException;
+    boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException;
 
     /**
      * Deletes static route from the backend and the database
@@ -225,7 +226,7 @@
      * @return TODO
      * @throws ResourceUnavailableException
      */
-    public boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException;
+    boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException;
 
     /**
      * Persists static route entry in the Database
@@ -234,15 +235,15 @@
      * @param cidr
      * @return
      */
-    public StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
+    StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
 
     /**
      * Lists static routes based on parameters passed to the call
      *
-     * @param listStaticRoutesCmd
+     * @param cmd Command object with parameters for { @see ListStaticRoutesCmd }
      * @return
      */
-    public Pair<List<? extends StaticRoute>, Integer> listStaticRoutes(ListStaticRoutesCmd cmd);
+    Pair<List<? extends StaticRoute>, Integer> listStaticRoutes(ListStaticRoutesCmd cmd);
 
     /**
      * Associates IP address from the Public network, to the VPC
@@ -262,6 +263,5 @@
      * @param routeId
      * @return
      */
-    public boolean applyStaticRoute(long routeId) throws ResourceUnavailableException;
-
+    boolean applyStaticRoute(long routeId) throws ResourceUnavailableException;
 }
diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java
index f93b3c5..5080cb5 100644
--- a/api/src/main/java/com/cloud/projects/ProjectService.java
+++ b/api/src/main/java/com/cloud/projects/ProjectService.java
@@ -78,9 +78,9 @@
 
     Project findByNameAndDomainId(String name, long domainId);
 
-    Project updateProject(long id, String displayText, String newOwnerName) throws ResourceAllocationException;
+    Project updateProject(long id, String name, String displayText, String newOwnerName) throws ResourceAllocationException;
 
-    Project updateProject(long id, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException;
+    Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException;
 
     boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType);
 
diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java b/api/src/main/java/com/cloud/resource/ResourceService.java
index e2b84ba..2757c91 100644
--- a/api/src/main/java/com/cloud/resource/ResourceService.java
+++ b/api/src/main/java/com/cloud/resource/ResourceService.java
@@ -49,6 +49,8 @@
      */
     Host updateHost(UpdateHostCmd cmd) throws NoTransitionException;
 
+    Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException;
+
     Host cancelMaintenance(CancelMaintenanceCmd cmd);
 
     Host reconnectHost(ReconnectHostCmd cmd) throws AgentUnavailableException;
diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java
index df44ec6..14d46e0 100644
--- a/api/src/main/java/com/cloud/server/ManagementService.java
+++ b/api/src/main/java/com/cloud/server/ManagementService.java
@@ -27,6 +27,7 @@
 import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd;
 import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
 import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd;
+import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd;
 import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd;
 import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd;
 import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd;
@@ -189,6 +190,12 @@
     GuestOSHypervisor getAddedGuestOsMapping(Long guestOsHypervisorId);
 
     /**
+     * Get hypervisor guest OS names
+     *
+     * @return the hypervisor guest OS name that can be used for mapping, with guest OS name, and the name of guest OS specific to hypervisor
+     */
+    List<Pair<String, String>> getHypervisorGuestOsNames(GetHypervisorGuestOsNamesCmd getHypervisorGuestOsNamesCmd);
+    /**
      * Adds a new guest OS
      *
      * @return A VO containing the new guest OS, with its category ID, name and display name
diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java
index 57db35f..4a14197 100644
--- a/api/src/main/java/com/cloud/storage/Volume.java
+++ b/api/src/main/java/com/cloud/storage/Volume.java
@@ -57,7 +57,8 @@
         UploadInProgress("Volume upload is in progress"),
         UploadError("Volume upload encountered some error"),
         UploadAbandoned("Volume upload is abandoned since the upload was never initiated within a specified time"),
-        Attaching("The volume is attaching to a VM from Ready state.");
+        Attaching("The volume is attaching to a VM from Ready state."),
+        Restoring("The volume is being restored from backup.");
 
         String _description;
 
@@ -133,6 +134,11 @@
             s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationSucceeded, Ready, null));
             s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationFailed, Ready, null));
             s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroy, Event.RecoverRequested, Ready, null));
+            s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.RestoreRequested, Restoring, null));
+            s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Expunged, Event.RestoreRequested, Restoring, null));
+            s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroy, Event.RestoreRequested, Restoring, null));
+            s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Restoring, Event.RestoreSucceeded, Ready, null));
+            s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Restoring, Event.RestoreFailed, Ready, null));
         }
     }
 
@@ -156,7 +162,10 @@
         ExpungingRequested,
         ResizeRequested,
         AttachRequested,
-        OperationTimeout;
+        OperationTimeout,
+        RestoreRequested,
+        RestoreSucceeded,
+        RestoreFailed;
     }
 
     /**
diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index b85195d..8b7565d 100644
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@ -21,6 +21,7 @@
 import java.net.MalformedURLException;
 import java.util.Map;
 
+import com.cloud.utils.fsm.NoTransitionException;
 import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
@@ -172,4 +173,6 @@
     Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException;
 
     void publishVolumeCreationUsageEvent(Volume volume);
+
+    boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException;
 }
diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java
index 258e870..3ec365f 100644
--- a/api/src/main/java/com/cloud/vm/UserVmService.java
+++ b/api/src/main/java/com/cloud/vm/UserVmService.java
@@ -404,10 +404,7 @@
     /**
      * Creates a vm group.
      *
-     * @param name
-     *            - name of the group
-     * @param accountId
-     *            - accountId
+     * @param cmd The command specifying domain ID, account name, group name, and project ID
      */
     InstanceGroup createVmGroup(CreateVMGroupCmd cmd);
 
@@ -438,16 +435,10 @@
 
     /**
      * Migrate the given VM to the destination host provided. The API returns the migrated VM if migration succeeds.
-     * Only Root
-     * Admin can migrate a VM.
+     * Only Root Admin can migrate a VM.
      *
-     * @param destinationStorage
-     *            TODO
-     * @param Long
-     *            vmId
-     *            vmId of The VM to migrate
-     * @param Host
-     *            destinationHost to migrate the VM
+     * @param vmId The ID of the VM to be migrated
+     * @param destinationHost The destination host to where the VM will be migrated
      *
      * @return VirtualMachine migrated VM
      * @throws ManagementServerException
@@ -466,14 +457,9 @@
      * Migrate the given VM with its volumes to the destination host. The API returns the migrated VM if it succeeds.
      * Only root admin can migrate a VM.
      *
-     * @param destinationStorage
-     *            TODO
-     * @param Long
-     *            vmId of The VM to migrate
-     * @param Host
-     *            destinationHost to migrate the VM
-     * @param Map
-     *            A map of volume to which pool it should be migrated
+     * @param vmId The ID of the VM to be migrated
+     * @param destinationHost The destination host to where the VM will be migrated
+     * @param volumeToPool A map of volume to which pool it should be migrated
      *
      * @return VirtualMachine migrated VM
      * @throws ManagementServerException
@@ -507,7 +493,7 @@
     /**
      * Finds and returns an encrypted password for a VM.
      *
-     * @param  userVmId
+     * @param  vmId
      * @return Base64 encoded userdata
      */
     String getVmUserData(long vmId);
diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java
index 7a0715f..34c7a42 100644
--- a/api/src/main/java/com/cloud/vm/VirtualMachine.java
+++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java
@@ -57,7 +57,8 @@
         Migrating(true, "VM is being migrated.  host id holds to from host"),
         Error(false, "VM is in error"),
         Unknown(false, "VM state is unknown."),
-        Shutdown(false, "VM state is shutdown from inside");
+        Shutdown(false, "VM state is shutdown from inside"),
+        Restoring(true, "VM is being restored from backup");
 
         private final boolean _transitional;
         String _description;
@@ -126,6 +127,11 @@
             s_fsm.addTransition(new Transition<State, Event>(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging,null));
             s_fsm.addTransition(new Transition<State, Event>(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null));
             s_fsm.addTransition(new Transition<State, Event>(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null));
+            s_fsm.addTransition(new Transition<State, Event>(State.Stopped, Event.RestoringRequested, State.Restoring, null));
+            s_fsm.addTransition(new Transition<State, Event>(State.Expunging, Event.RestoringRequested, State.Restoring, null));
+            s_fsm.addTransition(new Transition<State, Event>(State.Destroyed, Event.RestoringRequested, State.Restoring, null));
+            s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringSuccess, State.Stopped, null));
+            s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringFailed, State.Stopped, null));
 
             s_fsm.addTransition(new Transition<State, Event>(State.Starting, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, Arrays.asList(new Impact[]{Impact.USAGE})));
             s_fsm.addTransition(new Transition<State, Event>(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running, null));
@@ -210,6 +216,9 @@
         AgentReportMigrated,
         RevertRequested,
         SnapshotRequested,
+        RestoringRequested,
+        RestoringFailed,
+        RestoringSuccess,
 
         // added for new VMSync logic
         FollowAgentPowerOnReport,
diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
index dd0a5d7..add2518 100644
--- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java
+++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java
@@ -48,6 +48,10 @@
     String IOTHREADS = "iothreads";
     String IO_POLICY = "io.policy";
 
+    // KVM specific, the number of queues for multiqueue NICs
+    String NIC_MULTIQUEUE_NUMBER = "nic.multiqueue.number";
+    String NIC_PACKED_VIRTQUEUES_ENABLED = "nic.packed.virtqueues.enabled";
+
     // Mac OSX guest specific (internal)
     String SMC_PRESENT = "smc.present";
     String FIRMWARE = "firmware";
diff --git a/api/src/main/java/org/apache/cloudstack/acl/Role.java b/api/src/main/java/org/apache/cloudstack/acl/Role.java
index c25d7a9..5e5ffd5 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/Role.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/Role.java
@@ -23,4 +23,5 @@
 public interface Role extends RoleEntity, InternalIdentity, Identity {
     RoleType getRoleType();
     boolean isDefault();
+    boolean isPublicRole();
 }
diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
index 578c13e..9becce6 100644
--- a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
+++ b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java
@@ -38,15 +38,17 @@
      *  Moreover, we will check if the requested role is of 'Admin' type; roles with 'Admin' type should only be visible to 'root admins'.
      *  Therefore, if a non-'root admin' user tries to search for an 'Admin' role, this method will return null.
      */
+    Role findRole(Long id, boolean removePrivateRoles);
+
     Role findRole(Long id);
 
-    Role createRole(String name, RoleType roleType, String description);
+    Role createRole(String name, RoleType roleType, String description, boolean publicRole);
 
-    Role createRole(String name, Role role, String description);
+    Role createRole(String name, Role role, String description, boolean publicRole);
 
-    Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced);
+    Role importRole(String name, RoleType roleType, String description, List<Map<String, Object>> rules, boolean forced, boolean isPublicRole);
 
-    Role updateRole(Role role, String name, RoleType roleType, String description);
+    Role updateRole(Role role, String name, RoleType roleType, String description, Boolean publicRole);
 
     boolean deleteRole(Role role);
 
diff --git a/api/src/main/java/org/apache/cloudstack/annotation/AnnotationService.java b/api/src/main/java/org/apache/cloudstack/annotation/AnnotationService.java
index c72fba2..51c6286 100644
--- a/api/src/main/java/org/apache/cloudstack/annotation/AnnotationService.java
+++ b/api/src/main/java/org/apache/cloudstack/annotation/AnnotationService.java
@@ -45,7 +45,7 @@
         SERVICE_OFFERING(false), DISK_OFFERING(false), NETWORK_OFFERING(false),
         ZONE(false), POD(false), CLUSTER(false), HOST(false), DOMAIN(false),
         PRIMARY_STORAGE(false), SECONDARY_STORAGE(false), VR(false), SYSTEM_VM(false),
-        AUTOSCALE_VM_GROUP(true);
+        AUTOSCALE_VM_GROUP(true), MANAGEMENT_SERVER(false),;
 
         private final boolean usersAllowed;
 
@@ -77,6 +77,7 @@
             list.add(EntityType.SECONDARY_STORAGE);
             list.add(EntityType.VR);
             list.add(EntityType.SYSTEM_VM);
+            list.add(EntityType.MANAGEMENT_SERVER);
             if (roleType != RoleType.DomainAdmin) {
                 list.add(EntityType.DOMAIN);
                 list.add(EntityType.SERVICE_OFFERING);
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java
index 0a7ece7..9267ca6 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java
@@ -77,7 +77,8 @@
     Pod(com.cloud.dc.Pod.class),
     VmSnapshot(com.cloud.vm.snapshot.VMSnapshot.class),
     Role(org.apache.cloudstack.acl.Role.class),
-    VpnCustomerGateway(com.cloud.network.Site2SiteCustomerGateway.class);
+    VpnCustomerGateway(com.cloud.network.Site2SiteCustomerGateway.class),
+    ManagementServer(org.apache.cloudstack.management.ManagementServerHost.class);
 
     private final Class<?> clazz;
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 9f0418a..3e0e652 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -32,6 +32,7 @@
     public static final String ALLOCATED_ONLY = "allocatedonly";
     public static final String ANNOTATION = "annotation";
     public static final String API_KEY = "apikey";
+    public static final String ARCHIVED = "archived";
     public static final String ASYNC_BACKUP = "asyncbackup";
     public static final String AUTO_SELECT = "autoselect";
     public static final String USER_API_KEY = "userapikey";
@@ -247,6 +248,7 @@
     public static final String IP_AVAILABLE = "ipavailable";
     public static final String IP_LIMIT = "iplimit";
     public static final String IP_TOTAL = "iptotal";
+    public static final String IS_CONTROL_NODE = "iscontrolnode";
     public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired";
     public static final String IS_DYNAMIC = "isdynamic";
     public static final String IS_EDGE = "isedge";
@@ -298,6 +300,8 @@
     public static final String NIC = "nic";
     public static final String NIC_NETWORK_LIST = "nicnetworklist";
     public static final String NIC_IP_ADDRESS_LIST = "nicipaddresslist";
+    public static final String NIC_MULTIQUEUE_NUMBER = "nicmultiqueuenumber";
+    public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled";
     public static final String NEW_START_IP = "newstartip";
     public static final String NEW_END_IP = "newendip";
     public static final String NUM_RETRIES = "numretries";
@@ -310,10 +314,14 @@
     public static final String OPTIONS = "options";
     public static final String OS_CATEGORY_ID = "oscategoryid";
     public static final String OS_CATEGORY_NAME = "oscategoryname";
+    public static final String OS_NAME = "osname";
     public static final String OS_ID = "osid";
     public static final String OS_TYPE_ID = "ostypeid";
     public static final String OS_DISPLAY_NAME = "osdisplayname";
     public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor";
+    public static final String GUEST_OS_LIST = "guestoslist";
+    public static final String GUEST_OS_COUNT = "guestoscount";
+    public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled";
     public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
     public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";
     public static final String OUTPUT = "output";
@@ -365,6 +373,7 @@
     public static final String RESOURCE_TYPE = "resourcetype";
     public static final String RESOURCE_TYPE_NAME = "resourcetypename";
     public static final String RESPONSE = "response";
+    public static final String RETRIEVE_ONLY_RESOURCE_COUNT = "retrieveonlyresourcecount";
     public static final String REVERTABLE = "revertable";
     public static final String REVOKED = "revoked";
     public static final String REGISTERED = "registered";
@@ -457,6 +466,7 @@
     public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename";
     public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
     public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
+    public static final String VIRTUAL_MACHINE_TYPE = "virtualmachinetype";
     public static final String VIRTUAL_MACHINES = "virtualmachines";
     public static final String USAGE_ID = "usageid";
     public static final String USAGE_TYPE = "usagetype";
@@ -522,6 +532,7 @@
     public static final String PRIVATE_NETWORK_ID = "privatenetworkid";
     public static final String ALLOCATION_STATE = "allocationstate";
     public static final String MANAGED_STATE = "managedstate";
+    public static final String MANAGEMENT_SERVER_ID = "managementserverid";
     public static final String STORAGE_ID = "storageid";
     public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip";
     public static final String PING_DIR = "pingdir";
@@ -1017,11 +1028,20 @@
     public static final String LOGOUT = "logout";
     public static final String LIST_IDPS = "listIdps";
 
+    public static final String READY_FOR_SHUTDOWN = "readyforshutdown";
+    public static final String SHUTDOWN_TRIGGERED = "shutdowntriggered";
+    public static final String PENDING_JOBS_COUNT = "pendingjobscount";
+
     public static final String PUBLIC_MTU = "publicmtu";
     public static final String PRIVATE_MTU = "privatemtu";
     public static final String MTU = "mtu";
+    public static final String AUTO_ENABLE_KVM_HOST = "autoenablekvmhost";
     public static final String LIST_APIS = "listApis";
 
+    public static final String SOURCE_NAT_IP = "sourcenatipaddress";
+    public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
+    public static final String HAS_RULES = "hasrules";
+
     /**
      * This enum specifies IO Drivers, each option controls specific policies on I/O.
      * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java
new file mode 100644
index 0000000..0e8e136
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/BaseListRetrieveOnlyResourceCountCmd.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api;
+
+import org.apache.commons.lang3.BooleanUtils;
+
+public abstract class BaseListRetrieveOnlyResourceCountCmd extends BaseListTaggedResourcesCmd {
+    @Parameter(name = ApiConstants.RETRIEVE_ONLY_RESOURCE_COUNT, type = CommandType.BOOLEAN, description = "makes the API's response contains only the resource count")
+    private Boolean retrieveOnlyResourceCount;
+
+    public Boolean getRetrieveOnlyResourceCount() {
+        return BooleanUtils.toBooleanDefaultIfNull(retrieveOnlyResourceCount, false);
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
index 31f06df..88b3571 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
@@ -62,6 +62,7 @@
 import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.cloudstack.api.response.HostResponse;
 import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
+import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse;
 import org.apache.cloudstack.api.response.IPAddressResponse;
 import org.apache.cloudstack.api.response.ImageStoreResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
@@ -463,6 +464,8 @@
 
     GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor);
 
+    HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse(List<Pair<String, String>> hypervisorGuestOsNames);
+
     SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule sched);
 
     UsageRecordResponse createUsageResponse(Usage usageRecord);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
index 2b88003..8a469df 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/CreateRoleCmd.java
@@ -48,6 +48,10 @@
             description = "ID of the role to be cloned from. Either roleid or type must be passed in")
     private Long roleId;
 
+    @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
+            " If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
+    private boolean publicRole = true;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -60,6 +64,9 @@
         return roleId;
     }
 
+    public boolean isPublicRole() {
+        return publicRole;
+    }
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -81,10 +88,10 @@
             }
 
             CallContext.current().setEventDetails("Role: " + getRoleName() + ", from role: " + getRoleId() + ", description: " + getRoleDescription());
-            role = roleService.createRole(getRoleName(), existingRole, getRoleDescription());
+            role = roleService.createRole(getRoleName(), existingRole, getRoleDescription(), isPublicRole());
         } else {
             CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
-            role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription());
+            role = roleService.createRole(getRoleName(), getRoleType(), getRoleDescription(), isPublicRole());
         }
 
         if (role == null) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
index 4f3e9d1..058650c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ImportRoleCmd.java
@@ -64,6 +64,10 @@
             description = "Force create a role with the same name. This overrides the role type, description and rule permissions for the existing role. Default is false.")
     private Boolean forced;
 
+    @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
+            " If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
+    private boolean publicRole = true;
+
     @Inject
     ApiServerService _apiServer;
 
@@ -114,6 +118,10 @@
         return (forced != null) ? forced : false;
     }
 
+    public boolean isPublicRole() {
+        return publicRole;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -130,7 +138,7 @@
         }
 
         CallContext.current().setEventDetails("Role: " + getRoleName() + ", type: " + getRoleType() + ", description: " + getRoleDescription());
-        Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced());
+        Role role = roleService.importRole(getRoleName(), getRoleType(), getRoleDescription(), getRules(), isForced(), isPublicRole());
         if (role == null) {
             throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to import role");
         }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
index b55dc80..fef2b27 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java
@@ -92,6 +92,7 @@
             roleResponse.setRoleType(role.getRoleType());
             roleResponse.setDescription(role.getDescription());
             roleResponse.setIsDefault(role.isDefault());
+            roleResponse.setPublicRole(role.isPublicRole());
             roleResponse.setObjectName("role");
             roleResponses.add(roleResponse);
         }
@@ -104,7 +105,7 @@
     public void execute() {
         Pair<List<Role>, Integer> roles;
         if (getId() != null && getId() > 0L) {
-            roles = new Pair<List<Role>, Integer>(Collections.singletonList(roleService.findRole(getId())), 1);
+            roles = new Pair<>(Collections.singletonList(roleService.findRole(getId(), true)), 1);
         } else if (StringUtils.isNotBlank(getName()) || StringUtils.isNotBlank(getKeyword())) {
             roles = roleService.findRolesByName(getName(), getKeyword(), getStartIndex(), getPageSizeVal());
         } else if (getRoleType() != null) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
index e652918..4c317d0 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java
@@ -58,6 +58,7 @@
         response.setRoleName(role.getName());
         response.setRoleType(role.getRoleType());
         response.setDescription(role.getDescription());
+        response.setPublicRole(role.isPublicRole());
         response.setResponseName(getCommandName());
         response.setObjectName("role");
         setResponseObject(response);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
index 227aaf5..7d002cd 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/UpdateRoleCmd.java
@@ -52,6 +52,9 @@
     @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "The description of the role")
     private String roleDescription;
 
+    @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Indicates whether the role will be visible to all users (public) or only to root admins (private).")
+    private Boolean publicRole;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -64,6 +67,10 @@
         return roleName;
     }
 
+    public Boolean isPublicRole() {
+        return publicRole;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -80,7 +87,7 @@
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role id provided");
         }
         CallContext.current().setEventDetails("Role: " + getRoleName() + ", type:" + getRoleType() + ", description: " + getRoleDescription());
-        role = roleService.updateRole(role, getRoleName(), getRoleType(), getRoleDescription());
+        role = roleService.updateRole(role, getRoleName(), getRoleType(), getRoleDescription(), isPublicRole());
         setupResponse(role);
     }
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
index 3adc711..af74874 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.guest;
 
+import org.apache.commons.collections.MapUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -57,7 +58,7 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "Optional name for Guest OS")
     private String osName;
 
-    @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = true, description = "Map of (key/value pairs)")
+    @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)")
     private Map details;
 
 
@@ -79,7 +80,7 @@
 
     public Map getDetails() {
         Map<String, String> detailsMap = new HashMap<String, String>();
-        if (!details.isEmpty()) {
+        if (!MapUtils.isEmpty(details)) {
             Collection<?> servicesCollection = details.values();
             Iterator<?> iter = servicesCollection.iterator();
             while (iter.hasNext()) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java
index 6d9ac52..0ddd219 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.guest;
 
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -51,12 +52,18 @@
     @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor type. One of : XenServer, KVM, VMWare")
     private String hypervisor;
 
-    @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to create the mapping for. Use 'default' for default versions")
+    @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to create the mapping. Use 'default' for default versions. Please check hypervisor capabilities for correct version")
     private String hypervisorVersion;
 
     @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = true, description = "OS name specific to the hypervisor")
     private String osNameForHypervisor;
 
+    @Parameter(name = ApiConstants.OS_MAPPING_CHECK_ENABLED, type = CommandType.BOOLEAN, required = false, description = "When set to true, checks for the correct guest os mapping name in the provided hypervisor (supports VMware and XenServer only. At least one hypervisor host with the version specified must be available. Default version will not work.)", since = "4.19.0")
+    private Boolean osMappingCheckEnabled;
+
+    @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, required = false, description = "Forces add user defined guest os mapping, overrides any existing user defined mapping", since = "4.19.0")
+    private Boolean forced;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -81,6 +88,14 @@
         return osNameForHypervisor;
     }
 
+    public Boolean getOsMappingCheckEnabled() {
+        return BooleanUtils.toBooleanDefaultIfNull(osMappingCheckEnabled, false);
+    }
+
+    public boolean isForced() {
+        return BooleanUtils.toBooleanDefaultIfNull(forced, false);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java
new file mode 100644
index 0000000..7951770
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/GetHypervisorGuestOsNamesCmd.java
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.guest;
+
+import java.util.List;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.user.Account;
+import com.cloud.utils.Pair;
+
+@APICommand(name = GetHypervisorGuestOsNamesCmd.APINAME, description = "Gets the guest OS names in the hypervisor", responseObject = HypervisorGuestOsNamesResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin})
+public class GetHypervisorGuestOsNamesCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = Logger.getLogger(GetHypervisorGuestOsNamesCmd.class.getName());
+
+    public static final String APINAME = "getHypervisorGuestOsNames";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor type. One of : VMware, XenServer",
+            validations = {ApiArgValidator.NotNullOrEmpty})
+    private String hypervisor;
+
+    @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to get the guest os names (atleast one hypervisor host with the version specified must be available)",
+            validations = {ApiArgValidator.NotNullOrEmpty})
+    private String hypervisorVersion;
+
+    @Parameter(name = ApiConstants.KEYWORD, type = CommandType.STRING, required = false, description = "Keyword for guest os name")
+    private String keyword;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getHypervisor() {
+        return hypervisor;
+    }
+
+    public String getHypervisorVersion() {
+        return hypervisorVersion;
+    }
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    @Override
+    public void execute() {
+        List<Pair<String, String>> hypervisorGuestOsNames = _mgr.getHypervisorGuestOsNames(this);
+        HypervisorGuestOsNamesResponse response = _responseGenerator.createHypervisorGuestOSNamesResponse(hypervisorGuestOsNames);
+        response.setHypervisor(getHypervisor());
+        response.setHypervisorVersion(getHypervisorVersion());
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_GUEST_OS_HYPERVISOR_NAME_FETCH;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Getting guest OS names from hypervisor: " + getHypervisor() + ", version: " + getHypervisorVersion();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java
index 70c039f..29ae0b4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java
@@ -48,6 +48,12 @@
     @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, description = "list mapping by Guest OS Type UUID")
     private Long osTypeId;
 
+    @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = false, description = "list Guest OS mapping by OS display name")
+    private String osDisplayName;
+
+    @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = false, description = "list Guest OS mapping by OS mapping name with hypervisor")
+    private String osNameForHypervisor;
+
     @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = false, description = "list Guest OS mapping by hypervisor")
     private String hypervisor;
 
@@ -66,6 +72,14 @@
         return osTypeId;
     }
 
+    public String getOsDisplayName() {
+        return osDisplayName;
+    }
+
+    public String getOsNameForHypervisor() {
+        return osNameForHypervisor;
+    }
+
     public String getHypervisor() {
         return hypervisor;
     }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
index 1168bf0..f9639f7 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.guest;
 
+import org.apache.commons.collections.MapUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -53,7 +54,7 @@
     @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS")
     private String osDisplayName;
 
-    @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = true, description = "Map of (key/value pairs)")
+    @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)")
     private Map details;
 
 
@@ -71,7 +72,7 @@
 
     public Map getDetails() {
         Map<String, String> detailsMap = new HashMap<String, String>();;
-        if (!details.isEmpty()) {
+        if (MapUtils.isNotEmpty(detailsMap)) {
             Collection<?> servicesCollection = details.values();
             Iterator<?> iter = servicesCollection.iterator();
             while (iter.hasNext()) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java
index 679ec35..c83be13 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.guest;
 
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -47,6 +48,9 @@
     @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor specific name for this Guest OS")
     private String osNameForHypervisor;
 
+    @Parameter(name = ApiConstants.OS_MAPPING_CHECK_ENABLED, type = CommandType.BOOLEAN, required = false, description = "When set to true, checks for the correct guest os mapping name in the provided hypervisor (supports VMware and XenServer only. At least one hypervisor host with the version specified must be available. Default version will not work.)", since = "4.19.0")
+    private Boolean osMappingCheckEnabled;
+
 /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -59,6 +63,10 @@
         return osNameForHypervisor;
     }
 
+    public Boolean getOsMappingCheckEnabled() {
+        return BooleanUtils.toBooleanDefaultIfNull(osMappingCheckEnabled, false);
+    }
+
     @Override
     public void execute() {
         GuestOSHypervisor guestOsMapping = _mgr.updateGuestOsMapping(this);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java
index 5ca53c0..e3ff130 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java
@@ -19,7 +19,6 @@
 import com.cloud.host.Host;
 import com.cloud.user.Account;
 import org.apache.cloudstack.acl.RoleType;
-import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
@@ -117,9 +116,6 @@
         Host result;
         try {
             result = _resourceService.updateHost(this);
-            if(getAnnotation() != null) {
-                annotationService.addAnnotation(getAnnotation(), AnnotationService.EntityType.HOST, result.getUuid(), true);
-            }
             HostResponse hostResponse = _responseGenerator.createHostResponse(result);
             hostResponse.setResponseName(getCommandName());
             this.setResponseObject(hostResponse);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
index ac6dee1..8bf6fce 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java
@@ -28,6 +28,7 @@
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -58,7 +59,7 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the network offering")
     private String networkOfferingName;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of the network offering")
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the network offering, defaults to the value of 'name'.")
     private String displayText;
 
     @Parameter(name = ApiConstants.TRAFFIC_TYPE,
@@ -182,7 +183,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? networkOfferingName : displayText;
     }
 
     public String getTags() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
index 304b290..c2d8b3b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
@@ -36,6 +36,7 @@
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.offering.DiskOffering;
@@ -56,7 +57,7 @@
     @Parameter(name = ApiConstants.DISK_SIZE, type = CommandType.LONG, required = false, description = "size of the disk offering in GB (1GB = 1,073,741,824 bytes)")
     private Long diskSize;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "alternate display text of the disk offering", length = 4096)
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "An alternate display text of the disk offering, defaults to 'name'.", length = 4096)
     private String displayText;
 
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the disk offering")
@@ -179,7 +180,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? offeringName : displayText;
     }
 
     public String getOfferingName() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
index 24f5682..d947f6f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
@@ -59,7 +59,7 @@
     @Parameter(name = ApiConstants.CPU_SPEED, type = CommandType.INTEGER, required = false, description = "the CPU speed of the service offering in MHz.")
     private Integer cpuSpeed;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of the service offering")
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the service offering, defaults to 'name'.")
     private String displayText;
 
     @Parameter(name = ApiConstants.PROVISIONINGTYPE, type = CommandType.STRING, description = "provisioning type used to create volumes. Valid values are thin, sparse, fat.")
@@ -258,10 +258,7 @@
     }
 
     public String getDisplayText() {
-        if (StringUtils.isEmpty(displayText)) {
-            throw new InvalidParameterValueException("Failed to create service offering because the offering display text has not been spified.");
-        }
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? serviceOfferingName : displayText;
     }
 
     public String getProvisioningType() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java
index 731cb67..549d02b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java
@@ -83,6 +83,12 @@
                "<1b331390-59f2-4796-9993-bf11c6e76225>&migrateto[2].pool=<41fdb564-9d3b-447d-88ed-7628f7640cbc>")
     private Map migrateVolumeTo;
 
+    @Parameter(name = ApiConstants.AUTO_SELECT,
+            since = "4.19.0",
+            type = CommandType.BOOLEAN,
+            description = "Automatically select a destination host for a running instance, if hostId is not specified. false by default")
+    private Boolean autoSelect;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -144,31 +150,39 @@
         return ApiCommandResourceType.VirtualMachine;
     }
 
+    private Host getDestinationHost() {
+        if (getHostId() == null) {
+            return null;
+        }
+        Host destinationHost = _resourceService.getHost(getHostId());
+        // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs
+        if (destinationHost == null) {
+            s_logger.error(String.format("Unable to find the host with ID [%s].", getHostId()));
+            throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM.");
+        }
+        return destinationHost;
+    }
+
     @Override
     public void execute() {
-        if (hostId == null && MapUtils.isEmpty(migrateVolumeTo)) {
-            throw new InvalidParameterValueException(String.format("Either %s or %s must be passed for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO));
+        if (hostId == null && MapUtils.isEmpty(migrateVolumeTo) && !Boolean.TRUE.equals(autoSelect)) {
+            throw new InvalidParameterValueException(String.format("Either %s or %s must be passed or %s must be true for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT));
         }
 
         VirtualMachine virtualMachine = _userVmService.getVm(getVirtualMachineId());
-        if (!VirtualMachine.State.Running.equals(virtualMachine.getState()) && hostId != null) {
+        if (!VirtualMachine.State.Running.equals(virtualMachine.getState()) && (hostId != null || Boolean.TRUE.equals(autoSelect))) {
             throw new InvalidParameterValueException(String.format("%s is not in the Running state to migrate it to the new host.", virtualMachine));
         }
 
-        if (!VirtualMachine.State.Stopped.equals(virtualMachine.getState()) && hostId == null) {
-            throw new InvalidParameterValueException(String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.",
-                    virtualMachine, ApiConstants.HOST_ID));
+        if (!VirtualMachine.State.Stopped.equals(virtualMachine.getState()) && hostId == null && Boolean.FALSE.equals(autoSelect)) {
+            throw new InvalidParameterValueException(String.format("%s is not in the Stopped state to migrate, use the %s or %s parameter to migrate it to a new host.",
+                    virtualMachine, ApiConstants.HOST_ID,ApiConstants.AUTO_SELECT));
         }
 
         try {
             VirtualMachine migratedVm = null;
-            if (hostId != null) {
-                Host destinationHost = _resourceService.getHost(getHostId());
-                // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs
-                if (destinationHost == null) {
-                    s_logger.error(String.format("Unable to find the host with ID [%s].", getHostId()));
-                    throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM.");
-                }
+            if (getHostId() != null || Boolean.TRUE.equals(autoSelect)) {
+                Host destinationHost = getDestinationHost();
                 migratedVm = _userVmService.migrateVirtualMachineWithVolume(getVirtualMachineId(), destinationHost, getVolumeToPool());
             } else if (MapUtils.isNotEmpty(migrateVolumeTo)) {
                 migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), getVolumeToPool());
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
index f1c2a5b..b69e7f4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java
@@ -28,6 +28,7 @@
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -56,7 +57,7 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the vpc offering")
     private String vpcOfferingName;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of " + "the vpc offering")
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "the display text of the vpc offering, defaults to the 'name'")
     private String displayText;
 
     @Parameter(name = ApiConstants.SUPPORTED_SERVICES,
@@ -115,7 +116,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? vpcOfferingName : displayText;
     }
 
     public List<String> getSupportedServices() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
index e456074..22eb70c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java
@@ -25,7 +25,7 @@
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
+import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
 import org.apache.cloudstack.api.command.user.UserCmd;
@@ -42,7 +42,7 @@
 
 @APICommand(name = "listPublicIpAddresses", description = "Lists all public IP addresses", responseObject = IPAddressResponse.class, responseView = ResponseView.Restricted,
  requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, entityType = { IpAddress.class })
-public class ListPublicIpAddressesCmd extends BaseListTaggedResourcesCmd implements UserCmd {
+public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd {
     public static final Logger s_logger = Logger.getLogger(ListPublicIpAddressesCmd.class.getName());
 
     private static final String s_name = "listpublicipaddressesresponse";
@@ -173,10 +173,6 @@
         return forVirtualNetwork;
     }
 
-    public Boolean getForLoadBalancing() {
-        return forLoadBalancing;
-    }
-
     public String getState() {
         return state;
     }
@@ -192,12 +188,15 @@
     @Override
     public void execute() {
         Pair<List<? extends IpAddress>, Integer> result = _mgr.searchForIPAddresses(this);
-        ListResponse<IPAddressResponse> response = new ListResponse<IPAddressResponse>();
-        List<IPAddressResponse> ipAddrResponses = new ArrayList<IPAddressResponse>();
-        for (IpAddress ipAddress : result.first()) {
-            IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(getResponseView(), ipAddress);
-            ipResponse.setObjectName("publicipaddress");
-            ipAddrResponses.add(ipResponse);
+        ListResponse<IPAddressResponse> response = new ListResponse<>();
+        List<IPAddressResponse> ipAddrResponses = new ArrayList<>();
+
+        if (!getRetrieveOnlyResourceCount()) {
+            for (IpAddress ipAddress : result.first()) {
+                IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(getResponseView(), ipAddress);
+                ipResponse.setObjectName("publicipaddress");
+                ipAddrResponses.add(ipResponse);
+            }
         }
 
         response.setResponses(ipAddrResponses, result.second());
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java
index b4f152e..cdbe153 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java
@@ -232,7 +232,7 @@
                 responseObject.setResponseName(getCommandName());
             }
         } catch (Exception ex) {
-            // TODO what will happen if Resource Layer fails in a step inbetween
+            // TODO what will happen if Resource Layer fails in a step in between
             s_logger.warn("Failed to create autoscale vm group", ex);
         } finally {
             if (!success || vmGroup == null) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java
index 202bd36..89f1c70 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java
@@ -72,6 +72,9 @@
     @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the event", since="4.17.0")
     private String resourceType;
 
+    @Parameter(name = ApiConstants.ARCHIVED, type = CommandType.BOOLEAN, description = "true to list archived events otherwise false", since="4.19.0")
+    private Boolean archived;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -116,6 +119,10 @@
         return resourceType;
     }
 
+    public boolean getArchived() {
+        return archived != null && archived;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
index 47018b3..ecab393 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
@@ -34,6 +34,7 @@
 import org.apache.cloudstack.api.response.TemplateResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.exception.ResourceAllocationException;
@@ -55,8 +56,7 @@
 
     @Parameter(name = ApiConstants.DISPLAY_TEXT,
                type = CommandType.STRING,
-               required = true,
-               description = "the display text of the ISO. This is usually used for display purposes.",
+               description = "the display text of the ISO, defaults to the 'name'",
                length = 4096)
     private String displayText;
 
@@ -133,7 +133,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? isoName : displayText;
     }
 
     public void setDisplayText(String displayText) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java
index d2574ff..783d78f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/job/ListAsyncJobsCmd.java
@@ -24,6 +24,7 @@
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.response.AsyncJobResponse;
 import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.ManagementServerResponse;
 
 @APICommand(name = "listAsyncJobs", description = "Lists all pending asynchronous jobs for the account.", responseObject = AsyncJobResponse.class,
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -36,6 +37,9 @@
     @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "The start date of the async job (use format \"yyyy-MM-dd'T'HH:mm:ss'+'SSSS\")")
     private Date startDate;
 
+    @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "The id of the management server", since="4.19")
+    private Long managementServerId;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -44,6 +48,10 @@
         return startDate;
     }
 
+    public Long getManagementServerId() {
+        return managementServerId;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java
index 45e6f81..66a1598 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java
@@ -68,7 +68,7 @@
     @Parameter(name = ApiConstants.METHOD_NAME,
                type = CommandType.STRING,
                required = true,
-               description = "name of the load balancer stickiness policy method, possible values can be obtained from listNetworks API")
+               description = "name of the load balancer stickiness policy method, possible values are LbCookie, AppCookie, SourceBased")
     private String stickinessMethodName;
 
     @Parameter(name = ApiConstants.PARAM_LIST, type = CommandType.MAP, description = "param list. Example: param[0].name=cookiename&param[0].value=LBCookie ")
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
index 5b814e7..2315999 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
@@ -16,7 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.network;
 
-import com.cloud.network.NetworkService;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.acl.RoleType;
@@ -43,10 +43,10 @@
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.network.Network;
+import com.cloud.network.NetworkService;
 import com.cloud.network.Network.GuestType;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.utils.net.NetUtils;
-import org.apache.commons.lang3.StringUtils;
 
 @APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class},
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -183,6 +183,14 @@
     @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network", since = "4.18.0")
     private String ip6Dns2;
 
+    @Parameter(name = ApiConstants.SOURCE_NAT_IP,
+            type = CommandType.STRING,
+            description = "IPV4 address to be assigned to the public interface of the network router. " +
+                    "This address will be used as source NAT address for the network. " +
+                    "\nIf an address is given and it cannot be acquired, an error will be returned and the network won´t be implemented,",
+            since = "4.19")
+    private String sourceNatIP;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -266,6 +274,10 @@
         return tungstenVirtualRouterUuid;
     }
 
+    public String getSourceNatIP() {
+        return sourceNatIP;
+    }
+
     @Override
     public boolean isDisplay() {
         if(displayNetwork == null)
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java
index df82d9f..f370c03 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java
@@ -23,12 +23,13 @@
 import com.cloud.server.ResourceTag;
 import org.apache.cloudstack.api.response.NetworkOfferingResponse;
 import org.apache.cloudstack.api.response.ResourceIconResponse;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
+import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
 import org.apache.cloudstack.api.command.user.UserCmd;
@@ -44,7 +45,7 @@
 
 @APICommand(name = "listNetworks", description = "Lists all available networks.", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class},
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
-public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserCmd {
+public class ListNetworksCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd {
     public static final Logger s_logger = Logger.getLogger(ListNetworksCmd.class.getName());
     private static final String s_name = "listnetworksresponse";
 
@@ -190,14 +191,11 @@
 
     @Override
     public Boolean getDisplay() {
-        if (display != null) {
-            return display;
-        }
-        return super.getDisplay();
+        return BooleanUtils.toBooleanDefaultIfNull(display, super.getDisplay());
     }
 
     public Boolean getShowIcon() {
-        return showIcon != null ? showIcon : false;
+        return BooleanUtils.toBooleanDefaultIfNull(showIcon, false);
     }
 
     public String getNetworkFilter() {
@@ -215,16 +213,21 @@
     @Override
     public void execute() {
         Pair<List<? extends Network>, Integer> networks = _networkService.searchForNetworks(this);
-        ListResponse<NetworkResponse> response = new ListResponse<NetworkResponse>();
-        List<NetworkResponse> networkResponses = new ArrayList<NetworkResponse>();
-        for (Network network : networks.first()) {
-            NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(getResponseView(), network);
-            networkResponses.add(networkResponse);
+        ListResponse<NetworkResponse> response = new ListResponse<>();
+        List<NetworkResponse> networkResponses = new ArrayList<>();
+
+        if (!getRetrieveOnlyResourceCount()) {
+            for (Network network : networks.first()) {
+                NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(getResponseView(), network);
+                networkResponses.add(networkResponse);
+            }
         }
+
         response.setResponses(networkResponses, networks.second());
         response.setResponseName(getCommandName());
         setResponseObject(response);
-        if (response != null && response.getCount() > 0 && getShowIcon()) {
+
+        if (!getRetrieveOnlyResourceCount() && response.getCount() > 0 && getShowIcon()) {
             updateNetworkResponse(response.getResponses());
         }
     }
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
index 3aef973..d3cc169 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
@@ -104,6 +104,9 @@
     @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network. Empty string will update the second IPv6 DNS with the value from the zone", since = "4.18.0")
     private String ip6Dns2;
 
+    @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this network", since = "4.19")
+    private String sourceNatIP;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -181,6 +184,10 @@
         return ip6Dns2;
     }
 
+    public String getSourceNatIP() {
+        return sourceNatIP;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java
index a0902d6..a5742e8 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java
@@ -27,6 +27,7 @@
 import org.apache.cloudstack.api.response.ProjectResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.event.EventTypes;
@@ -61,7 +62,7 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the project")
     private String name;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "display text of the project")
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING,  description = "The display text of the project, defaults to the 'name´.")
     private String displayText;
 
     // ///////////////////////////////////////////////////
@@ -98,7 +99,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? name : displayText;
     }
 
     @Override
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java
index 8732f08..6520aa6 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/UpdateProjectCmd.java
@@ -28,6 +28,7 @@
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.commons.lang3.EnumUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.event.EventTypes;
@@ -35,7 +36,6 @@
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.projects.Project;
 import com.cloud.projects.ProjectAccount;
-import org.apache.commons.lang3.StringUtils;
 
 @APICommand(name = "updateProject", description = "Updates a project", responseObject = ProjectResponse.class, since = "3.0.0",
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -67,6 +67,9 @@
             "to promote or demote the user/account based on the roleType (Regular or Admin) provided. Defaults to true")
     private Boolean swapOwner;
 
+    @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the project", since = "4.19.0")
+    private String name;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -87,6 +90,10 @@
         return userId;
     }
 
+    public String getName() {
+        return name;
+    }
+
     public ProjectAccount.Role getRoleType(String role) {
         String type = role.substring(0, 1).toUpperCase() + role.substring(1).toLowerCase();
         if (!EnumUtils.isValidEnum(ProjectAccount.Role.class, type)) {
@@ -136,9 +143,9 @@
 
         Project project = null;
         if (isSwapOwner()) {
-            project = _projectService.updateProject(getId(), getDisplayText(), getAccountName());
+            project = _projectService.updateProject(getId(), getName(), getDisplayText(), getAccountName());
         }  else {
-            project = _projectService.updateProject(getId(), getDisplayText(), getAccountName(), getUserId(), getAccountRole());
+            project = _projectService.updateProject(getId(), getName(), getDisplayText(), getAccountName(), getUserId(), getAccountRole());
         }
         if (project != null) {
             ProjectResponse response = _responseGenerator.createProjectResponse(project);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java
index a21cbfb..ea4b599 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java
@@ -29,6 +29,7 @@
 import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.api.response.ProjectResponse;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.api.APICommand;
@@ -67,8 +68,7 @@
 
     @Parameter(name = ApiConstants.DISPLAY_TEXT,
                type = CommandType.STRING,
-               required = true,
-               description = "the display text of the template. This is usually used for display purposes.",
+               description = "The display text of the template, defaults to the 'name'.",
                length = 4096)
     private String displayText;
 
@@ -144,7 +144,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? templateName : displayText;
     }
 
     public Boolean isFeatured() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
index 255b11a..1a5e851 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
@@ -39,6 +39,7 @@
 import org.apache.cloudstack.api.response.TemplateResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.exception.ResourceAllocationException;
@@ -60,8 +61,7 @@
 
     @Parameter(name = ApiConstants.DISPLAY_TEXT,
                type = CommandType.STRING,
-               required = true,
-               description = "the display text of the template. This is usually used for display purposes.",
+               description = "The display text of the template, defaults to 'name'.",
                length = 4096)
     private String displayText;
 
@@ -176,7 +176,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? templateName : displayText;
     }
 
     public String getFormat() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java
new file mode 100644
index 0000000..935f39b
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+
+import javax.inject.Inject;
+import java.util.Date;
+
+@APICommand(name = "createVMSchedule", description = "Create VM Schedule", responseObject = VMScheduleResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateVMScheduleCmd extends BaseCmd {
+
+    @Inject
+    VMScheduleManager vmScheduleManager;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM for which schedule is to be defined")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.DESCRIPTION,
+            type = CommandType.STRING,
+            required = false,
+            description = "Description of the schedule")
+    private String description;
+
+    @Parameter(name = ApiConstants.SCHEDULE,
+            type = CommandType.STRING,
+            required = true,
+            description = "Schedule for action on VM in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'")
+    private String schedule;
+
+    @Parameter(name = ApiConstants.TIMEZONE,
+            type = CommandType.STRING,
+            required = true,
+            description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.")
+    private String timeZone;
+
+    @Parameter(name = ApiConstants.ACTION,
+            type = CommandType.STRING,
+            required = true,
+            description = "Action to take on the VM (start/stop/restart/force_stop/force_reboot).")
+    private String action;
+
+    @Parameter(name = ApiConstants.START_DATE,
+            type = CommandType.DATE,
+            required = false,
+            description = "start date from which the schedule becomes active. Defaults to current date plus 1 minute."
+                    + "Use format \"yyyy-MM-dd hh:mm:ss\")")
+    private Date startDate;
+
+    @Parameter(name = ApiConstants.END_DATE,
+            type = CommandType.DATE,
+            required = false,
+            description = "end date after which the schedule becomes inactive"
+                    + "Use format \"yyyy-MM-dd hh:mm:ss\")")
+    private Date endDate;
+
+    @Parameter(name = ApiConstants.ENABLED,
+            type = CommandType.BOOLEAN,
+            required = false,
+            description = "Enable VM schedule. Defaults to true")
+    private Boolean enabled;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getSchedule() {
+        return schedule;
+    }
+
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    public Boolean getEnabled() {
+        if (enabled == null) {
+            enabled = true;
+        }
+        return enabled;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        VMScheduleResponse response = vmScheduleManager.createSchedule(this);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId());
+        if (vm == null) {
+            throw new InvalidParameterValueException(String.format("Unable to find VM by id=%d", getVmId()));
+        }
+        return vm.getAccountId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java
new file mode 100644
index 0000000..775a902
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmd.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+
+import javax.inject.Inject;
+import java.util.Collections;
+import java.util.List;
+
+@APICommand(name = "deleteVMSchedule", description = "Delete VM Schedule.", responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteVMScheduleCmd extends BaseCmd {
+    @Inject
+    VMScheduleManager vmScheduleManager;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of VM")
+    private Long vmId;
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = VMScheduleResponse.class,
+            required = false,
+            description = "ID of VM schedule")
+    private Long id;
+    @Parameter(name = ApiConstants.IDS,
+            type = CommandType.LIST,
+            collectionType = CommandType.UUID,
+            entityType = VMScheduleResponse.class,
+            required = false,
+            description = "IDs of VM schedule")
+    private List<Long> ids;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public List<Long> getIds() {
+        if (ids == null) {
+            return Collections.emptyList();
+        }
+        return ids;
+    }
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        long rowsRemoved = vmScheduleManager.removeSchedule(this);
+
+        if (rowsRemoved > 0) {
+            final SuccessResponse response = new SuccessResponse();
+            response.setResponseName(getCommandName());
+            response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
+            setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete VM Schedules");
+        }
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, getVmId());
+        if (vm == null) {
+            throw new InvalidParameterValueException(String.format("Unable to find VM by id=%d", getVmId()));
+        }
+        return vm.getAccountId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 78d6155..cd7de7e 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -263,6 +263,14 @@
     @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO")
     private String ioDriverPolicy;
 
+    @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18",
+            description = "The number of queues for multiqueue NICs.")
+    private Integer nicMultiqueueNumber;
+
+    @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18",
+            description = "Enable packed virtqueues or not.")
+    private Boolean nicPackedVirtQueues;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -337,6 +345,14 @@
             customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled));
         }
 
+        if (nicMultiqueueNumber != null) {
+            customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString());
+        }
+
+        if (BooleanUtils.toBoolean(nicPackedVirtQueues)) {
+            customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues));
+        }
+
         return customparameterMap;
     }
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java
new file mode 100644
index 0000000..3474f1d
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmd.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+
+import javax.inject.Inject;
+
+@APICommand(name = "listVMSchedule", description = "List VM Schedules.", responseObject = VMScheduleResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ListVMScheduleCmd extends BaseListCmd {
+    @Inject
+    VMScheduleManager vmScheduleManager;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM for which schedule is to be defined")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = VMScheduleResponse.class,
+            required = false,
+            description = "ID of VM schedule")
+    private Long id;
+
+    @Parameter(name = ApiConstants.ACTION,
+            type = CommandType.STRING,
+            required = false,
+            description = "Action taken by schedule")
+    private String action;
+
+    @Parameter(name = ApiConstants.ENABLED,
+            type = CommandType.BOOLEAN,
+            required = false,
+            description = "ID of VM schedule")
+    private Boolean enabled;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public Boolean getEnabled() {
+        return enabled;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+    @Override
+    public void execute() {
+        ListResponse<VMScheduleResponse> response = vmScheduleManager.listSchedule(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
+        setResponseObject(response);
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java
index e609655..07d83b4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java
@@ -26,7 +26,7 @@
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiConstants.VMDetails;
-import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
+import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
 import org.apache.cloudstack.api.command.user.UserCmd;
@@ -44,6 +44,7 @@
 import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.api.response.VpcResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.exception.InvalidParameterValueException;
@@ -54,7 +55,7 @@
 
 @APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
-public class ListVMsCmd extends BaseListTaggedResourcesCmd implements UserCmd {
+public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd {
     public static final Logger s_logger = Logger.getLogger(ListVMsCmd.class.getName());
 
     private static final String s_name = "listvirtualmachinesresponse";
@@ -148,7 +149,6 @@
     @Parameter(name = ApiConstants.USER_DATA, type = CommandType.BOOLEAN, description = "Whether to return the VMs' user data or not. By default, user data will not be returned.", since = "4.18.0.0")
     private Boolean showUserData;
 
-
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -255,14 +255,11 @@
 
     @Override
     public Boolean getDisplay() {
-        if (display != null) {
-            return display;
-        }
-        return super.getDisplay();
+        return BooleanUtils.toBooleanDefaultIfNull(display, super.getDisplay());
     }
 
     public Boolean getShowIcon() {
-        return showIcon != null ? showIcon : false;
+        return BooleanUtils.toBooleanDefaultIfNull(showIcon, false);
     }
 
     public Boolean getAccumulate() {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java
new file mode 100644
index 0000000..72f85c1
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+
+import javax.inject.Inject;
+import java.util.Date;
+
+@APICommand(name = "updateVMSchedule", description = "Update VM Schedule.", responseObject = VMScheduleResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateVMScheduleCmd extends BaseCmd {
+    @Inject
+    VMScheduleManager vmScheduleManager;
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = VMScheduleResponse.class,
+            required = true,
+            description = "ID of VM schedule")
+    private Long id;
+
+    @Parameter(name = ApiConstants.DESCRIPTION,
+            type = CommandType.STRING,
+            required = false,
+            description = "Name of the schedule")
+    private String description;
+
+    @Parameter(name = ApiConstants.SCHEDULE,
+            type = CommandType.STRING,
+            required = false,
+            description = "Schedule for action on VM in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'")
+    private String schedule;
+
+    @Parameter(name = ApiConstants.TIMEZONE,
+            type = CommandType.STRING,
+            required = false,
+            description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.")
+    private String timeZone;
+
+    @Parameter(name = ApiConstants.START_DATE,
+            type = CommandType.DATE,
+            required = false,
+            description = "start date from which the schedule becomes active"
+                    + "Use format \"yyyy-MM-dd hh:mm:ss\")")
+    private Date startDate;
+
+    @Parameter(name = ApiConstants.END_DATE,
+            type = CommandType.DATE,
+            required = false,
+            description = "end date after which the schedule becomes inactive"
+                    + "Use format \"yyyy-MM-dd hh:mm:ss\")")
+    private Date endDate;
+
+    @Parameter(name = ApiConstants.ENABLED,
+            type = CommandType.BOOLEAN,
+            required = false,
+            description = "Enable VM schedule")
+    private Boolean enabled;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getSchedule() {
+        return schedule;
+    }
+
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    public Boolean getEnabled() {
+        return enabled;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        VMScheduleResponse response = vmScheduleManager.updateSchedule(this);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        VMSchedule vmSchedule = _entityMgr.findById(VMSchedule.class, getId());
+        if (vmSchedule == null) {
+            throw new InvalidParameterValueException(String.format("Unable to find vmSchedule by id=%d", getId()));
+        }
+        VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmSchedule.getVmId());
+        return vm.getAccountId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
index fdac3ff..b62a909 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
@@ -22,7 +22,7 @@
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
+import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
 import org.apache.cloudstack.api.command.user.UserCmd;
@@ -35,13 +35,14 @@
 import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.storage.Volume;
 
 @APICommand(name = "listVolumes", description = "Lists all volumes.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {
         Volume.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
-public class ListVolumesCmd extends BaseListTaggedResourcesCmd implements UserCmd {
+public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd {
     public static final Logger s_logger = Logger.getLogger(ListVolumesCmd.class.getName());
 
     private static final String s_name = "listvolumesresponse";
@@ -145,15 +146,13 @@
 
     @Override
     public Boolean getDisplay() {
-        if (display != null) {
-            return display;
-        }
-        return super.getDisplay();
+        return BooleanUtils.toBooleanDefaultIfNull(display, super.getDisplay());
     }
 
     public String getState() {
         return state;
     }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
index af70049..7ca66b2 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
@@ -16,7 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.vpc;
 
-import com.cloud.network.NetworkService;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import org.apache.cloudstack.acl.RoleType;
@@ -40,6 +40,7 @@
 import com.cloud.exception.InsufficientCapacityException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.NetworkService;
 import com.cloud.network.vpc.Vpc;
 
 @APICommand(name = "createVPC", description = "Creates a VPC", responseObject = VpcResponse.class, responseView = ResponseView.Restricted, entityType = {Vpc.class},
@@ -72,8 +73,8 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the VPC")
     private String vpcName;
 
-    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of " +
-            "the VPC")
+    @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, description = "The display text of the VPC, defaults to its 'name'.")
+
     private String displayText;
 
     @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, required = true, description = "the cidr of the VPC. All VPC " +
@@ -112,6 +113,12 @@
     @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the VPC", since = "4.18.0")
     private String ip6Dns2;
 
+    @Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router." +
+            "This address will be used as source NAT address for the networks in ths VPC. " +
+            "\nIf an address is given and it cannot be acquired, an error will be returned and the network won´t be implemented,",
+            since = "4.19")
+    private String sourceNatIP;
+
     // ///////////////////////////////////////////////////
     // ///////////////// Accessors ///////////////////////
     // ///////////////////////////////////////////////////
@@ -137,7 +144,7 @@
     }
 
     public String getDisplayText() {
-        return displayText;
+        return StringUtils.isEmpty(displayText) ? vpcName    : displayText;
     }
 
     public Long getVpcOffering() {
@@ -179,6 +186,15 @@
         return display;
     }
 
+
+    public String getSourceNatIP() {
+        return sourceNatIP;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
     @Override
     public void create() throws ResourceAllocationException {
         Vpc vpc = _vpcService.createVpc(this);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java
index b230603..76cbcca 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListVPCsCmd.java
@@ -142,9 +142,7 @@
     @Override
     public void execute() {
         Pair<List<? extends Vpc>, Integer> vpcs =
-            _vpcService.listVpcs(getId(), getVpcName(), getDisplayText(), getSupportedServices(), getCidr(), getVpcOffId(), getState(), getAccountName(), getDomainId(),
-                getKeyword(), getStartIndex(), getPageSizeVal(), getZoneId(), isRecursive(), listAll(), getRestartRequired(), getTags(),
-                getProjectId(), getDisplay());
+            _vpcService.listVpcs(this);
         ListResponse<VpcResponse> response = new ListResponse<VpcResponse>();
         List<VpcResponse> vpcResponses = new ArrayList<VpcResponse>();
         for (Vpc vpc : vpcs.first()) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
index 4c3408e..d4c7d0d 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
@@ -34,6 +34,8 @@
 import org.apache.cloudstack.api.response.VpcResponse;
 
 import com.cloud.event.EventTypes;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.network.vpc.Vpc;
 import com.cloud.user.Account;
 
@@ -63,6 +65,12 @@
             description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0")
     private Integer publicMtu;
 
+    @Parameter(name = ApiConstants.SOURCE_NAT_IP,
+            type = CommandType.STRING,
+            description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this VPC",
+            since = "4.19")
+    private String sourceNatIP;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -87,6 +95,10 @@
         return publicMtu;
     }
 
+    public String getSourceNatIP() {
+        return sourceNatIP;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -107,13 +119,22 @@
 
     @Override
     public void execute() {
-        Vpc result = _vpcService.updateVpc(getId(), getVpcName(), getDisplayText(), getCustomId(), isDisplayVpc(), getPublicMtu());
-        if (result != null) {
-            VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result);
-            response.setResponseName(getCommandName());
-            setResponseObject(response);
-        } else {
-            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VPC");
+        try {
+            Vpc result = _vpcService.updateVpc(this);
+            if (result != null) {
+                VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result);
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VPC");
+            }
+        } catch (final ResourceUnavailableException ex) {
+            s_logger.warn("Exception: ", ex);
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
+        } catch (final InsufficientCapacityException ex) {
+            s_logger.info(ex);
+            s_logger.trace(ex);
+            throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage());
         }
     }
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
index eecd6be..3eeaaef 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java
@@ -32,9 +32,21 @@
 public class AsyncJobResponse extends BaseResponse {
 
     @SerializedName("accountid")
-    @Param(description = "the account that executed the async command")
+    @Param(description = "the account id that executed the async command")
     private String accountId;
 
+    @SerializedName("account")
+    @Param(description = "the account that executed the async command")
+    private String account;
+
+    @SerializedName("domainid")
+    @Param(description = "the domain id that executed the async command")
+    private String domainid;
+
+    @SerializedName("domainpath")
+    @Param(description = "the domain that executed the async command")
+    private String domainPath;
+
     @SerializedName(ApiConstants.USER_ID)
     @Param(description = "the user that executed the async command")
     private String userId;
@@ -71,6 +83,10 @@
     @Param(description = "the unique ID of the instance/entity object related to the job")
     private String jobInstanceId;
 
+    @SerializedName("managementserverid")
+    @Param(description = "the msid of the management server on which the job is running", since = "4.19")
+    private Long msid;
+
     @SerializedName(ApiConstants.CREATED)
     @Param(description = "  the created date of the job")
     private Date created;
@@ -83,6 +99,18 @@
         this.accountId = accountId;
     }
 
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public void setDomainId(String domainid) {
+        this.domainid = domainid;
+    }
+
+    public void setDomainPath(String domainPath) {
+        this.domainPath = domainPath;
+    }
+
     public void setUserId(String userId) {
         this.userId = userId;
     }
@@ -127,4 +155,8 @@
     public void setRemoved(final Date removed) {
         this.removed = removed;
     }
+
+    public void setMsid(Long msid) {
+        this.msid = msid;
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
index 339d092..b1bba90 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BaseRoleResponse.java
@@ -36,6 +36,11 @@
     @Param(description = "the description of the role")
     private String roleDescription;
 
+    @SerializedName(ApiConstants.IS_PUBLIC)
+    @Param(description = "Indicates whether the role will be visible to all users (public) or only to root admins (private)." +
+            " If this parameter is not specified during the creation of the role its value will be defaulted to true (public).")
+    private boolean publicRole = true;
+
     public void setId(String id) {
         this.id = id;
     }
@@ -47,4 +52,8 @@
     public void setDescription(String description) {
         this.roleDescription = description;
     }
+
+    public void setPublicRole(boolean publicRole) {
+        this.publicRole = publicRole;
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java
index a3ca777..8f65492 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java
@@ -93,6 +93,10 @@
     @Param(description = "whether the event is parented")
     private String parentId;
 
+    @SerializedName(ApiConstants.ARCHIVED)
+    @Param(description = "whether the event has been archived or not")
+    private Boolean archived;
+
     public void setId(String id) {
         this.id = id;
     }
@@ -173,4 +177,8 @@
     public void setProjectName(String projectName) {
         this.projectName = projectName;
     }
+
+    public void setArchived(Boolean archived) {
+        this.archived = archived;
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
index c1a57c3..2e3c5bb 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
@@ -35,13 +35,25 @@
     @Param(description = "the ID of the OS category")
     private String osCategoryId;
 
+    @SerializedName(ApiConstants.OS_CATEGORY_NAME)
+    @Param(description = "the name of the OS category")
+    private String osCategoryName;
+
+    @SerializedName(ApiConstants.NAME)
+    @Param(description = "the name of the OS type")
+    private String name;
+
+    /**
+     * @deprecated description, as name is the correct interpretation and is needed for UI forms
+     */
+    @Deprecated(since = "4.19")
     @SerializedName(ApiConstants.DESCRIPTION)
     @Param(description = "the name/description of the OS type")
     private String description;
 
     @SerializedName(ApiConstants.IS_USER_DEFINED)
     @Param(description = "is the guest OS user defined")
-    private Boolean isUserDefined;
+    private String isUserDefined;
 
     public String getId() {
         return id;
@@ -59,6 +71,22 @@
         this.osCategoryId = osCategoryId;
     }
 
+    public String getOsCategoryName() {
+        return osCategoryName;
+    }
+
+    public void setOsCategoryName(String osCategoryName) {
+        this.osCategoryName = osCategoryName;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
     public String getDescription() {
         return description;
     }
@@ -67,11 +95,11 @@
         this.description = description;
     }
 
-    public Boolean getIsUserDefined() {
+    public String getIsUserDefined() {
         return isUserDefined;
     }
 
-    public void setIsUserDefined(Boolean isUserDefined) {
+    public void setIsUserDefined(String isUserDefined) {
         this.isUserDefined = isUserDefined;
     }
 
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java
new file mode 100644
index 0000000..52f55bf
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsNamesResponse.java
@@ -0,0 +1,76 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.response;
+
+import java.util.List;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class HypervisorGuestOsNamesResponse extends BaseResponse {
+    @SerializedName(ApiConstants.HYPERVISOR)
+    @Param(description = "the hypervisor")
+    private String hypervisor;
+
+    @SerializedName(ApiConstants.HYPERVISOR_VERSION)
+    @Param(description = "version of the hypervisor for guest os names")
+    private String hypervisorVersion;
+
+    @SerializedName(ApiConstants.GUEST_OS_LIST)
+    @Param(description = "the guest OS list of the hypervisor", responseObject = HypervisorGuestOsResponse.class)
+    private List<HypervisorGuestOsResponse> guestOSList;
+
+    @SerializedName(ApiConstants.GUEST_OS_COUNT)
+    @Param(description = "the guest OS count of the hypervisor")
+    private Integer guestOSCount;
+
+    public String getHypervisor() {
+        return hypervisor;
+    }
+
+    public void setHypervisor(String hypervisor) {
+        this.hypervisor = hypervisor;
+    }
+
+    public String getHypervisorVersion() {
+        return hypervisorVersion;
+    }
+
+    public void setHypervisorVersion(String hypervisorVersion) {
+        this.hypervisorVersion = hypervisorVersion;
+    }
+
+    public List<HypervisorGuestOsResponse> getGuestOSList() {
+        return guestOSList;
+    }
+
+    public void setGuestOSList(List<HypervisorGuestOsResponse> guestOSList) {
+        this.guestOSList = guestOSList;
+    }
+
+    public Integer getGuestOSCount() {
+        return guestOSCount;
+    }
+
+    public void setGuestOSCount(Integer guestOSCount) {
+        this.guestOSCount = guestOSCount;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.java
new file mode 100644
index 0000000..e9ef630
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorGuestOsResponse.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 org.apache.cloudstack.api.response;
+
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+
+public class HypervisorGuestOsResponse extends BaseResponse {
+    @SerializedName(ApiConstants.OS_DISPLAY_NAME)
+    @Param(description = "standard display name for the Guest OS")
+    private String osStdName;
+
+    @SerializedName(ApiConstants.OS_NAME_FOR_HYPERVISOR)
+    @Param(description = "hypervisor specific name for the Guest OS")
+    private String osNameForHypervisor;
+
+    public String getOsStdName() {
+        return osStdName;
+    }
+
+    public void setOsStdName(String osStdName) {
+        this.osStdName = osStdName;
+    }
+
+    public String getOsNameForHypervisor() {
+        return osNameForHypervisor;
+    }
+
+    public void setOsNameForHypervisor(String osNameForHypervisor) {
+        this.osNameForHypervisor = osNameForHypervisor;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
index 13497d8..7e3adc4 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java
@@ -96,15 +96,19 @@
     private Boolean isSystem;
 
     @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
-    @Param(description = "virtual machine id the ip address is assigned to (not null only for static nat Ip)")
+    @Param(description = "virtual machine id the ip address is assigned to")
     private String virtualMachineId;
 
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_TYPE)
+    @Param(description = "virtual machine type the ip address is assigned to", since = "4.19.0")
+    private String virtualMachineType;
+
     @SerializedName("vmipaddress")
     @Param(description = "virtual machine (dnat) ip address (not null only for static nat Ip)")
     private String virtualMachineIp;
 
     @SerializedName("virtualmachinename")
-    @Param(description = "virtual machine name the ip address is assigned to (not null only for static nat Ip)")
+    @Param(description = "virtual machine name the ip address is assigned to")
     private String virtualMachineName;
 
     @SerializedName("virtualmachinedisplayname")
@@ -159,10 +163,9 @@
     @Param(description="the name of the Network where ip belongs to")
     private String networkName;
 
-    /*
-        @SerializedName(ApiConstants.JOB_ID) @Param(description="shows the current pending asynchronous job ID. This tag is not returned if no current pending jobs are acting on the volume")
-        private IdentityProxy jobId = new IdentityProxy("async_job");
-    */
+    @SerializedName(ApiConstants.HAS_RULES)
+    @Param(description="whether the ip address has Firewall/PortForwarding/LoadBalancing rules defined")
+    private boolean hasRules;
 
     public void setIpAddress(String ipAddress) {
         this.ipAddress = ipAddress;
@@ -232,6 +235,10 @@
         this.virtualMachineId = virtualMachineId;
     }
 
+    public void setVirtualMachineType(String virtualMachineType) {
+        this.virtualMachineType = virtualMachineType;
+    }
+
     public void setVirtualMachineIp(String virtualMachineIp) {
         this.virtualMachineIp = virtualMachineIp;
     }
@@ -305,4 +312,8 @@
     public void setNetworkName(String networkName) {
         this.networkName = networkName;
     }
+
+    public void setHasRules(final boolean hasRules) {
+        this.hasRules = hasRules;
+    }
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java
new file mode 100644
index 0000000..813a48e
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+
+import java.util.Date;
+
+@EntityReference(value = VMSchedule.class)
+public class VMScheduleResponse extends BaseResponse {
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "the ID of VM schedule")
+    private String id;
+
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
+    @Param(description = "ID of virtual machine")
+    private String vmId;
+
+    @SerializedName(ApiConstants.DESCRIPTION)
+    @Param(description = "Description of VM schedule")
+    private String description;
+
+    @SerializedName(ApiConstants.SCHEDULE)
+    @Param(description = "Cron formatted VM schedule")
+    private String schedule;
+
+    @SerializedName(ApiConstants.TIMEZONE)
+    @Param(description = "Timezone of the schedule")
+    private String timeZone;
+
+    @SerializedName(ApiConstants.ACTION)
+    @Param(description = "Action")
+    private VMSchedule.Action action;
+
+    @SerializedName(ApiConstants.ENABLED)
+    @Param(description = "VM schedule is enabled")
+    private boolean enabled;
+
+    @SerializedName(ApiConstants.START_DATE)
+    @Param(description = "Date from which the schedule is active")
+    private Date startDate;
+
+    @SerializedName(ApiConstants.END_DATE)
+    @Param(description = "Date after which the schedule becomes inactive")
+    private Date endDate;
+
+    @SerializedName(ApiConstants.CREATED)
+    @Param(description = "Date when the schedule was created")
+    private Date created;
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setSchedule(String schedule) {
+        this.schedule = schedule;
+    }
+
+    public void setTimeZone(String timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public void setAction(VMSchedule.Action action) {
+        this.action = action;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public void setEndDate(Date endDate) {
+        this.endDate = endDate;
+    }
+
+    public void setCreated(Date created) {this.created = created;}
+}
diff --git a/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java b/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java
index 0159fb2..54a53f3 100644
--- a/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java
+++ b/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java
@@ -16,12 +16,13 @@
 // under the License.
 package org.apache.cloudstack.management;
 
+import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 
-public interface ManagementServerHost extends InternalIdentity, Identity {
+public interface ManagementServerHost extends InternalIdentity, Identity, ControlledEntity {
     enum State {
-        Up, Down
+        Up, Down, PreparingToShutDown, ReadyToShutDown, ShuttingDown
     }
 
     long getMsid();
diff --git a/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java b/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java
new file mode 100644
index 0000000..4dfcd0a
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.userdata;
+
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.framework.config.Configurable;
+
+import com.cloud.utils.component.Manager;
+
+public interface UserDataManager extends Manager, Configurable {
+    String concatenateUserData(String userdata1, String userdata2, String userdataProvider);
+    String validateUserData(String userData, BaseCmd.HTTPMethod httpmethod);
+}
diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.java b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.java
new file mode 100644
index 0000000..b1e7df3
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedule.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 org.apache.cloudstack.vm.schedule;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+import java.time.ZoneId;
+import java.util.Date;
+
+public interface VMSchedule extends Identity, InternalIdentity {
+    enum Action {
+        START, STOP, REBOOT, FORCE_STOP, FORCE_REBOOT
+    }
+
+    long getVmId();
+
+    String getDescription();
+
+    String getSchedule();
+
+    String getTimeZone();
+
+    Action getAction();
+
+    boolean getEnabled();
+
+    Date getStartDate();
+
+    Date getEndDate();
+
+    ZoneId getTimeZoneId();
+
+    Date getCreated();
+}
diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java
new file mode 100644
index 0000000..6aca05b
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManager.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule;
+
+import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+
+public interface VMScheduleManager {
+    VMScheduleResponse createSchedule(CreateVMScheduleCmd createVMScheduleCmd);
+
+    VMScheduleResponse createResponse(VMSchedule vmSchedule);
+
+    ListResponse<VMScheduleResponse> listSchedule(ListVMScheduleCmd listVMScheduleCmd);
+
+    VMScheduleResponse updateSchedule(UpdateVMScheduleCmd updateVMScheduleCmd);
+
+    long removeScheduleByVmId(long vmId, boolean expunge);
+
+    Long removeSchedule(DeleteVMScheduleCmd deleteVMScheduleCmd);
+}
diff --git a/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java
new file mode 100644
index 0000000..d7a18b7
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJob.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+import java.util.Date;
+
+public interface VMScheduledJob extends Identity, InternalIdentity {
+    long getVmId();
+
+    long getVmScheduleId();
+
+    Long getAsyncJobId();
+
+    void setAsyncJobId(long asyncJobId);
+
+    VMSchedule.Action getAction();
+
+    Date getScheduledTime();
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmdTest.java
new file mode 100644
index 0000000..e28720f
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmdTest.java
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.admin.offering;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class CreateDiskOfferingCmdTest {
+
+    @InjectMocks
+    private CreateDiskOfferingCmd createDiskOfferingCmd = new CreateDiskOfferingCmd();
+
+    @Test
+    public void testGetDisplayTextWhenEmpty() {
+        String netName = "net-offering";
+        ReflectionTestUtils.setField(createDiskOfferingCmd , "offeringName", netName);
+        Assert.assertEquals(createDiskOfferingCmd.getDisplayText(), netName);
+    }
+
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java
new file mode 100644
index 0000000..8b95456
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java
@@ -0,0 +1,37 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.admin.offering;
+
+import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class CreateNetworkOfferingCmdTest {
+
+    @InjectMocks
+    private CreateNetworkOfferingCmd createNetworkOfferingCmd = new CreateNetworkOfferingCmd();
+
+    @Test
+    public void createVpcNtwkOffWithEmptyDisplayText() {
+        String netName = "network";
+        ReflectionTestUtils.setField(createNetworkOfferingCmd, "networkOfferingName", netName);
+        Assert.assertEquals(createNetworkOfferingCmd.getDisplayText(), netName);
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java
new file mode 100644
index 0000000..717b5c326
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.admin.offering;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CreateServiceOfferingCmdTest {
+
+    @InjectMocks
+    private CreateServiceOfferingCmd createServiceOfferingCmd;
+
+    @Test
+    public void testGetDisplayTextWhenEmpty() {
+        String netName = "net-offering";
+        ReflectionTestUtils.setField(createServiceOfferingCmd, "serviceOfferingName", netName);
+        Assert.assertEquals(createServiceOfferingCmd.getDisplayText(), netName);
+    }
+
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java
index 7a98626..61a3c8f 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java
@@ -16,16 +16,10 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.vm;
 
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.ManagementServerException;
-import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.VirtualMachineMigrationException;
-import com.cloud.host.Host;
-import com.cloud.resource.ResourceService;
-import com.cloud.uservm.UserVm;
-import com.cloud.utils.db.UUIDManager;
-import com.cloud.vm.UserVmService;
-import com.cloud.vm.VirtualMachine;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ResponseGenerator;
 import org.apache.cloudstack.api.ResponseObject;
@@ -40,13 +34,21 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.Spy;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.springframework.test.util.ReflectionTestUtils;
 
-import java.util.List;
-import java.util.Map;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ManagementServerException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.exception.VirtualMachineMigrationException;
+import com.cloud.host.Host;
+import com.cloud.resource.ResourceService;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.db.UUIDManager;
+import com.cloud.vm.UserVmService;
+import com.cloud.vm.VirtualMachine;
 
-@RunWith(PowerMockRunner.class)
+@RunWith(MockitoJUnitRunner.class)
 public class MigrateVirtualMachineWithVolumeCmdTest {
     @Mock
     UserVmService userVmServiceMock;
@@ -68,44 +70,46 @@
 
     @Spy
     @InjectMocks
-    MigrateVirtualMachineWithVolumeCmd cmdSpy = new MigrateVirtualMachineWithVolumeCmd();
+    MigrateVirtualMachineWithVolumeCmd cmdSpy;
 
     private Long hostId = 1L;
-    private Long virtualMachineUuid = 1L;
+    private Long virtualMachineId = 1L;
     private String virtualMachineName = "VM-name";
-    private Map<String, String> migrateVolumeTo = Map.of("key","value");
+    private  Map<String, String> migrateVolumeTo = null;
     private SystemVmResponse systemVmResponse = new SystemVmResponse();
     private UserVmResponse userVmResponse = new UserVmResponse();
 
     @Before
-    public void setup() {
-        Mockito.when(cmdSpy.getVirtualMachineId()).thenReturn(virtualMachineUuid);
-        Mockito.when(cmdSpy.getHostId()).thenReturn(hostId);
-        Mockito.when(cmdSpy.getVolumeToPool()).thenReturn(migrateVolumeTo);
+    public void setUp() throws Exception {
+        ReflectionTestUtils.setField(cmdSpy, "virtualMachineId", virtualMachineId);
+        migrateVolumeTo = new HashMap<>();
+        migrateVolumeTo.put("volume", "abc");
+        migrateVolumeTo.put("pool", "xyz");
     }
 
     @Test
-    public void executeTestHostIdIsNullAndMigrateVolumeToIsNullThrowsInvalidParameterValueException(){
+    public void executeTestRequiredArgsNullThrowsInvalidParameterValueException() {
         ReflectionTestUtils.setField(cmdSpy, "hostId", null);
         ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", null);
+        ReflectionTestUtils.setField(cmdSpy, "autoSelect", null);
 
         try {
             cmdSpy.execute();
         } catch (Exception e) {
             Assert.assertEquals(InvalidParameterValueException.class, e.getClass());
-            String expected = String.format("Either %s or %s must be passed for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO);
+            String expected = String.format("Either %s or %s must be passed or %s must be true for migrating the VM.", ApiConstants.HOST_ID, ApiConstants.MIGRATE_TO, ApiConstants.AUTO_SELECT);
             Assert.assertEquals(expected , e.getMessage());
         }
     }
 
     @Test
-    public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueException(){
+    public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueException() {
         ReflectionTestUtils.setField(cmdSpy, "hostId", hostId);
         ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped);
-        Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName));
+        Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineId, virtualMachineName));
 
         try {
             cmdSpy.execute();
@@ -117,33 +121,35 @@
     }
 
     @Test
-    public void executeTestVMIsRunningAndHostIdIsNullThrowsInvalidParameterValueException(){
+    public void executeTestVMIsRunningHostIdIsNullAndAutoSelectIsFalseThrowsInvalidParameterValueException() {
         ReflectionTestUtils.setField(cmdSpy, "hostId", null);
+        ReflectionTestUtils.setField(cmdSpy, "autoSelect", false);
         ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running);
-        Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName));
+        Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineId, virtualMachineName));
 
         try {
             cmdSpy.execute();
         } catch (Exception e) {
             Assert.assertEquals(InvalidParameterValueException.class, e.getClass());
-            String expected = String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.", virtualMachineMock,
-                    ApiConstants.HOST_ID);
+            String expected = String.format("%s is not in the Stopped state to migrate, use the %s or %s parameter to migrate it to a new host.", virtualMachineMock,
+                    ApiConstants.HOST_ID, ApiConstants.AUTO_SELECT);
             Assert.assertEquals(expected , e.getMessage());
         }
     }
 
     @Test
-    public void executeTestHostIdIsNullThrowsInvalidParameterValueException(){
+    public void executeTestHostIdIsNullThrowsInvalidParameterValueException() {
+        ReflectionTestUtils.setField(cmdSpy, "virtualMachineId", virtualMachineId);
         ReflectionTestUtils.setField(cmdSpy, "hostId", hostId);
         ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
+        ReflectionTestUtils.setField(cmdSpy, "autoSelect", false);
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running);
         Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(null);
-        Mockito.when(uuidManagerMock.getUuid(Host.class, virtualMachineUuid)).thenReturn(virtualMachineUuid.toString());
 
         try {
             cmdSpy.execute();
@@ -154,15 +160,22 @@
         }
     }
 
+    private Map getMockedMigrateVolumeToApiCmdParam() {
+        Map<String, String> migrateVolumeTo = new HashMap<>();
+        migrateVolumeTo.put("volume", "abc");
+        migrateVolumeTo.put("pool", "xyz");
+        return Map.of("", migrateVolumeTo);
+    }
+
     @Test
     public void executeTestHostIsNotNullMigratedVMIsNullThrowsServerApiException() throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException {
         ReflectionTestUtils.setField(cmdSpy, "hostId", hostId);
-        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
+        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam());
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running);
-        Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(hostMock);
-        Mockito.when(userVmServiceMock.migrateVirtualMachineWithVolume(virtualMachineUuid, hostMock, migrateVolumeTo)).thenReturn(null);
+        Mockito.when(resourceServiceMock.getHost(hostId)).thenReturn(hostMock);
+        Mockito.when(userVmServiceMock.migrateVirtualMachineWithVolume(Mockito.anyLong(), Mockito.any(), Mockito.anyMap())).thenReturn(null);
 
         try {
             cmdSpy.execute();
@@ -176,11 +189,11 @@
     @Test
     public void executeTestHostIsNullMigratedVMIsNullThrowsServerApiException() {
         ReflectionTestUtils.setField(cmdSpy, "hostId", null);
-        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
+        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam());
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped);
-        Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(null);
+        Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(null);
 
         try {
             cmdSpy.execute();
@@ -194,11 +207,11 @@
     @Test
     public void executeTestSystemVMMigratedWithSuccess() {
         ReflectionTestUtils.setField(cmdSpy, "hostId", null);
-        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
+        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam());
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped);
-        Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(virtualMachineMock);
+        Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(virtualMachineMock);
         Mockito.when(virtualMachineMock.getType()).thenReturn(VirtualMachine.Type.ConsoleProxy);
         Mockito.when(responseGeneratorMock.createSystemVmResponse(virtualMachineMock)).thenReturn(systemVmResponse);
 
@@ -211,11 +224,11 @@
     public void executeTestUserVMMigratedWithSuccess() {
         UserVm userVmMock = Mockito.mock(UserVm.class);
         ReflectionTestUtils.setField(cmdSpy, "hostId", null);
-        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo);
+        ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam());
 
         Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(userVmMock);
         Mockito.when(userVmMock.getState()).thenReturn(VirtualMachine.State.Stopped);
-        Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(userVmMock);
+        Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(userVmMock);
         Mockito.when(userVmMock.getType()).thenReturn(VirtualMachine.Type.User);
         Mockito.when(responseGeneratorMock.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock)).thenReturn(List.of(userVmResponse));
 
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java
index 18a2882..16b716d 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java
@@ -25,6 +25,8 @@
 
 import org.apache.cloudstack.api.ApiCmdTestUtil;
 import org.apache.cloudstack.api.ApiConstants;
+import org.springframework.test.util.ReflectionTestUtils;
+
 
 public class CreateVPCOfferingCmdTest {
 
@@ -61,4 +63,12 @@
         Assert.assertNull(cmd.getServiceProviders());
     }
 
+    @Test
+    public void testCreateVPCOfferingWithEmptyDisplayText() {
+        CreateVPCOfferingCmd cmd = new CreateVPCOfferingCmd();
+        String netName = "net-vpc";
+        ReflectionTestUtils.setField(cmd,"vpcOfferingName", netName);
+        Assert.assertEquals(cmd.getDisplayText(), netName);
+    }
+
 }
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
index a910de7..4b9d4fd 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java
@@ -54,7 +54,7 @@
         when(role.getDescription()).thenReturn("User test");
         when(role.getName()).thenReturn("testuser");
         when(role.getRoleType()).thenReturn(RoleType.User);
-        when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription())).thenReturn(role);
+        when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription(), true)).thenReturn(role);
         createRoleCmd.execute();
         RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
         Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), role.getName());
@@ -71,7 +71,7 @@
         when(newRole.getDescription()).thenReturn("User test");
         when(newRole.getName()).thenReturn("testuser");
         when(newRole.getRoleType()).thenReturn(RoleType.User);
-        when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription())).thenReturn(newRole);
+        when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription(), true)).thenReturn(newRole);
         createRoleCmd.execute();
         RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject();
         Assert.assertEquals((String) ReflectionTestUtils.getField(response, "roleName"), newRole.getName());
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
index 8de0148..6299c1e 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java
@@ -93,7 +93,7 @@
         when(role.getDescription()).thenReturn("test user imported");
         when(role.getName()).thenReturn("Test User");
         when(role.getRoleType()).thenReturn(RoleType.User);
-        when(roleService.importRole(anyString(),any(), anyString(), any(), anyBoolean())).thenReturn(role);
+        when(roleService.importRole(anyString(), any(), anyString(), any(), anyBoolean(), anyBoolean())).thenReturn(role);
 
         importRoleCmd.execute();
         RoleResponse response = (RoleResponse) importRoleCmd.getResponseObject();
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
index d298def..84b9152 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java
@@ -58,7 +58,7 @@
         when(role.getDescription()).thenReturn("Default user");
         when(role.getName()).thenReturn("User");
         when(role.getRoleType()).thenReturn(RoleType.User);
-        when(roleService.updateRole(role,updateRoleCmd.getRoleName(),updateRoleCmd.getRoleType(),updateRoleCmd.getRoleDescription())).thenReturn(role);
+        when(roleService.updateRole(role, updateRoleCmd.getRoleName(), updateRoleCmd.getRoleType(), updateRoleCmd.getRoleDescription(), updateRoleCmd.isPublicRole())).thenReturn(role);
         when(role.getId()).thenReturn(1L);
         when(role.getDescription()).thenReturn("Description Initial");
         when(role.getName()).thenReturn("User");
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmdTest.java
new file mode 100644
index 0000000..55a41c6
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmdTest.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.user.iso;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RegisterIsoCmdTest  {
+
+    @InjectMocks
+    private RegisterIsoCmd registerIsoCmd = new RegisterIsoCmd();
+
+    @Test
+    public void testGetDisplayTextWhenEmpty() {
+        String netName = "net-iso";
+        ReflectionTestUtils.setField(registerIsoCmd, "isoName", netName);
+        Assert.assertEquals(registerIsoCmd.getDisplayText(), netName);
+    }
+
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmdTest.java
new file mode 100644
index 0000000..ee31931
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/project/CreateProjectCmdTest.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command.user.project;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CreateProjectCmdTest  {
+
+    @InjectMocks
+    private CreateProjectCmd createProjectCmd = new CreateProjectCmd();
+
+    @Test
+    public void testGetDisplayTextWhenEmpty() {
+        String netName = "net-project";
+        ReflectionTestUtils.setField(createProjectCmd , "name", netName);
+        Assert.assertEquals(createProjectCmd.getDisplayText(), netName);
+    }
+
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmdTest.java
index dfd3b6e..0c31e50 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmdTest.java
@@ -30,6 +30,8 @@
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
 import java.util.ArrayList;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -139,4 +141,12 @@
         testIsDeployAsIsBase(Hypervisor.HypervisorType.XenServer, true, false);
         testIsDeployAsIsBase(Hypervisor.HypervisorType.Any, true, false);
     }
+
+    @Test
+    public void testGetDisplayTextWhenEmpty() {
+        registerTemplateCmd = new RegisterTemplateCmd();
+        String netName = "net-template";
+        ReflectionTestUtils.setField(registerTemplateCmd , "templateName", netName);
+        Assert.assertEquals(registerTemplateCmd.getDisplayText(), netName);
+    }
 }
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java
new file mode 100644
index 0000000..2fdef2a
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmdTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.db.EntityManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+public class CreateVMScheduleCmdTest {
+    @Mock
+    public VMScheduleManager vmScheduleManager;
+    @Mock
+    public EntityManager entityManager;
+    @InjectMocks
+    private CreateVMScheduleCmd createVMScheduleCmd = new CreateVMScheduleCmd();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and CreateVMScheduleCmd"
+     * when: "CreateVMScheduleCmd is executed successfully"
+     * then: "a VMSchedule response is created"
+     */
+    @Test
+    public void testSuccessfulExecution() {
+        VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class);
+        Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenReturn(vmScheduleResponse);
+        createVMScheduleCmd.execute();
+        Assert.assertEquals(vmScheduleResponse, createVMScheduleCmd.getResponseObject());
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and CreateVMScheduleCmd"
+     * when: "CreateVMScheduleCmd is executed with an invalid parameter"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterException.class)
+    public void testInvalidParameterException() {
+        Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenThrow(InvalidParameterException.class);
+        createVMScheduleCmd.execute();
+    }
+
+    /**
+     * given: "We have an EntityManager and CreateVMScheduleCmd"
+     * when: "CreateVMScheduleCmd.getEntityOwnerId is executed for a VM which does exist"
+     * then: "owner of that VM is returned"
+     */
+    @Test
+    public void testSuccessfulGetEntityOwnerId() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(entityManager.findById(VirtualMachine.class, createVMScheduleCmd.getVmId())).thenReturn(vm);
+        long ownerId = createVMScheduleCmd.getEntityOwnerId();
+        Assert.assertEquals(vm.getAccountId(), ownerId);
+    }
+
+    /**
+     * given: "We have an EntityManager and CreateVMScheduleCmd"
+     * when: "CreateVMScheduleCmd.getEntityOwnerId is executed for a VM which doesn't exist"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterValueException.class)
+    public void testFailureGetEntityOwnerId() {
+        Mockito.when(entityManager.findById(VirtualMachine.class, createVMScheduleCmd.getVmId())).thenReturn(null);
+        long ownerId = createVMScheduleCmd.getEntityOwnerId();
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java
new file mode 100644
index 0000000..6adfc2b
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeleteVMScheduleCmdTest.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.db.EntityManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+public class DeleteVMScheduleCmdTest {
+    @Mock
+    public VMScheduleManager vmScheduleManager;
+    @Mock
+    public EntityManager entityManager;
+
+    @InjectMocks
+    private DeleteVMScheduleCmd deleteVMScheduleCmd = new DeleteVMScheduleCmd();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and DeleteVMScheduleCmd"
+     * when: "VMScheduleManager.removeSchedule() is executed returning 1 row"
+     * then: "a Success response is created"
+     */
+    @Test
+    public void testSuccessfulExecution() {
+        final SuccessResponse response = new SuccessResponse();
+        response.setResponseName(deleteVMScheduleCmd.getCommandName());
+        response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
+
+        Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(1L);
+        deleteVMScheduleCmd.execute();
+        SuccessResponse actualResponse = (SuccessResponse) deleteVMScheduleCmd.getResponseObject();
+        Assert.assertEquals(response.getResponseName(), actualResponse.getResponseName());
+        Assert.assertEquals(response.getObjectName(), actualResponse.getObjectName());
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and DeleteVMScheduleCmd"
+     * when: "VMScheduleManager.removeSchedule() is executed returning 0 row"
+     * then: "ServerApiException is thrown"
+     */
+    @Test(expected = ServerApiException.class)
+    public void testServerApiException() {
+        Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(0L);
+        deleteVMScheduleCmd.execute();
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and DeleteVMScheduleCmd"
+     * when: "DeleteVMScheduleCmd is executed with an invalid parameter"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterException.class)
+    public void testInvalidParameterException() {
+        Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenThrow(InvalidParameterException.class);
+        deleteVMScheduleCmd.execute();
+    }
+
+    /**
+     * given: "We have an EntityManager and DeleteVMScheduleCmd"
+     * when: "DeleteVMScheduleCmd.getEntityOwnerId is executed for a VM which does exist"
+     * then: "owner of that VM is returned"
+     */
+    @Test
+    public void testSuccessfulGetEntityOwnerId() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(entityManager.findById(VirtualMachine.class, deleteVMScheduleCmd.getVmId())).thenReturn(vm);
+        long ownerId = deleteVMScheduleCmd.getEntityOwnerId();
+        Assert.assertEquals(vm.getAccountId(), ownerId);
+    }
+
+    /**
+     * given: "We have an EntityManager and DeleteVMScheduleCmd"
+     * when: "DeleteVMScheduleCmd.getEntityOwnerId is executed for a VM which doesn't exist"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterValueException.class)
+    public void testFailureGetEntityOwnerId() {
+        Mockito.when(entityManager.findById(VirtualMachine.class, deleteVMScheduleCmd.getVmId())).thenReturn(null);
+        long ownerId = deleteVMScheduleCmd.getEntityOwnerId();
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java
new file mode 100644
index 0000000..18657b4
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class ListVMScheduleCmdTest {
+    @Mock
+    public VMScheduleManager vmScheduleManager;
+    @InjectMocks
+    private ListVMScheduleCmd listVMScheduleCmd = new ListVMScheduleCmd();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * given: "We have a VMScheduleManager with 0 schedules and ListVMScheduleCmd"
+     * when: "ListVMScheduleCmd is executed"
+     * then: "a list of size 0 is returned"
+     */
+    @Test
+    public void testEmptyResponse() {
+        ListResponse<VMScheduleResponse> response = new ListResponse<VMScheduleResponse>();
+        response.setResponses(new ArrayList<VMScheduleResponse>());
+        Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(response);
+        listVMScheduleCmd.execute();
+        ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
+        Assert.assertEquals(response, actualResponseObject);
+        Assert.assertEquals(0L, actualResponseObject.getResponses().size());
+    }
+
+    /**
+     * given: "We have a VMScheduleManager with 1 schedule and ListVMScheduleCmd"
+     * when: "ListVMScheduleCmd is executed"
+     * then: "a list of size 1 is returned"
+     */
+    @Test
+    public void testNonEmptyResponse() {
+        ListResponse<VMScheduleResponse> listResponse = new ListResponse<VMScheduleResponse>();
+        VMScheduleResponse response = Mockito.mock(VMScheduleResponse.class);
+        listResponse.setResponses(Collections.singletonList(response));
+        Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(listResponse);
+        listVMScheduleCmd.execute();
+        ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
+        Assert.assertEquals(listResponse, actualResponseObject);
+        Assert.assertEquals(1L, actualResponseObject.getResponses().size());
+        Assert.assertEquals(response, actualResponseObject.getResponses().get(0));
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and ListVMScheduleCmd"
+     * when: "ListVMScheduleCmd is executed with an invalid parameter"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterException.class)
+    public void testInvalidParameterException() {
+        Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenThrow(InvalidParameterException.class);
+        listVMScheduleCmd.execute();
+        ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java
new file mode 100644
index 0000000..044685b
--- /dev/null
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.db.EntityManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+public class UpdateVMScheduleCmdTest {
+    @Mock
+    public VMScheduleManager vmScheduleManager;
+    @Mock
+    public EntityManager entityManager;
+    @InjectMocks
+    private UpdateVMScheduleCmd updateVMScheduleCmd = new UpdateVMScheduleCmd();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and UpdateVMScheduleCmd"
+     * when: "UpdateVMScheduleCmd is executed successfully"
+     * then: "a VMSchedule response is created"
+     */
+    @Test
+    public void testSuccessfulExecution() {
+        VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class);
+        Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenReturn(vmScheduleResponse);
+        updateVMScheduleCmd.execute();
+        Assert.assertEquals(vmScheduleResponse, updateVMScheduleCmd.getResponseObject());
+    }
+
+    /**
+     * given: "We have a VMScheduleManager and UpdateVMScheduleCmd"
+     * when: "UpdateVMScheduleCmd is executed with an invalid parameter"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterException.class)
+    public void testInvalidParameterException() {
+        Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenThrow(InvalidParameterException.class);
+        updateVMScheduleCmd.execute();
+    }
+
+    /**
+     * given: "We have an EntityManager and UpdateVMScheduleCmd"
+     * when: "UpdateVMScheduleCmd.getEntityOwnerId is executed for a VM which does exist"
+     * then: "owner of that VM is returned"
+     */
+    @Test
+    public void testSuccessfulGetEntityOwnerId() {
+        VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+
+        Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(vmSchedule);
+        Mockito.when(entityManager.findById(VirtualMachine.class, vmSchedule.getVmId())).thenReturn(vm);
+
+        long ownerId = updateVMScheduleCmd.getEntityOwnerId();
+        Assert.assertEquals(vm.getAccountId(), ownerId);
+    }
+
+    /**
+     * given: "We have an EntityManager and UpdateVMScheduleCmd"
+     * when: "UpdateVMScheduleCmd.getEntityOwnerId is executed for a VM Schedule which doesn't exist"
+     * then: "an InvalidParameterException is thrown"
+     */
+    @Test(expected = InvalidParameterValueException.class)
+    public void testFailureGetEntityOwnerId() {
+        VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
+        Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(null);
+        long ownerId = updateVMScheduleCmd.getEntityOwnerId();
+    }
+}
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java
index 92ac005..17c8e23 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java
@@ -133,6 +133,12 @@
         Assert.assertTrue(cmd.getDisplayVpc());
     }
 
+    public void testGetDisplayTextWhenEmpty() {
+        String netName = "net-vpc";
+        ReflectionTestUtils.setField(cmd, "vpcName", netName);
+        Assert.assertEquals(cmd.getDisplayText(), netName);
+    }
+
     public void testCreate() throws ResourceAllocationException {
         Vpc vpc = Mockito.mock(Vpc.class);
         ReflectionTestUtils.setField(cmd, "zoneId", 1L);
diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
index 3f27f4c..d174d36 100644
--- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
+++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.cloudstack.api.command.user.vpc;
 
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.ResourceUnavailableException;
 import com.cloud.network.vpc.Vpc;
 import com.cloud.network.vpc.VpcService;
 import junit.framework.TestCase;
@@ -72,7 +74,7 @@
         Assert.assertEquals(cmd.getPublicMtu(), publicMtu);
     }
 
-    public void testExecute() {
+    public void testExecute() throws ResourceUnavailableException, InsufficientCapacityException {
         ReflectionTestUtils.setField(cmd, "id", 1L);
         ReflectionTestUtils.setField(cmd, "vpcName", "updatedVpcName");
         ReflectionTestUtils.setField(cmd, "displayText", "Updated VPC Name");
@@ -85,10 +87,10 @@
         responseGenerator = Mockito.mock(ResponseGenerator.class);
         cmd._responseGenerator = responseGenerator;
         Mockito.when(_vpcService.updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(),
-                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt())).thenReturn(vpc);
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString())).thenReturn(vpc);
         Mockito.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Full, vpc)).thenReturn(response);
         Mockito.verify(_vpcService, Mockito.times(0)).updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(),
-                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
+                Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString());
 
     }
 }
diff --git a/client/pom.xml b/client/pom.xml
index f7e2de6..cbe45d8 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <repositories>
         <repository>
@@ -354,6 +354,16 @@
         </dependency>
         <dependency>
             <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-engine-userdata-cloud-init</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-engine-userdata</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
             <artifactId>cloud-mom-rabbitmq</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -552,6 +562,11 @@
             <artifactId>cloud-plugin-integrations-kubernetes-service</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+           <groupId>org.apache.cloudstack</groupId>
+           <artifactId>cloud-plugin-shutdown</artifactId>
+           <version>${project.version}</version>
+       </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
index 08f8566..63cdc45 100644
--- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
+++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
@@ -45,6 +45,7 @@
 import org.eclipse.jetty.server.handler.RequestLogHandler;
 import org.eclipse.jetty.server.handler.gzip.GzipHandler;
 import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.ssl.KeyStoreScanner;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@@ -241,6 +242,14 @@
             sslConnector.setPort(httpsPort);
             sslConnector.setHost(bindInterface);
             server.addConnector(sslConnector);
+
+            // add scanner to auto-reload certs
+            try {
+                KeyStoreScanner scanner = new KeyStoreScanner(sslContextFactory);
+                server.addBean(scanner);
+            } catch (Exception ex) {
+                LOG.error("failed to set up keystore scanner, manual refresh of certificates will be required", ex);
+            }
         }
     }
 
diff --git a/core/pom.xml b/core/pom.xml
index 1970a40..339439a 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingAnswer.java
new file mode 100644
index 0000000..53cfded
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingAnswer.java
@@ -0,0 +1,38 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+public class CheckGuestOsMappingAnswer extends Answer {
+
+    protected CheckGuestOsMappingAnswer() {
+    }
+
+    public CheckGuestOsMappingAnswer(CheckGuestOsMappingCommand cmd) {
+        super(cmd, true, null);
+    }
+
+    public CheckGuestOsMappingAnswer(CheckGuestOsMappingCommand cmd, String details) {
+        super(cmd, false, details);
+    }
+
+    public CheckGuestOsMappingAnswer(CheckGuestOsMappingCommand cmd, Throwable th) {
+        super(cmd, false, th.getMessage());
+    }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingCommand.java b/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingCommand.java
new file mode 100644
index 0000000..11efd80
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/CheckGuestOsMappingCommand.java
@@ -0,0 +1,65 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import java.util.Objects;
+
+public class CheckGuestOsMappingCommand extends Command {
+    String guestOsName;
+    String guestOsHypervisorMappingName;
+    String hypervisorVersion;
+
+    public CheckGuestOsMappingCommand() {
+        super();
+    }
+
+    public CheckGuestOsMappingCommand(String guestOsName, String guestOsHypervisorMappingName, String hypervisorVersion) {
+        super();
+        this.guestOsName = guestOsName;
+        this.guestOsHypervisorMappingName = guestOsHypervisorMappingName;
+        this.hypervisorVersion = hypervisorVersion;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+
+    public String getGuestOsName() {
+        return guestOsName;
+    }
+
+    public String getGuestOsHypervisorMappingName() {
+        return guestOsHypervisorMappingName;
+    }
+
+    public String getHypervisorVersion() {
+        return hypervisorVersion;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        CheckGuestOsMappingCommand that = (CheckGuestOsMappingCommand) o;
+        return Objects.equals(guestOsName, that.guestOsName) && Objects.equals(guestOsHypervisorMappingName, that.guestOsHypervisorMappingName) && Objects.equals(hypervisorVersion, that.hypervisorVersion);
+    }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswer.java b/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswer.java
new file mode 100644
index 0000000..d59d66c
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswer.java
@@ -0,0 +1,58 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import java.util.List;
+import java.util.Objects;
+
+import com.cloud.utils.Pair;
+
+public class GetHypervisorGuestOsNamesAnswer extends Answer {
+    private List<Pair<String, String>> hypervisorGuestOsNames;
+
+    protected GetHypervisorGuestOsNamesAnswer() {
+    }
+
+    public GetHypervisorGuestOsNamesAnswer(GetHypervisorGuestOsNamesCommand cmd, List<Pair<String, String>> hypervisorGuestOsNames) {
+        super(cmd, true, null);
+        this.hypervisorGuestOsNames = hypervisorGuestOsNames;
+    }
+
+    public GetHypervisorGuestOsNamesAnswer(GetHypervisorGuestOsNamesCommand cmd, String details) {
+        super(cmd, false, details);
+    }
+
+    public GetHypervisorGuestOsNamesAnswer(GetHypervisorGuestOsNamesCommand cmd, Throwable th) {
+        super(cmd, false, th.getMessage());
+    }
+
+    public List<Pair<String, String>> getHypervisorGuestOsNames() {
+        return hypervisorGuestOsNames;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        GetHypervisorGuestOsNamesAnswer that = (GetHypervisorGuestOsNamesAnswer) o;
+        return Objects.equals(hypervisorGuestOsNames, that.hypervisorGuestOsNames);
+    }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommand.java b/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommand.java
new file mode 100644
index 0000000..b31ff0e
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommand.java
@@ -0,0 +1,53 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import java.util.Objects;
+
+public class GetHypervisorGuestOsNamesCommand extends Command {
+    String keyword;
+
+    public GetHypervisorGuestOsNamesCommand() {
+        super();
+    }
+
+    public GetHypervisorGuestOsNamesCommand(String keyword) {
+        super();
+        this.keyword = keyword;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+        GetHypervisorGuestOsNamesCommand that = (GetHypervisorGuestOsNamesCommand) o;
+        return Objects.equals(keyword, that.keyword);
+    }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/PingRoutingCommand.java b/core/src/main/java/com/cloud/agent/api/PingRoutingCommand.java
index d7733ee..ce529ad 100644
--- a/core/src/main/java/com/cloud/agent/api/PingRoutingCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/PingRoutingCommand.java
@@ -29,6 +29,7 @@
 
     boolean _gatewayAccessible = true;
     boolean _vnetAccessible = true;
+    private Boolean hostHealthCheckResult;
 
     protected PingRoutingCommand() {
     }
@@ -57,4 +58,12 @@
     public void setVnetAccessible(boolean vnetAccessible) {
         _vnetAccessible = vnetAccessible;
     }
+
+    public Boolean getHostHealthCheckResult() {
+        return hostHealthCheckResult;
+    }
+
+    public void setHostHealthCheckResult(Boolean hostHealthCheckResult) {
+        this.hostHealthCheckResult = hostHealthCheckResult;
+    }
 }
diff --git a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
index b459f88..b4f9d20 100644
--- a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
@@ -44,6 +44,7 @@
     List<String> hostTags = new ArrayList<String>();
     String hypervisorVersion;
     HashMap<String, HashMap<String, VgpuTypesInfo>> groupDetails = new HashMap<String, HashMap<String, VgpuTypesInfo>>();
+    private Boolean hostHealthCheckResult;
 
     public StartupRoutingCommand() {
         super(Host.Type.Routing);
@@ -188,4 +189,12 @@
     public void setSupportsClonedVolumes(boolean supportsClonedVolumes) {
         this.supportsClonedVolumes = supportsClonedVolumes;
     }
+
+    public Boolean getHostHealthCheckResult() {
+        return hostHealthCheckResult;
+    }
+
+    public void setHostHealthCheckResult(Boolean hostHealthCheckResult) {
+        this.hostHealthCheckResult = hostHealthCheckResult;
+    }
 }
diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-lifecycle-core-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-lifecycle-core-context-inheritable.xml
index b754d6b..3e57a01 100644
--- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-lifecycle-core-context-inheritable.xml
+++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-lifecycle-core-context-inheritable.xml
@@ -39,5 +39,10 @@
         <property name="typeClass"
             value="com.cloud.utils.component.PluggableService" />
     </bean>
-    
+
+    <bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
+        <property name="registry" ref="userDataProvidersRegistry" />
+        <property name="typeClass" value="org.apache.cloudstack.userdata.UserDataProvider" />
+    </bean>
+
 </beans>
\ No newline at end of file
diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
index cb55913..a7f384c 100644
--- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
+++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
@@ -342,4 +342,8 @@
     <bean id="kubernetesClusterHelperRegistry"
           class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
     </bean>
+
+    <bean id="userDataProvidersRegistry"
+          class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
+    </bean>
 </beans>
diff --git a/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswerTest.java b/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswerTest.java
new file mode 100644
index 0000000..36d2fb1
--- /dev/null
+++ b/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesAnswerTest.java
@@ -0,0 +1,66 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import com.cloud.utils.Pair;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GetHypervisorGuestOsNamesAnswerTest {
+
+    @Test
+    public void testGuestOsMappingAnswerSuccess() {
+        List<Pair<String, String>> hypervisorGuestOsNames = new ArrayList<>();
+        hypervisorGuestOsNames.add(new Pair<>("centos", "centos"));
+        GetHypervisorGuestOsNamesCommand cmd = new GetHypervisorGuestOsNamesCommand();
+        GetHypervisorGuestOsNamesAnswer answer = new GetHypervisorGuestOsNamesAnswer(cmd, hypervisorGuestOsNames);
+        List<Pair<String, String>> resultOsNames = answer.getHypervisorGuestOsNames();
+
+        Assert.assertEquals("centos", resultOsNames.get(0).first());
+        assertTrue(answer.getResult());
+    }
+
+    @Test
+    public void testGuestOsMappingAnswerFailure1() {
+        Throwable th = Mockito.mock(Throwable.class);
+        Mockito.when(th.getMessage()).thenReturn("failure");
+        GetHypervisorGuestOsNamesCommand cmd = new GetHypervisorGuestOsNamesCommand();
+        GetHypervisorGuestOsNamesAnswer answer = new GetHypervisorGuestOsNamesAnswer(cmd, th);
+
+        Assert.assertEquals("failure", answer.getDetails());
+        assertFalse(answer.getResult());
+    }
+
+    @Test
+    public void testGuestOsMappingAnswerFailure2() {
+        GetHypervisorGuestOsNamesCommand cmd = new GetHypervisorGuestOsNamesCommand();
+        GetHypervisorGuestOsNamesAnswer answer = new GetHypervisorGuestOsNamesAnswer(cmd, "failure");
+
+        Assert.assertEquals("failure", answer.getDetails());
+        assertFalse(answer.getResult());
+    }
+}
diff --git a/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommandTest.java b/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommandTest.java
new file mode 100644
index 0000000..5d7cb88
--- /dev/null
+++ b/core/src/test/java/com/cloud/agent/api/GetHypervisorGuestOsNamesCommandTest.java
@@ -0,0 +1,40 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+public class GetHypervisorGuestOsNamesCommandTest {
+
+    @Test
+    public void testExecuteInSequence() {
+        GetHypervisorGuestOsNamesCommand cmd =  new GetHypervisorGuestOsNamesCommand();
+        assertFalse(cmd.executeInSequence());
+    }
+
+    @Test
+    public void testKeyword() {
+        GetHypervisorGuestOsNamesCommand cmd =  new GetHypervisorGuestOsNamesCommand("centos");
+        assertEquals("centos", cmd.getKeyword());
+    }
+}
diff --git a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingAnswerTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingAnswerTest.java
new file mode 100644
index 0000000..1ade5e0
--- /dev/null
+++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingAnswerTest.java
@@ -0,0 +1,66 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.api.agent.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.cloud.agent.api.CheckGuestOsMappingAnswer;
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.mockito.Mockito;
+
+public class CheckGuestOsMappingAnswerTest {
+    CheckGuestOsMappingCommand cmd = new CheckGuestOsMappingCommand("CentOS 7.2", "centos64Guest", "6.0");
+    CheckGuestOsMappingAnswer answer = new CheckGuestOsMappingAnswer(cmd);
+
+    @Test
+    public void testGetResult() {
+        boolean b = answer.getResult();
+        assertTrue(b);
+    }
+
+    @Test
+    public void testExecuteInSequence() {
+        boolean b = answer.executeInSequence();
+        assertFalse(b);
+    }
+
+    @Test
+    public void testGuestOsMappingAnswerDetails() {
+        CheckGuestOsMappingCommand cmd = new CheckGuestOsMappingCommand("CentOS 7.2", "centos64Guest", "6.0");
+        CheckGuestOsMappingAnswer answer = new CheckGuestOsMappingAnswer(cmd, "details");
+        String details = answer.getDetails();
+        Assert.assertEquals("details", details);
+    }
+
+    @Test
+    public void testGuestOsMappingAnswerFailure() {
+        Throwable th = Mockito.mock(Throwable.class);
+        Mockito.when(th.getMessage()).thenReturn("Failure");
+        CheckGuestOsMappingCommand cmd = new CheckGuestOsMappingCommand("CentOS 7.2", "centos64Guest", "6.0");
+        CheckGuestOsMappingAnswer answer = new CheckGuestOsMappingAnswer(cmd, th);
+        assertFalse(answer.getResult());
+        Assert.assertEquals("Failure", answer.getDetails());
+
+    }
+}
diff --git a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java
new file mode 100644
index 0000000..3f68422
--- /dev/null
+++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java
@@ -0,0 +1,46 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package org.apache.cloudstack.api.agent.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
+import org.junit.Test;
+
+import com.cloud.agent.api.AgentControlCommand;
+
+public class CheckGuestOsMappingCommandTest {
+
+    @Test
+    public void testExecuteInSequence() {
+        CheckGuestOsMappingCommand cmd = new CheckGuestOsMappingCommand();
+        boolean b = cmd.executeInSequence();
+        assertFalse(b);
+    }
+
+    @Test
+    public void testCommandParams() {
+        CheckGuestOsMappingCommand cmd = new CheckGuestOsMappingCommand("CentOS 7.2", "centos64Guest", "6.0");
+        assertEquals("CentOS 7.2", cmd.getGuestOsName());
+        assertEquals("centos64Guest", cmd.getGuestOsHypervisorMappingName());
+        assertEquals("6.0", cmd.getHypervisorVersion());
+    }
+}
diff --git a/debian/changelog b/debian/changelog
index f83c9b1..0b67488 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+cloudstack (4.19.0.0-SNAPSHOT) unstable; urgency=low
+
+  * Update the version to 4.19.0.0-SNAPSHOT
+
+ -- the Apache CloudStack project <dev@cloudstack.apache.org>  Wed, 21 Jun 2023 12:31:00 +0200
+
 cloudstack (4.18.1.0-SNAPSHOT) unstable; urgency=low
 
   * Update the version to 4.18.1.0-SNAPSHOT
diff --git a/developer/pom.xml b/developer/pom.xml
index dda007c..44f9e56 100644
--- a/developer/pom.xml
+++ b/developer/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/engine/api/pom.xml b/engine/api/pom.xml
index 5de69f1..fb3ed5d 100644
--- a/engine/api/pom.xml
+++ b/engine/api/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/components-api/pom.xml b/engine/components-api/pom.xml
index 63f92fe..93be1b5 100644
--- a/engine/components-api/pom.xml
+++ b/engine/components-api/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
index 818e0a7..6ba0c3b 100644
--- a/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
+++ b/engine/components-api/src/main/java/com/cloud/agent/AgentManager.java
@@ -39,6 +39,13 @@
 public interface AgentManager {
     static final ConfigKey<Integer> Wait = new ConfigKey<Integer>("Advanced", Integer.class, "wait", "1800", "Time in seconds to wait for control commands to return",
             true);
+    ConfigKey<Boolean> EnableKVMAutoEnableDisable = new ConfigKey<>(Boolean.class,
+                    "enable.kvm.host.auto.enable.disable",
+                    "Advanced",
+                    "false",
+                    "(KVM only) Enable Auto Disable/Enable KVM hosts in the cluster " +
+                            "according to the hosts health check results",
+                    true, ConfigKey.Scope.Cluster, null);
 
     public enum TapAgentsAction {
         Add, Del, Contains,
diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
index c5caa31..5343fb6 100644
--- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
+++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java
@@ -20,6 +20,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO;
 
 import com.cloud.dc.ClusterVO;
@@ -59,6 +60,10 @@
     public static final String MESSAGE_CREATE_VLAN_IP_RANGE_EVENT = "Message.CreateVlanIpRange.Event";
     public static final String MESSAGE_DELETE_VLAN_IP_RANGE_EVENT = "Message.DeleteVlanIpRange.Event";
 
+    static final String VM_USERDATA_MAX_LENGTH_STRING = "vm.userdata.max.length";
+    static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
+            "Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
+
     /**
      * @param offering
      * @return
diff --git a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java
index 2fa66d7..80d9ab3 100644
--- a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java
+++ b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java
@@ -233,4 +233,6 @@
 
     public static final String MESSAGE_ASSIGN_IPADDR_EVENT = "Message.AssignIpAddr.Event";
     public static final String MESSAGE_RELEASE_IPADDR_EVENT = "Message.ReleaseIpAddr.Event";
+
+    void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception;
 }
diff --git a/engine/network/pom.xml b/engine/network/pom.xml
index d0dc4ca..f0a3cd9 100644
--- a/engine/network/pom.xml
+++ b/engine/network/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml
index 2497de6..e5eeb88 100755
--- a/engine/orchestration/pom.xml
+++ b/engine/orchestration/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -68,6 +68,11 @@
             <artifactId>cloud-server</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-plugin-shutdown</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
index b74c11c..abdee76 100644
--- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
@@ -51,6 +51,7 @@
 import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 import org.apache.log4j.MDC;
 
@@ -1250,6 +1251,52 @@
             super(type, link, data);
         }
 
+        private void processHostHealthCheckResult(Boolean hostHealthCheckResult, long hostId) {
+            if (hostHealthCheckResult == null) {
+                return;
+            }
+            HostVO host = _hostDao.findById(hostId);
+            if (host == null) {
+                s_logger.error(String.format("Unable to find host with ID: %s", hostId));
+                return;
+            }
+            if (!BooleanUtils.toBoolean(EnableKVMAutoEnableDisable.valueIn(host.getClusterId()))) {
+                s_logger.debug(String.format("%s is disabled for the cluster %s, cannot process the health check result " +
+                        "received for the host %s", EnableKVMAutoEnableDisable.key(), host.getClusterId(), host.getName()));
+                return;
+            }
+
+            ResourceState.Event resourceEvent = hostHealthCheckResult ? ResourceState.Event.Enable : ResourceState.Event.Disable;
+
+            try {
+                s_logger.info(String.format("Host health check %s, auto %s KVM host: %s",
+                        hostHealthCheckResult ? "succeeds" : "fails",
+                        hostHealthCheckResult ? "enabling" : "disabling",
+                        host.getName()));
+                _resourceMgr.autoUpdateHostAllocationState(hostId, resourceEvent);
+            } catch (NoTransitionException e) {
+                s_logger.error(String.format("Cannot Auto %s host: %s", resourceEvent, host.getName()), e);
+            }
+        }
+
+        private void processStartupRoutingCommand(StartupRoutingCommand startup, long hostId) {
+            if (startup == null) {
+                s_logger.error("Empty StartupRoutingCommand received");
+                return;
+            }
+            Boolean hostHealthCheckResult = startup.getHostHealthCheckResult();
+            processHostHealthCheckResult(hostHealthCheckResult, hostId);
+        }
+
+        private void processPingRoutingCommand(PingRoutingCommand pingRoutingCommand, long hostId) {
+            if (pingRoutingCommand == null) {
+                s_logger.error("Empty PingRoutingCommand received");
+                return;
+            }
+            Boolean hostHealthCheckResult = pingRoutingCommand.getHostHealthCheckResult();
+            processHostHealthCheckResult(hostHealthCheckResult, hostId);
+        }
+
         protected void processRequest(final Link link, final Request request) {
             final AgentAttache attache = (AgentAttache)link.attachment();
             final Command[] cmds = request.getCommands();
@@ -1291,6 +1338,7 @@
                 try {
                     if (cmd instanceof StartupRoutingCommand) {
                         final StartupRoutingCommand startup = (StartupRoutingCommand) cmd;
+                        processStartupRoutingCommand(startup, hostId);
                         answer = new StartupAnswer(startup, attache.getId(), mgmtServiceConf.getPingInterval());
                     } else if (cmd instanceof StartupProxyCommand) {
                         final StartupProxyCommand startup = (StartupProxyCommand) cmd;
@@ -1322,6 +1370,7 @@
                             // if the router is sending a ping, verify the
                             // gateway was pingable
                             if (cmd instanceof PingRoutingCommand) {
+                                processPingRoutingCommand((PingRoutingCommand) cmd, hostId);
                                 final boolean gatewayAccessible = ((PingRoutingCommand)cmd).isGatewayAccessible();
                                 final HostVO host = _hostDao.findById(Long.valueOf(cmdHostId));
 
@@ -1748,8 +1797,8 @@
 
     @Override
     public ConfigKey<?>[] getConfigKeys() {
-        return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize,
-            DirectAgentThreadCap };
+        return new ConfigKey<?>[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize,
+                DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable };
     }
 
     protected class SetHostParamsListener implements Listener {
diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java
index 749a738..bd4e259 100644
--- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java
@@ -50,6 +50,11 @@
 import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
 import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao;
+import org.apache.cloudstack.shutdown.ShutdownManager;
+import org.apache.cloudstack.shutdown.command.CancelShutdownManagementServerHostCommand;
+import org.apache.cloudstack.shutdown.command.PrepareForShutdownManagementServerHostCommand;
+import org.apache.cloudstack.shutdown.command.BaseShutdownManagementServerHostCommand;
+import org.apache.cloudstack.shutdown.command.TriggerShutdownManagementServerHostCommand;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
 import org.apache.cloudstack.utils.security.SSLUtils;
 import org.apache.log4j.Logger;
@@ -129,6 +134,8 @@
     private HAConfigDao haConfigDao;
     @Inject
     private CAManager caService;
+    @Inject
+    private ShutdownManager shutdownManager;
 
     protected ClusteredAgentManagerImpl() {
         super();
@@ -1341,8 +1348,10 @@
                 return _gson.toJson(answers);
             } else if (cmds.length == 1 && cmds[0] instanceof ScheduleHostScanTaskCommand) {
                 final ScheduleHostScanTaskCommand cmd = (ScheduleHostScanTaskCommand)cmds[0];
-                final String response = handleScheduleHostScanTaskCommand(cmd);
-                return response;
+                return handleScheduleHostScanTaskCommand(cmd);
+            } else if (cmds.length == 1 && cmds[0] instanceof BaseShutdownManagementServerHostCommand) {
+                final BaseShutdownManagementServerHostCommand cmd = (BaseShutdownManagementServerHostCommand)cmds[0];
+                return handleShutdownManagementServerHostCommand(cmd);
             }
 
             try {
@@ -1376,6 +1385,36 @@
             return null;
         }
 
+        private String handleShutdownManagementServerHostCommand(BaseShutdownManagementServerHostCommand cmd) {
+            if (cmd instanceof PrepareForShutdownManagementServerHostCommand) {
+                s_logger.debug("Received BaseShutdownManagementServerHostCommand - preparing to shut down");
+                try {
+                    shutdownManager.prepareForShutdown();
+                    return "Successfully prepared for shutdown";
+                } catch(CloudRuntimeException e) {
+                    return e.getMessage();
+                }
+            }
+            if (cmd instanceof TriggerShutdownManagementServerHostCommand) {
+                s_logger.debug("Received TriggerShutdownManagementServerHostCommand - triggering a shut down");
+                try {
+                    shutdownManager.triggerShutdown();
+                    return "Successfully triggered shutdown";
+                } catch(CloudRuntimeException e) {
+                    return e.getMessage();
+                }
+            }
+            if (cmd instanceof CancelShutdownManagementServerHostCommand) {
+                s_logger.debug("Received CancelShutdownManagementServerHostCommand - cancelling shut down");
+                try {
+                    shutdownManager.cancelShutdown();
+                    return "Successfully prepared for shutdown";
+                } catch(CloudRuntimeException e) {
+                    return e.getMessage();
+                }
+            }
+            throw new CloudRuntimeException("Unknown BaseShutdownManagementServerHostCommand command received : " + cmd);
+        }
     }
 
     public boolean executeAgentUserRequest(final long agentId, final Event event) throws AgentUnavailableException {
diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 2f3e128..f821200 100644
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -949,7 +949,7 @@
 
             /**
              * private transaction method to run over the objects and determine nic requirements
-             * @return the total numer of nics required
+             * @return the total number of nics required
              */
             private int determineNumberOfNicsRequired() {
                 int size = 0;
@@ -1183,7 +1183,7 @@
     }
 
     /**
-     * Validates the locked IP, throwing an exeption if the locked IP is null or the locked IP is not in 'Free' state.
+     * Validates the locked IP, throwing an exception if the locked IP is null or the locked IP is not in 'Free' state.
      */
     protected void validateLockedRequestedIp(IPAddressVO ipVO, IPAddressVO lockedIpVO) {
         if (lockedIpVO == null) {
@@ -1605,7 +1605,7 @@
                 }
 
                 if (s_logger.isDebugEnabled()) {
-                    s_logger.debug("Asking " + element.getName() + " to implemenet " + network);
+                    s_logger.debug("Asking " + element.getName() + " to implement " + network);
                 }
 
                 if (!element.implement(network, offering, dest, context)) {
@@ -2498,7 +2498,7 @@
 
         //remove the secondary ip addresses corresponding to to this nic
         if (!removeVmSecondaryIpsOfNic(nic.getId())) {
-            s_logger.debug("Removing nic " + nic.getId() + " secondary ip addreses failed");
+            s_logger.debug("Removing nic " + nic.getId() + " secondary ip addresses failed");
         }
     }
 
diff --git a/engine/pom.xml b/engine/pom.xml
index 0bed0e9..91f430b 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <build>
@@ -58,6 +58,8 @@
         <module>storage/image</module>
         <module>storage/snapshot</module>
         <module>storage/volume</module>
+        <module>userdata/cloud-init</module>
+        <module>userdata</module>
     </modules>
     <profiles>
         <profile>
diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml
index da40a52..21066ff 100644
--- a/engine/schema/pom.xml
+++ b/engine/schema/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java
index 6dfe75c..ca51ad3 100644
--- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java
+++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java
@@ -143,6 +143,8 @@
 
     HostVO findByName(String name);
 
+    HostVO findHostByHypervisorTypeAndVersion(HypervisorType hypervisorType, String hypervisorVersion);
+
     List<HostVO> listHostsWithActiveVMs(long offeringId);
 
     /**
diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
index 15c87d6..392b623 100644
--- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java
@@ -116,6 +116,7 @@
     protected SearchBuilder<HostVO> ResourceStateSearch;
     protected SearchBuilder<HostVO> NameLikeSearch;
     protected SearchBuilder<HostVO> NameSearch;
+    protected SearchBuilder<HostVO> hostHypervisorTypeAndVersionSearch;
     protected SearchBuilder<HostVO> SequenceSearch;
     protected SearchBuilder<HostVO> DirectlyConnectedSearch;
     protected SearchBuilder<HostVO> UnmanagedDirectConnectSearch;
@@ -311,6 +312,13 @@
         NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ);
         NameSearch.done();
 
+        hostHypervisorTypeAndVersionSearch = createSearchBuilder();
+        hostHypervisorTypeAndVersionSearch.and("hypervisorType", hostHypervisorTypeAndVersionSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
+        hostHypervisorTypeAndVersionSearch.and("hypervisorVersion", hostHypervisorTypeAndVersionSearch.entity().getHypervisorVersion(), SearchCriteria.Op.EQ);
+        hostHypervisorTypeAndVersionSearch.and("type", hostHypervisorTypeAndVersionSearch.entity().getType(), SearchCriteria.Op.EQ);
+        hostHypervisorTypeAndVersionSearch.and("status", hostHypervisorTypeAndVersionSearch.entity().getStatus(), SearchCriteria.Op.EQ);
+        hostHypervisorTypeAndVersionSearch.done();
+
         SequenceSearch = createSearchBuilder();
         SequenceSearch.and("id", SequenceSearch.entity().getId(), SearchCriteria.Op.EQ);
         // SequenceSearch.addRetrieve("sequence", SequenceSearch.entity().getSequence());
@@ -1446,6 +1454,16 @@
         return findOneBy(sc);
     }
 
+    @Override
+    public HostVO findHostByHypervisorTypeAndVersion(HypervisorType hypervisorType, String hypervisorVersion) {
+        SearchCriteria<HostVO> sc = hostHypervisorTypeAndVersionSearch.create();
+        sc.setParameters("hypervisorType", hypervisorType);
+        sc.setParameters("hypervisorVersion", hypervisorVersion);
+        sc.setParameters("type", Host.Type.Routing);
+        sc.setParameters("status", Status.Up);
+        return findOneBy(sc);
+    }
+
     private ResultSet executeSqlGetResultsetForMethodFindHostInZoneToExecuteCommand(HypervisorType hypervisorType, long zoneId, TransactionLegacy tx, String sql) throws SQLException {
         PreparedStatement pstmt = tx.prepareAutoCloseStatement(sql);
         pstmt.setString(1, Objects.toString(hypervisorType));
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/ExternalFirewallDeviceDao.java b/engine/schema/src/main/java/com/cloud/network/dao/ExternalFirewallDeviceDao.java
index b67130b..efb1b56 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/ExternalFirewallDeviceDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/ExternalFirewallDeviceDao.java
@@ -34,14 +34,14 @@
     /**
      * list the firewall devices added in to this physical network of certain provider type?
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      */
     List<ExternalFirewallDeviceVO> listByPhysicalNetworkAndProvider(long physicalNetworkId, String providerName);
 
     /**
      * list the firewall devices added in to this physical network by their allocation state
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      * @param allocationState firewall device allocation state
      * @return list of ExternalFirewallDeviceVO for the devices in the physical network with a device allocation state
      */
@@ -50,7 +50,7 @@
     /**
      * list the load balancer devices added in to this physical network by the device status (enabled/disabled)
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      * @param state firewall device status
      * @return list of ExternalFirewallDeviceVO for the devices in the physical network with a device state
      */
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceDao.java b/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceDao.java
index 9125447..9d0f3d6 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceDao.java
@@ -34,14 +34,14 @@
     /**
      * list the load balancer devices added in to this physical network of certain provider type?
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      */
     List<ExternalLoadBalancerDeviceVO> listByPhysicalNetworkAndProvider(long physicalNetworkId, String providerName);
 
     /**
      * list the load balancer devices added in to this physical network by their allocation state
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      * @param allocationState load balancer device allocation state
      * @return list of ExternalLoadBalancerDeviceVO for the devices in the physical network with a device allocation state
      */
@@ -50,7 +50,7 @@
     /**
      * list the load balancer devices added in to this physical network by the device status (enabled/disabled)
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      * @param state load balancer device status
      * @return list of ExternalLoadBalancerDeviceVO for the devices in the physical network with a device state
      */
@@ -59,7 +59,7 @@
     /**
      * list the load balancer devices added in to this physical network by the managed type (external/cloudstack managed)
      * @param physicalNetworkId physical Network Id
-     * @param providerName netwrok service provider name
+     * @param providerName network service provider name
      * @param managed managed type
      * @return list of ExternalLoadBalancerDeviceVO for the devices in to this physical network of a managed type
      */
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java
index f2e9bcb..21200db 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java
@@ -53,6 +53,12 @@
 
     List<FirewallRuleVO> listByIpAndNotRevoked(long ipAddressId);
 
+    /**
+     * counts the number of portforwarding rules for an IP address
+     *
+     * @param sourceIpId the id of the IP record
+     * @return the number of portforwarding rules for this IP
+     */
     long countRulesByIpId(long sourceIpId);
 
     long countRulesByIpIdAndState(long sourceIpId, FirewallRule.State state);
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java
index 51dfa91..bb4822e 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java
@@ -58,7 +58,7 @@
     IPAddressVO findByAssociatedVmId(long vmId);
 
     // for vm secondary ips case mapping is  IP1--> vmIp1, IP2-->vmIp2, etc
-    // This method is used when one vm is mapped to muliple to public ips
+    // This method is used when one vm is mapped to multiple to public ips
     List<IPAddressVO> findAllByAssociatedVmId(long vmId);
 
     IPAddressVO findByIpAndSourceNetworkId(long networkId, String ipAddress);
@@ -75,7 +75,7 @@
 
     long countFreeIPsInNetwork(long networkId);
 
-    IPAddressVO findByVmIp(String vmIp);
+    IPAddressVO findByIp(String ipAddress);
 
     IPAddressVO findByAssociatedVmIdAndVmIp(long vmId, String vmIp);
 
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
index b995959..d82ffbe 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java
@@ -304,7 +304,7 @@
 
 
     // for vm secondary ips case mapping is  IP1--> vmIp1, IP2-->vmIp2, etc
-    // Used when vm is mapped to muliple to public ips
+    // Used when vm is mapped to multiple to public ips
     @Override
     public List<IPAddressVO> findAllByAssociatedVmId(long vmId) {
         SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create();
@@ -314,9 +314,9 @@
     }
 
     @Override
-    public IPAddressVO findByVmIp(String vmIp) {
+    public IPAddressVO findByIp(String ipAddress) {
         SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create();
-        sc.setParameters("associatedVmIp", vmIp);
+        sc.setParameters("ipAddress", ipAddress);
         return findOneBy(sc);
     }
 
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
index 83e19b1..e7cfe6f 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
@@ -19,9 +19,12 @@
 import com.cloud.storage.GuestOSVO;
 import com.cloud.utils.db.GenericDao;
 
+import java.util.List;
+
 public interface GuestOSDao extends GenericDao<GuestOSVO, Long> {
 
     GuestOSVO listByDisplayName(String displayName);
 
+    List<GuestOSVO> listLikeDisplayName(String displayName);
     GuestOSVO findByCategoryIdAndDisplayNameOrderByCreatedDesc(long categoryId, String displayName);
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
index 68da2b9..3b9a3ec 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
@@ -33,12 +33,19 @@
 
     protected final SearchBuilder<GuestOSVO> Search;
 
+    protected final SearchBuilder<GuestOSVO> displayNameSearch;
+
     public GuestOSDaoImpl() {
         Search = createSearchBuilder();
         Search.and("category_id", Search.entity().getCategoryId(), SearchCriteria.Op.EQ);
         Search.and("display_name", Search.entity().getDisplayName(), SearchCriteria.Op.EQ);
         Search.and("is_user_defined", Search.entity().getIsUserDefined(), SearchCriteria.Op.EQ);
         Search.done();
+
+        displayNameSearch = createSearchBuilder();
+        displayNameSearch.and("display_name", displayNameSearch.entity().getDisplayName(), SearchCriteria.Op.LIKE);
+        displayNameSearch.done();
+
     }
 
     @Override
@@ -49,6 +56,13 @@
     }
 
     @Override
+    public List<GuestOSVO> listLikeDisplayName(String displayName) {
+        SearchCriteria<GuestOSVO> sc = displayNameSearch.create();
+        sc.setParameters("display_name", "%" + displayName + "%");
+        return listBy(sc);
+    }
+
+    @Override
     public GuestOSVO findByCategoryIdAndDisplayNameOrderByCreatedDesc(long categoryId, String displayName) {
         SearchCriteria<GuestOSVO> sc = Search.create();
         sc.setParameters("category_id", categoryId);
@@ -56,9 +70,9 @@
         sc.setParameters("is_user_defined", false);
 
         Filter orderByFilter = new Filter(GuestOSVO.class, "created", false, null, 1L);
-        List<GuestOSVO> guestOSes = listBy(sc, orderByFilter);
-        if (CollectionUtils.isNotEmpty(guestOSes)) {
-            return guestOSes.get(0);
+        List<GuestOSVO> guestOSlist = listBy(sc, orderByFilter);
+        if (CollectionUtils.isNotEmpty(guestOSlist)) {
+            return guestOSlist.get(0);
         }
         return null;
     }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
index 17c6b3c..47a7143 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
@@ -40,4 +40,6 @@
                                                                       String minHypervisorVersion);
 
     List<String> listHypervisorSupportedVersionsFromMinimumVersion(String hypervisorType, String hypervisorVersion);
+
+    List<GuestOSHypervisorVO> listByHypervisorTypeAndVersion(String hypervisorType, String hypervisorVersion);
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
index ae3ae9a..65f17c2 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
@@ -39,6 +39,7 @@
     protected final SearchBuilder<GuestOSHypervisorVO> userDefinedMappingSearch;
     protected final SearchBuilder<GuestOSHypervisorVO> guestOsNameSearch;
     protected final SearchBuilder<GuestOSHypervisorVO> availableHypervisorVersionSearch;
+    protected final SearchBuilder<GuestOSHypervisorVO> hypervisorTypeAndVersionSearch;
 
     public GuestOSHypervisorDaoImpl() {
         guestOsSearch = createSearchBuilder();
@@ -73,6 +74,11 @@
         availableHypervisorVersionSearch.select(null, SearchCriteria.Func.DISTINCT,
                 availableHypervisorVersionSearch.entity().getHypervisorVersion());
         availableHypervisorVersionSearch.done();
+
+        hypervisorTypeAndVersionSearch = createSearchBuilder();
+        hypervisorTypeAndVersionSearch.and("hypervisor_type", hypervisorTypeAndVersionSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ);
+        hypervisorTypeAndVersionSearch.and("hypervisor_version", hypervisorTypeAndVersionSearch.entity().getHypervisorVersion(), SearchCriteria.Op.EQ);
+        hypervisorTypeAndVersionSearch.done();
     }
 
     @Override
@@ -176,4 +182,11 @@
         return versions;
     }
 
+    @Override
+    public List<GuestOSHypervisorVO> listByHypervisorTypeAndVersion(String hypervisorType, String hypervisorVersion) {
+        SearchCriteria<GuestOSHypervisorVO> sc = hypervisorTypeAndVersionSearch.create();
+        sc.setParameters("hypervisor_type", hypervisorType);
+        sc.setParameters("hypervisor_version", hypervisorVersion);
+        return listIncludingRemovedBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
index 932dc71..17cdee9 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
@@ -81,6 +81,7 @@
 import com.cloud.upgrade.dao.Upgrade41710to41720;
 import com.cloud.upgrade.dao.Upgrade41720to41800;
 import com.cloud.upgrade.dao.Upgrade41800to41810;
+import com.cloud.upgrade.dao.Upgrade41810to41900;
 import com.cloud.upgrade.dao.Upgrade420to421;
 import com.cloud.upgrade.dao.Upgrade421to430;
 import com.cloud.upgrade.dao.Upgrade430to440;
@@ -218,6 +219,7 @@
                 .next("4.17.1.0", new Upgrade41710to41720())
                 .next("4.17.2.0", new Upgrade41720to41800())
                 .next("4.18.0.0", new Upgrade41800to41810())
+                .next("4.18.1.0", new Upgrade41810to41900())
                 .build();
     }
 
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java b/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
index def77c5..9b64430 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
@@ -17,6 +17,7 @@
 package com.cloud.upgrade;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import java.sql.Connection;
@@ -26,6 +27,7 @@
 
 import javax.inject.Inject;
 
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.GuestOSHypervisorMapping;
 import com.cloud.storage.GuestOSHypervisorVO;
 import com.cloud.storage.GuestOSVO;
@@ -94,7 +96,7 @@
         }
     }
 
-    private boolean addGuestOs(long categoryId, String displayName) {
+    public boolean addGuestOs(long categoryId, String displayName) {
         LOG.debug("Adding guest OS with category id: " + categoryId + " and display name: " + displayName);
         GuestOSVO guestOS = new GuestOSVO();
         guestOS.setCategoryId(categoryId);
@@ -116,7 +118,7 @@
             return;
         }
 
-        LOG.debug("Adding guest OS hypervisor mapping - " + mapping.toString());
+        LOG.debug("Adding guest OS hypervisor mapping - " + mapping.toString() + ", for guest OS with id - " + guestOsId);
         GuestOSHypervisorVO guestOsMapping = new GuestOSHypervisorVO();
         guestOsMapping.setHypervisorType(mapping.getHypervisorType());
         guestOsMapping.setHypervisorVersion(mapping.getHypervisorVersion());
@@ -198,4 +200,33 @@
         LOG.warn("Invalid Guest OS hypervisor mapping");
         return false;
     }
+
+    /**
+     * Copies guest OS mappings from src version to dest version for the hypervisor (use this to copy all mappings from older version to newer version during upgrade)
+     * @return true if copied successfully, else false.
+     */
+    public boolean copyGuestOSHypervisorMappings(HypervisorType hypervisorType, String srcVersion, String destVersion) {
+        if (hypervisorType == HypervisorType.None || hypervisorType == HypervisorType.Any) {
+            LOG.warn("Unable to copy, invalid hypervisor");
+            return false;
+        }
+
+        if (StringUtils.isAnyBlank(srcVersion, destVersion)) {
+            LOG.warn("Unable to copy, invalid hypervisor version details");
+            return false;
+        }
+
+        List<GuestOSHypervisorVO> guestOSHypervisorMappingsForSrcVersion = guestOSHypervisorDao.listByHypervisorTypeAndVersion(hypervisorType.toString(), srcVersion);
+        if (CollectionUtils.isEmpty(guestOSHypervisorMappingsForSrcVersion)) {
+            LOG.warn(String.format("Unable to copy, couldn't find guest OS mappings for hypervisor: %s and src version: %s", hypervisorType.toString(), srcVersion));
+            return false;
+        }
+
+        LOG.debug(String.format("Adding guest OS mappings for hypervisor: %s and version: %s, from version: %s ", hypervisorType.toString(), destVersion, srcVersion));
+        for (GuestOSHypervisorVO guestOSHypervisorMapping : guestOSHypervisorMappingsForSrcVersion) {
+            GuestOSHypervisorMapping mapping = new GuestOSHypervisorMapping(hypervisorType.toString(), destVersion, guestOSHypervisorMapping.getGuestOsName());
+            addGuestOsHypervisorMapping(mapping, guestOSHypervisorMapping.getGuestOsId());
+        }
+        return true;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java
index 915f22b..2e7eee1 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade410to420.java
@@ -1884,7 +1884,7 @@
             //Update all snapshots except KVM snapshots
             int rowCount = snapshotStoreInsert.executeUpdate();
             s_logger.debug("Inserted " + rowCount + " snapshots into snapshot_store_ref");
-            //backsnap_id for KVM snapshots is complate path. CONCAT is not required
+            //backsnap_id for KVM snapshots is complete path. CONCAT is not required
             try(PreparedStatement snapshotStoreInsert_2 =
                     conn.prepareStatement("INSERT INTO `cloud`.`snapshot_store_ref` (store_id,  snapshot_id, created, size, parent_snapshot_id, install_path, volume_id, update_count, ref_cnt, store_role, state) select sechost_id, id, created, size, prev_snap_id, backup_snap_id, volume_id, 0, 0, 'Image', 'Ready' from `cloud`.`snapshots` where status = 'BackedUp' and hypervisor_type = 'KVM' and sechost_id is not null and removed is null");) {
                 rowCount = snapshotStoreInsert_2.executeUpdate();
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java
new file mode 100644
index 0000000..c9ac468
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.upgrade.dao;
+
+import com.cloud.upgrade.SystemVmTemplateRegistration;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.log4j.Logger;
+
+import java.io.InputStream;
+import java.sql.Connection;
+
+public class Upgrade41810to41900 implements DbUpgrade, DbUpgradeSystemVmTemplate {
+    final static Logger LOG = Logger.getLogger(Upgrade41810to41900.class);
+    private SystemVmTemplateRegistration systemVmTemplateRegistration;
+
+    @Override
+    public String[] getUpgradableVersionRange() {
+        return new String[] {"4.18.1.0", "4.19.0.0"};
+    }
+
+    @Override
+    public String getUpgradedVersion() {
+        return "4.19.0.0";
+    }
+
+    @Override
+    public boolean supportsRollingUpgrade() {
+        return false;
+    }
+
+    @Override
+    public InputStream[] getPrepareScripts() {
+        final String scriptFile = "META-INF/db/schema-41810to41900.sql";
+        final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find " + scriptFile);
+        }
+
+        return new InputStream[] {script};
+    }
+
+    @Override
+    public void performDataMigration(Connection conn) {
+    }
+
+    @Override
+    public InputStream[] getCleanupScripts() {
+        final String scriptFile = "META-INF/db/schema-41810to41900-cleanup.sql";
+        final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile);
+        if (script == null) {
+            throw new CloudRuntimeException("Unable to find " + scriptFile);
+        }
+
+        return new InputStream[] {script};
+    }
+
+    private void initSystemVmTemplateRegistration() {
+        systemVmTemplateRegistration = new SystemVmTemplateRegistration("");
+    }
+
+    @Override
+    public void updateSystemVmTemplates(Connection conn) {
+        LOG.debug("Updating System Vm template IDs");
+        initSystemVmTemplateRegistration();
+        try {
+            systemVmTemplateRegistration.updateSystemVmTemplates(conn);
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
+        }
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java
index 4b476af..81a1124 100644
--- a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java
+++ b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java
@@ -19,13 +19,16 @@
 
 package com.cloud.vm;
 
+import java.util.Date;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Table;
-import java.util.Date;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
 
 @Entity
 @Table(name = "console_session")
@@ -55,7 +58,8 @@
     private long hostId;
 
     @Column(name = "acquired")
-    private boolean acquired;
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date acquired;
 
     @Column(name = "removed")
     private Date removed;
@@ -124,11 +128,11 @@
         this.removed = removed;
     }
 
-    public boolean isAcquired() {
+    public Date getAcquired() {
         return acquired;
     }
 
-    public void setAcquired(boolean acquired) {
+    public void setAcquired(Date acquired) {
         this.acquired = acquired;
     }
 }
diff --git a/engine/schema/src/main/java/com/cloud/vm/NicVO.java b/engine/schema/src/main/java/com/cloud/vm/NicVO.java
index 8905ebf..fba7c96 100644
--- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java
+++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java
@@ -30,6 +30,9 @@
 import javax.persistence.Table;
 import javax.persistence.Transient;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
 import com.cloud.network.Networks.AddressFormat;
 import com.cloud.network.Networks.Mode;
 import com.cloud.utils.db.GenericDao;
@@ -399,6 +402,15 @@
     }
 
     @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 31).append(id).toHashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return EqualsBuilder.reflectionEquals(this, obj);
+    }
+
     public Integer getMtu() {
         return mtu;
     }
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java
index dcf6505..5b5c350 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java
@@ -43,7 +43,7 @@
     private static final Logger s_logger = Logger.getLogger(ConsoleProxyDaoImpl.class);
 
     //
-    // query SQL for returnning console proxy assignment info as following
+    // query SQL for returning console proxy assignment info as following
     //         proxy vm id, count of assignment
     //
     private static final String PROXY_ASSIGNMENT_MATRIX = "SELECT c.id, count(runningVm.id) AS count "
@@ -63,7 +63,7 @@
         + " WHERE v.type='ConsoleProxy' AND (v.state='Creating' OR v.state='Starting' OR v.state='Running' OR v.state='Migrating')" + " GROUP BY d.id, d.name";
 
     //
-    // query SQL for returnning running console proxy count at data center basis
+    // query SQL for returning running console proxy count at data center basis
     //
     private static final String DATACENTER_PROXY_MATRIX =
         "SELECT d.id, d.name, count(dcid) as count"
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
index f2f4703..8e7e229 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java
@@ -19,12 +19,12 @@
 
 package com.cloud.vm.dao;
 
+import java.util.Date;
+
+import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.vm.ConsoleSessionVO;
-import com.cloud.utils.db.GenericDaoBase;
-
-import java.util.Date;
 
 public class ConsoleSessionDaoImpl extends GenericDaoBase<ConsoleSessionVO, Long> implements ConsoleSessionDao {
 
@@ -48,7 +48,7 @@
         if (consoleSessionVO == null) {
             return false;
         }
-        return !consoleSessionVO.isAcquired();
+        return consoleSessionVO.getAcquired() == null;
     }
 
     @Override
@@ -61,7 +61,7 @@
     @Override
     public void acquireSession(String sessionUuid) {
         ConsoleSessionVO consoleSessionVO = findByUuid(sessionUuid);
-        consoleSessionVO.setAcquired(true);
+        consoleSessionVO.setAcquired(new Date());
         update(consoleSessionVO.getId(), consoleSessionVO);
     }
 
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
index c52c690..2216564 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
@@ -89,6 +89,8 @@
 
     NicVO findByMacAddress(String macAddress);
 
+    NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac);
+
     List<NicVO> findNicsByIpv6GatewayIpv6CidrAndReserver(String ipv6Gateway, String ipv6Cidr, String reserverName);
 
     NicVO findByIpAddressAndVmType(String ip, VirtualMachine.Type vmType);
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
index c8efc07..211e548 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
@@ -218,6 +218,14 @@
     }
 
     @Override
+    public NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac) {
+        SearchCriteria<NicVO> sc = AllFieldsSearch.create();
+        sc.setParameters("network", networkId);
+        sc.setParameters("macAddress", mac);
+        return findOneIncludingRemovedBy(sc);
+    }
+
+    @Override
     public NicVO findDefaultNicForVM(long instanceId) {
         SearchCriteria<NicVO> sc = AllFieldsSearch.create();
         sc.setParameters("instance", instanceId);
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
index f5a0ceb..d464725 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java
@@ -55,6 +55,9 @@
     @Column(name = "is_default")
     private boolean isDefault = false;
 
+    @Column(name = "public_role")
+    private boolean publicRole = true;
+
     @Column(name = GenericDao.REMOVED_COLUMN)
     private Date removed;
 
@@ -120,4 +123,12 @@
     public String toString() {
         return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "uuid", "roleType");
     }
+
+    public boolean isPublicRole() {
+        return publicRole;
+    }
+
+    public void setPublicRole(boolean publicRole) {
+        this.publicRole = publicRole;
+    }
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
index 36833d5..a776f7b 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java
@@ -26,13 +26,15 @@
 import java.util.List;
 
 public interface RoleDao extends GenericDao<RoleVO, Long> {
-    List<RoleVO> findAllByName(String roleName);
+    List<RoleVO> findAllByName(String roleName, boolean showPrivateRole);
 
-    Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit);
+    Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole);
 
-    List<RoleVO> findAllByRoleType(RoleType type);
-    List<RoleVO> findByName(String roleName);
-    RoleVO findByNameAndType(String roleName, RoleType type);
+    List<RoleVO> findAllByRoleType(RoleType type, boolean showPrivateRole);
+    List<RoleVO> findByName(String roleName, boolean showPrivateRole);
+    RoleVO findByNameAndType(String roleName, RoleType type, boolean showPrivateRole);
 
-    Pair<List<RoleVO>, Integer> findAllByRoleType(RoleType type, Long offset, Long limit);
+    Pair<List<RoleVO>, Integer> findAllByRoleType(RoleType type, Long offset, Long limit, boolean showPrivateRole);
+
+    Pair<List<RoleVO>, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
index b4938a1..06d3108 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java
@@ -35,32 +35,41 @@
     private final SearchBuilder<RoleVO> RoleByNameSearch;
     private final SearchBuilder<RoleVO> RoleByTypeSearch;
     private final SearchBuilder<RoleVO> RoleByNameAndTypeSearch;
+    private final SearchBuilder<RoleVO> RoleByIsPublicSearch;
 
     public RoleDaoImpl() {
         super();
 
         RoleByNameSearch = createSearchBuilder();
         RoleByNameSearch.and("roleName", RoleByNameSearch.entity().getName(), SearchCriteria.Op.LIKE);
+        RoleByNameSearch.and("isPublicRole", RoleByNameSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
         RoleByNameSearch.done();
 
         RoleByTypeSearch = createSearchBuilder();
         RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
+        RoleByTypeSearch.and("isPublicRole", RoleByTypeSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
         RoleByTypeSearch.done();
 
         RoleByNameAndTypeSearch = createSearchBuilder();
         RoleByNameAndTypeSearch.and("roleName", RoleByNameAndTypeSearch.entity().getName(), SearchCriteria.Op.EQ);
         RoleByNameAndTypeSearch.and("roleType", RoleByNameAndTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ);
+        RoleByNameAndTypeSearch.and("isPublicRole", RoleByNameAndTypeSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
         RoleByNameAndTypeSearch.done();
+
+        RoleByIsPublicSearch = createSearchBuilder();
+        RoleByIsPublicSearch.and("isPublicRole", RoleByIsPublicSearch.entity().isPublicRole(), SearchCriteria.Op.EQ);
+        RoleByIsPublicSearch.done();
     }
 
     @Override
-    public List<RoleVO> findAllByName(final String roleName) {
-        return findAllByName(roleName, null, null, null).first();
+    public List<RoleVO> findAllByName(final String roleName, boolean showPrivateRole) {
+        return findAllByName(roleName, null, null, null, showPrivateRole).first();
     }
 
     @Override
-    public Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit) {
+    public Pair<List<RoleVO>, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole) {
         SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
+        filterPrivateRolesIfNeeded(sc, showPrivateRole);
         if (StringUtils.isNotEmpty(roleName)) {
             sc.setParameters("roleName", roleName);
         }
@@ -72,28 +81,44 @@
     }
 
     @Override
-    public List<RoleVO> findAllByRoleType(final RoleType type) {
-        return findAllByRoleType(type, null, null).first();
+    public List<RoleVO> findAllByRoleType(final RoleType type, boolean showPrivateRole) {
+        return findAllByRoleType(type, null, null, showPrivateRole).first();
     }
 
-    public Pair<List<RoleVO>, Integer> findAllByRoleType(final RoleType type, Long offset, Long limit) {
+    public Pair<List<RoleVO>, Integer> findAllByRoleType(final RoleType type, Long offset, Long limit, boolean showPrivateRole) {
         SearchCriteria<RoleVO> sc = RoleByTypeSearch.create();
+        filterPrivateRolesIfNeeded(sc, showPrivateRole);
         sc.setParameters("roleType", type);
         return searchAndCount(sc, new Filter(RoleVO.class, "id", true, offset, limit));
     }
 
     @Override
-    public List<RoleVO> findByName(String roleName) {
+    public List<RoleVO> findByName(String roleName, boolean showPrivateRole) {
         SearchCriteria<RoleVO> sc = RoleByNameSearch.create();
+        filterPrivateRolesIfNeeded(sc, showPrivateRole);
         sc.setParameters("roleName", roleName);
         return listBy(sc);
     }
 
     @Override
-    public RoleVO findByNameAndType(String roleName, RoleType type) {
+    public RoleVO findByNameAndType(String roleName, RoleType type, boolean showPrivateRole) {
         SearchCriteria<RoleVO> sc = RoleByNameAndTypeSearch.create();
+        filterPrivateRolesIfNeeded(sc, showPrivateRole);
         sc.setParameters("roleName", roleName);
         sc.setParameters("roleType", type);
         return findOneBy(sc);
     }
+
+    @Override
+    public Pair<List<RoleVO>, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole) {
+        SearchCriteria<RoleVO> sc = RoleByIsPublicSearch.create();
+        filterPrivateRolesIfNeeded(sc, showPrivateRole);
+        return searchAndCount(sc, new Filter(RoleVO.class, "id", true, startIndex, limit));
+    }
+
+    public void filterPrivateRolesIfNeeded(SearchCriteria<RoleVO> sc, boolean showPrivateRole) {
+        if (!showPrivateRole) {
+            sc.setParameters("isPublicRole", true);
+        }
+    }
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java
new file mode 100644
index 0000000..176f88c
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm.schedule;
+
+import com.cloud.utils.db.GenericDao;
+
+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 java.time.ZoneId;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.UUID;
+
+@Entity
+@Table(name = "vm_schedule")
+public class VMScheduleVO implements VMSchedule {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id", nullable = false)
+    Long id;
+
+    @Column(name = "uuid", nullable = false)
+    String uuid;
+
+    @Column(name = "description")
+    String description;
+
+    @Column(name = "vm_id", nullable = false)
+    long vmId;
+
+    @Column(name = "schedule", nullable = false)
+    String schedule;
+
+    @Column(name = "timezone", nullable = false)
+    String timeZone;
+
+    @Column(name = "action", nullable = false)
+    @Enumerated(value = EnumType.STRING)
+    Action action;
+
+    @Column(name = "enabled", nullable = false)
+    boolean enabled;
+
+    @Column(name = "start_date", nullable = false)
+    @Temporal(value = TemporalType.TIMESTAMP)
+    Date startDate;
+
+    @Column(name = "end_date", nullable = true)
+    @Temporal(value = TemporalType.TIMESTAMP)
+    Date endDate;
+
+    @Column(name = GenericDao.CREATED_COLUMN)
+    Date created;
+
+    @Column(name = GenericDao.REMOVED_COLUMN)
+    Date removed;
+
+    public VMScheduleVO() {
+        uuid = UUID.randomUUID().toString();
+    }
+
+    public VMScheduleVO(long vmId, String description, String schedule, String timeZone, Action action, Date startDate, Date endDate, boolean enabled) {
+        uuid = UUID.randomUUID().toString();
+        this.vmId = vmId;
+        this.description = description;
+        this.schedule = schedule;
+        this.timeZone = timeZone;
+        this.action = action;
+        this.startDate = startDate;
+        this.endDate = endDate;
+        this.enabled = enabled;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public long getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(long vmId) {
+        this.vmId = vmId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getSchedule() {
+        return schedule.substring(2);
+    }
+
+    public void setSchedule(String schedule) {
+        this.schedule = schedule;
+    }
+
+    @Override
+    public String getTimeZone() {
+        return timeZone;
+    }
+
+    public void setTimeZone(String timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    public Action getAction() {
+        return action;
+    }
+
+    public void setAction(Action action) {
+        this.action = action;
+    }
+
+    public boolean getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @Override
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    @Override
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    public void setEndDate(Date endDate) {
+        this.endDate = endDate;
+    }
+
+    @Override
+    public ZoneId getTimeZoneId() {
+        return TimeZone.getTimeZone(getTimeZone()).toZoneId();
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java
new file mode 100644
index 0000000..0c2dd94
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule;
+
+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 java.util.Date;
+import java.util.UUID;
+
+@Entity
+@Table(name = "vm_scheduled_job")
+public class VMScheduledJobVO implements VMScheduledJob {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    Long id;
+
+    @Column(name = "uuid", nullable = false)
+    String uuid;
+
+    @Column(name = "vm_id", nullable = false)
+    long vmId;
+
+    @Column(name = "vm_schedule_id", nullable = false)
+    long vmScheduleId;
+
+    @Column(name = "async_job_id")
+    Long asyncJobId;
+
+    @Column(name = "action", nullable = false)
+    @Enumerated(value = EnumType.STRING)
+    VMSchedule.Action action;
+
+    @Column(name = "scheduled_timestamp")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    Date scheduledTime;
+
+    public VMScheduledJobVO() {
+        uuid = UUID.randomUUID().toString();
+    }
+
+    public VMScheduledJobVO(long vmId, long vmScheduleId, VMSchedule.Action action, Date scheduledTime) {
+        uuid = UUID.randomUUID().toString();
+        this.vmId = vmId;
+        this.vmScheduleId = vmScheduleId;
+        this.action = action;
+        this.scheduledTime = scheduledTime;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public long getVmId() {
+        return vmId;
+    }
+
+    @Override
+    public long getVmScheduleId() {
+        return vmScheduleId;
+    }
+
+    @Override
+    public Long getAsyncJobId() {
+        return asyncJobId;
+    }
+
+    @Override
+    public void setAsyncJobId(long asyncJobId) {
+        this.asyncJobId = asyncJobId;
+    }
+
+    @Override
+    public VMSchedule.Action getAction() {
+        return action;
+    }
+
+    @Override
+    public Date getScheduledTime() {
+        return scheduledTime;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java
new file mode 100644
index 0000000..b8c808b
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDao.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule.dao;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleVO;
+
+import java.util.List;
+
+public interface VMScheduleDao extends GenericDao<VMScheduleVO, Long> {
+    List<VMScheduleVO> listAllActiveSchedules();
+
+    long removeSchedulesForVmIdAndIds(Long vmId, List<Long> ids);
+
+    Pair<List<VMScheduleVO>, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit);
+
+    SearchCriteria<VMScheduleVO> getSearchCriteriaForVMId(Long vmId);
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java
new file mode 100644
index 0000000..db8c0c3
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduleDaoImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule.dao;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.vm.schedule.VMScheduleVO;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+@Component
+public class VMScheduleDaoImpl extends GenericDaoBase<VMScheduleVO, Long> implements VMScheduleDao {
+
+    private final SearchBuilder<VMScheduleVO> activeScheduleSearch;
+
+    private final SearchBuilder<VMScheduleVO> scheduleSearchByVmIdAndIds;
+
+    private final SearchBuilder<VMScheduleVO> scheduleSearch;
+
+    public VMScheduleDaoImpl() {
+        super();
+        activeScheduleSearch = createSearchBuilder();
+        activeScheduleSearch.and(ApiConstants.ENABLED, activeScheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
+        activeScheduleSearch.and().op(activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.NULL);
+        activeScheduleSearch.or(ApiConstants.END_DATE, activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.GT);
+        activeScheduleSearch.cp();
+        activeScheduleSearch.done();
+
+        scheduleSearchByVmIdAndIds = createSearchBuilder();
+        scheduleSearchByVmIdAndIds.and(ApiConstants.ID, scheduleSearchByVmIdAndIds.entity().getId(), SearchCriteria.Op.IN);
+        scheduleSearchByVmIdAndIds.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearchByVmIdAndIds.entity().getVmId(), SearchCriteria.Op.EQ);
+        scheduleSearchByVmIdAndIds.done();
+
+        scheduleSearch = createSearchBuilder();
+        scheduleSearch.and(ApiConstants.ID, scheduleSearch.entity().getId(), SearchCriteria.Op.EQ);
+        scheduleSearch.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearch.entity().getVmId(), SearchCriteria.Op.EQ);
+        scheduleSearch.and(ApiConstants.ACTION, scheduleSearch.entity().getAction(), SearchCriteria.Op.EQ);
+        scheduleSearch.and(ApiConstants.ENABLED, scheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
+        scheduleSearch.done();
+
+    }
+
+    @Override
+    public List<VMScheduleVO> listAllActiveSchedules() {
+        // WHERE enabled = true AND (end_date IS NULL OR end_date > current_date)
+        SearchCriteria<VMScheduleVO> sc = activeScheduleSearch.create();
+        sc.setParameters(ApiConstants.ENABLED, true);
+        sc.setParameters(ApiConstants.END_DATE, new Date());
+        return search(sc, null);
+    }
+
+    @Override
+    public long removeSchedulesForVmIdAndIds(Long vmId, List<Long> ids) {
+        SearchCriteria<VMScheduleVO> sc = scheduleSearchByVmIdAndIds.create();
+        sc.setParameters(ApiConstants.ID, ids.toArray());
+        sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
+        return remove(sc);
+    }
+
+    @Override
+    public Pair<List<VMScheduleVO>, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit) {
+        SearchCriteria<VMScheduleVO> sc = scheduleSearch.create();
+
+        if (id != null) {
+            sc.setParameters(ApiConstants.ID, id);
+        }
+        if (enabled != null) {
+            sc.setParameters(ApiConstants.ENABLED, enabled);
+        }
+        if (action != null) {
+            sc.setParameters(ApiConstants.ACTION, action);
+        }
+        sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
+
+        Filter filter = new Filter(VMScheduleVO.class, ApiConstants.ID, false, offset, limit);
+        return searchAndCount(sc, filter);
+    }
+
+    @Override
+    public SearchCriteria<VMScheduleVO> getSearchCriteriaForVMId(Long vmId) {
+        SearchCriteria<VMScheduleVO> sc = scheduleSearch.create();
+        sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
+        return sc;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java
new file mode 100644
index 0000000..7b8c01a
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule.dao;
+
+import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.vm.schedule.VMScheduledJobVO;
+
+import java.util.Date;
+import java.util.List;
+
+public interface VMScheduledJobDao extends GenericDao<VMScheduledJobVO, Long> {
+
+    List<VMScheduledJobVO> listJobsToStart(Date currentTimestamp);
+
+    int expungeJobsForSchedules(List<Long> scheduleId, Date dateAfter);
+
+    int expungeJobsBefore(Date currentTimestamp);
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java
new file mode 100644
index 0000000..50a2b12
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDaoImpl.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule.dao;
+
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import org.apache.cloudstack.vm.schedule.VMScheduledJobVO;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+@Component
+public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long> implements VMScheduledJobDao {
+
+    private final SearchBuilder<VMScheduledJobVO> jobsToStartSearch;
+
+    private final SearchBuilder<VMScheduledJobVO> expungeJobsBeforeSearch;
+
+    private final SearchBuilder<VMScheduledJobVO> expungeJobForScheduleSearch;
+
+    static final String SCHEDULED_TIMESTAMP = "scheduled_timestamp";
+
+    static final String VM_SCHEDULE_ID = "vm_schedule_id";
+
+    public VMScheduledJobDaoImpl() {
+        super();
+        jobsToStartSearch = createSearchBuilder();
+        jobsToStartSearch.and(SCHEDULED_TIMESTAMP, jobsToStartSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ);
+        jobsToStartSearch.and("async_job_id", jobsToStartSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL);
+        jobsToStartSearch.done();
+
+        expungeJobsBeforeSearch = createSearchBuilder();
+        expungeJobsBeforeSearch.and(SCHEDULED_TIMESTAMP, expungeJobsBeforeSearch.entity().getScheduledTime(), SearchCriteria.Op.LT);
+        expungeJobsBeforeSearch.done();
+
+        expungeJobForScheduleSearch = createSearchBuilder();
+        expungeJobForScheduleSearch.and(VM_SCHEDULE_ID, expungeJobForScheduleSearch.entity().getVmScheduleId(), SearchCriteria.Op.IN);
+        expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ);
+        expungeJobForScheduleSearch.done();
+    }
+
+    /**
+     * Execution of job wouldn't be at exact seconds. So, we round off and then execute.
+     */
+    @Override
+    public List<VMScheduledJobVO> listJobsToStart(Date currentTimestamp) {
+        if (currentTimestamp == null) {
+            currentTimestamp = new Date();
+        }
+        Date truncatedTs = DateUtils.round(currentTimestamp, Calendar.MINUTE);
+
+        SearchCriteria<VMScheduledJobVO> sc = jobsToStartSearch.create();
+        sc.setParameters(SCHEDULED_TIMESTAMP, truncatedTs);
+        Filter filter = new Filter(VMScheduledJobVO.class, "vmScheduleId", true, null, null);
+        return search(sc, filter);
+    }
+
+    @Override
+    public int expungeJobsForSchedules(List<Long> vmScheduleIds, Date dateAfter) {
+        SearchCriteria<VMScheduledJobVO> sc = expungeJobForScheduleSearch.create();
+        sc.setParameters(VM_SCHEDULE_ID, vmScheduleIds.toArray());
+        if (dateAfter != null) {
+            sc.setParameters(SCHEDULED_TIMESTAMP, dateAfter);
+        }
+        return expunge(sc);
+    }
+
+    @Override
+    public int expungeJobsBefore(Date date) {
+        SearchCriteria<VMScheduledJobVO> sc = expungeJobsBeforeSearch.create();
+        sc.setParameters(SCHEDULED_TIMESTAMP, date);
+        return expunge(sc);
+    }
+}
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 51e557f..04ec733 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -275,4 +275,6 @@
   <bean id="UserVmDeployAsIsDetailsDaoImpl" class="com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDaoImpl" />
   <bean id="NetworkPermissionDaoImpl" class="org.apache.cloudstack.network.dao.NetworkPermissionDaoImpl" />
   <bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" />
+  <bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" />
+  <bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" />
 </beans>
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
index 2af6723..a0c9847 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
@@ -1568,4 +1568,4 @@
 SET
   usage_type = 22
 WHERE
-  usage_type = 24 AND usage_display like '% io write';
\ No newline at end of file
+  usage_type = 24 AND usage_display like '% io write';
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900-cleanup.sql
new file mode 100644
index 0000000..ff0e780
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900-cleanup.sql
@@ -0,0 +1,20 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+--;
+-- Schema upgrade cleanup from 4.18.1.0 to 4.19.0.0
+--;
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql
new file mode 100644
index 0000000..52c58fe
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql
@@ -0,0 +1,182 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+--;
+-- Schema upgrade from 4.18.1.0 to 4.19.0.0
+--;
+
+ALTER TABLE `cloud`.`mshost` MODIFY COLUMN `state` varchar(25);
+
+DROP VIEW IF EXISTS `cloud`.`async_job_view`;
+CREATE VIEW `cloud`.`async_job_view` AS
+    select
+        account.id account_id,
+        account.uuid account_uuid,
+        account.account_name account_name,
+        account.type account_type,
+        domain.id domain_id,
+        domain.uuid domain_uuid,
+        domain.name domain_name,
+        domain.path domain_path,
+        user.id user_id,
+        user.uuid user_uuid,
+        async_job.id,
+        async_job.uuid,
+        async_job.job_cmd,
+        async_job.job_status,
+        async_job.job_process_status,
+        async_job.job_result_code,
+        async_job.job_result,
+        async_job.created,
+        async_job.removed,
+        async_job.instance_type,
+        async_job.instance_id,
+        async_job.job_executing_msid,
+        CASE
+            WHEN async_job.instance_type = 'Volume' THEN volumes.uuid
+            WHEN
+                async_job.instance_type = 'Template'
+                    or async_job.instance_type = 'Iso'
+            THEN
+                vm_template.uuid
+            WHEN
+                async_job.instance_type = 'VirtualMachine'
+                    or async_job.instance_type = 'ConsoleProxy'
+                    or async_job.instance_type = 'SystemVm'
+                    or async_job.instance_type = 'DomainRouter'
+            THEN
+                vm_instance.uuid
+            WHEN async_job.instance_type = 'Snapshot' THEN snapshots.uuid
+            WHEN async_job.instance_type = 'Host' THEN host.uuid
+            WHEN async_job.instance_type = 'StoragePool' THEN storage_pool.uuid
+            WHEN async_job.instance_type = 'IpAddress' THEN user_ip_address.uuid
+            WHEN async_job.instance_type = 'SecurityGroup' THEN security_group.uuid
+            WHEN async_job.instance_type = 'PhysicalNetwork' THEN physical_network.uuid
+            WHEN async_job.instance_type = 'TrafficType' THEN physical_network_traffic_types.uuid
+            WHEN async_job.instance_type = 'PhysicalNetworkServiceProvider' THEN physical_network_service_providers.uuid
+            WHEN async_job.instance_type = 'FirewallRule' THEN firewall_rules.uuid
+            WHEN async_job.instance_type = 'Account' THEN acct.uuid
+            WHEN async_job.instance_type = 'User' THEN us.uuid
+            WHEN async_job.instance_type = 'StaticRoute' THEN static_routes.uuid
+            WHEN async_job.instance_type = 'PrivateGateway' THEN vpc_gateways.uuid
+            WHEN async_job.instance_type = 'Counter' THEN counter.uuid
+            WHEN async_job.instance_type = 'Condition' THEN conditions.uuid
+            WHEN async_job.instance_type = 'AutoScalePolicy' THEN autoscale_policies.uuid
+            WHEN async_job.instance_type = 'AutoScaleVmProfile' THEN autoscale_vmprofiles.uuid
+            WHEN async_job.instance_type = 'AutoScaleVmGroup' THEN autoscale_vmgroups.uuid
+            ELSE null
+        END instance_uuid
+    from
+        `cloud`.`async_job`
+            left join
+        `cloud`.`account` ON async_job.account_id = account.id
+            left join
+        `cloud`.`domain` ON domain.id = account.domain_id
+            left join
+        `cloud`.`user` ON async_job.user_id = user.id
+            left join
+        `cloud`.`volumes` ON async_job.instance_id = volumes.id
+            left join
+        `cloud`.`vm_template` ON async_job.instance_id = vm_template.id
+            left join
+        `cloud`.`vm_instance` ON async_job.instance_id = vm_instance.id
+            left join
+        `cloud`.`snapshots` ON async_job.instance_id = snapshots.id
+            left join
+        `cloud`.`host` ON async_job.instance_id = host.id
+            left join
+        `cloud`.`storage_pool` ON async_job.instance_id = storage_pool.id
+            left join
+        `cloud`.`user_ip_address` ON async_job.instance_id = user_ip_address.id
+            left join
+        `cloud`.`security_group` ON async_job.instance_id = security_group.id
+            left join
+        `cloud`.`physical_network` ON async_job.instance_id = physical_network.id
+            left join
+        `cloud`.`physical_network_traffic_types` ON async_job.instance_id = physical_network_traffic_types.id
+            left join
+        `cloud`.`physical_network_service_providers` ON async_job.instance_id = physical_network_service_providers.id
+            left join
+        `cloud`.`firewall_rules` ON async_job.instance_id = firewall_rules.id
+            left join
+        `cloud`.`account` acct ON async_job.instance_id = acct.id
+            left join
+        `cloud`.`user` us ON async_job.instance_id = us.id
+            left join
+        `cloud`.`static_routes` ON async_job.instance_id = static_routes.id
+            left join
+        `cloud`.`vpc_gateways` ON async_job.instance_id = vpc_gateways.id
+            left join
+        `cloud`.`counter` ON async_job.instance_id = counter.id
+            left join
+        `cloud`.`conditions` ON async_job.instance_id = conditions.id
+            left join
+        `cloud`.`autoscale_policies` ON async_job.instance_id = autoscale_policies.id
+            left join
+        `cloud`.`autoscale_vmprofiles` ON async_job.instance_id = autoscale_vmprofiles.id
+            left join
+        `cloud`.`autoscale_vmgroups` ON async_job.instance_id = autoscale_vmgroups.id;
+
+-- Invalidate existing console_session records
+UPDATE `cloud`.`console_session` SET removed=now();
+-- Modify acquired column in console_session to datetime type
+ALTER TABLE `cloud`.`console_session` DROP `acquired`, ADD `acquired` datetime COMMENT 'When the session was acquired' AFTER `host_id`;
+
+-- create_public_parameter_on_roles. #6960
+ALTER TABLE `cloud`.`roles` ADD COLUMN `public_role` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Indicates whether the role will be visible to all users (public) or only to root admins (private). If this parameter is not specified during the creation of the role its value will be defaulted to true (public).';
+
+-- Add tables for VM Scheduler
+DROP TABLE IF EXISTS `cloud`.`vm_schedule`;
+CREATE TABLE `cloud`.`vm_schedule` (
+  `id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
+  `vm_id` bigint unsigned NOT NULL,
+  `uuid` varchar(40) NOT NULL COMMENT 'schedule uuid',
+  `description` varchar(1024) COMMENT 'description of the vm schedule',
+  `schedule` varchar(255) NOT NULL COMMENT 'schedule frequency in cron format',
+  `timezone` varchar(100) NOT NULL COMMENT 'the timezone in which the schedule time is specified',
+  `action` varchar(20) NOT NULL COMMENT 'action to perform',
+  `enabled` int(1) NOT NULL COMMENT 'Enabled or disabled',
+  `start_date` datetime NOT NULL COMMENT 'start time for this schedule',
+  `end_date` datetime COMMENT 'end time for this schedule',
+  `created` datetime NOT NULL COMMENT 'date created',
+  `removed` datetime COMMENT 'date removed if not null',
+  PRIMARY KEY (`id`),
+  INDEX `i_vm_schedule__vm_id`(`vm_id`),
+  INDEX `i_vm_schedule__enabled_end_date`(`enabled`, `end_date`),
+  CONSTRAINT `fk_vm_schedule__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE
+  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+DROP TABLE IF EXISTS `cloud`.`vm_scheduled_job`;
+CREATE TABLE `cloud`.`vm_scheduled_job` (
+  `id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
+  `vm_id` bigint unsigned NOT NULL,
+  `vm_schedule_id` bigint unsigned NOT NULL,
+  `uuid` varchar(40) NOT NULL COMMENT 'scheduled job uuid',
+  `action` varchar(20) NOT NULL COMMENT 'action to perform',
+  `scheduled_timestamp` datetime NOT NULL COMMENT 'Time at which the action is taken',
+  `async_job_id` bigint unsigned DEFAULT NULL COMMENT 'If this schedule is being executed, it is the id of the create aysnc_job. Before that it is null',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY (`vm_schedule_id`, `scheduled_timestamp`),
+  INDEX `i_vm_scheduled_job__scheduled_timestamp`(`scheduled_timestamp`),
+  INDEX `i_vm_scheduled_job__vm_id`(`vm_id`),
+  CONSTRAINT `fk_vm_scheduled_job__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_vm_scheduled_job__vm_schedule_id` FOREIGN KEY (`vm_schedule_id`) REFERENCES `vm_schedule`(`id`) ON DELETE CASCADE
+  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Add support for different cluster types for kubernetes
+ALTER TABLE `cloud`.`kubernetes_cluster` ADD COLUMN `cluster_type` varchar(64) DEFAULT 'CloudManaged' COMMENT 'type of cluster';
+ALTER TABLE `cloud`.`kubernetes_cluster` MODIFY COLUMN `kubernetes_version_id` bigint unsigned NULL COMMENT 'the ID of the Kubernetes version of this Kubernetes cluster';
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql b/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql
index 06ee70b..90a52bd 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql
@@ -448,7 +448,7 @@
 
 UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,LXC,Hyperv' WHERE name='hypervisor.list';
 UPDATE `cloud`.`configuration` SET description="If set to true, will set guest VM's name as it appears on the hypervisor, to its hostname. The flag is supported for VMware hypervisor only" WHERE name='vm.instancename.flag';
-INSERT IGNORE INTO `cloud`.`configuration`(category, instance, component, name, value, description, default_value) VALUES ('Advanced', 'DEFAULT', 'management-server', 'implicit.host.tags', 'GPU', 'Tag hosts at the time of host disovery based on the host properties/capabilities ', 'GPU');
+INSERT IGNORE INTO `cloud`.`configuration`(category, instance, component, name, value, description, default_value) VALUES ('Advanced', 'DEFAULT', 'management-server', 'implicit.host.tags', 'GPU', 'Tag hosts at the time of host discovery based on the host properties/capabilities ', 'GPU');
 
 DROP VIEW IF EXISTS `cloud`.`domain_router_view`;
 CREATE VIEW `cloud`.`domain_router_view` AS
diff --git a/engine/schema/src/test/java/com/cloud/upgrade/GuestOsMapperTest.java b/engine/schema/src/test/java/com/cloud/upgrade/GuestOsMapperTest.java
new file mode 100644
index 0000000..fa6c693
--- /dev/null
+++ b/engine/schema/src/test/java/com/cloud/upgrade/GuestOsMapperTest.java
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.upgrade;
+
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.storage.GuestOSHypervisorVO;
+import com.cloud.storage.dao.GuestOSHypervisorDao;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(PowerMockRunner.class)
+public class GuestOsMapperTest {
+
+    @Spy
+    @InjectMocks
+    GuestOsMapper guestOsMapper = new GuestOsMapper();
+
+    @Mock
+    GuestOSHypervisorDao guestOSHypervisorDao;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testCopyGuestOSHypervisorMappingsFailures() {
+        boolean result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.Any, "6.0", "7.0");
+        Assert.assertFalse(result);
+
+        result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.None, "6.0", "7.0");
+        Assert.assertFalse(result);
+
+        result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.XenServer, "", "7.0");
+        Assert.assertFalse(result);
+
+        result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.XenServer, "6.0", "");
+        Assert.assertFalse(result);
+
+        Mockito.when(guestOSHypervisorDao.listByHypervisorTypeAndVersion(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
+        result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.XenServer, "6.0", "7.0");
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testCopyGuestOSHypervisorMappingsSuccess() {
+        GuestOSHypervisorVO guestOSHypervisorVO = Mockito.mock(GuestOSHypervisorVO.class);
+        List<GuestOSHypervisorVO> guestOSHypervisorVOS = new ArrayList<>();
+        guestOSHypervisorVOS.add(guestOSHypervisorVO);
+        Mockito.when(guestOSHypervisorDao.listByHypervisorTypeAndVersion(Mockito.anyString(), Mockito.anyString())).thenReturn(guestOSHypervisorVOS);
+        Mockito.when(guestOSHypervisorVO.getGuestOsName()).thenReturn("centos");
+        GuestOSHypervisorVO guestOsMapping = Mockito.mock(GuestOSHypervisorVO.class);
+        Mockito.when(guestOSHypervisorDao.persist(guestOsMapping)).thenReturn(guestOsMapping);
+
+        boolean result = guestOsMapper.copyGuestOSHypervisorMappings(Hypervisor.HypervisorType.XenServer, "6.0", "7.0");
+        Assert.assertTrue(result);
+    }
+}
diff --git a/engine/service/pom.xml b/engine/service/pom.xml
index 3e312d4..64453d3 100644
--- a/engine/service/pom.xml
+++ b/engine/service/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <artifactId>cloud-engine-service</artifactId>
     <packaging>war</packaging>
diff --git a/engine/storage/cache/pom.xml b/engine/storage/cache/pom.xml
index e70e1f0..989402c 100644
--- a/engine/storage/cache/pom.xml
+++ b/engine/storage/cache/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/cache/src/main/java/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java b/engine/storage/cache/src/main/java/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java
index 0abdf2e..a687ddf 100644
--- a/engine/storage/cache/src/main/java/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java
+++ b/engine/storage/cache/src/main/java/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java
@@ -253,11 +253,11 @@
             if (obj != null) {
                 State st = obj.getState();
 
-                long miliSeconds = 10000;
+                long milliSeconds = 10000;
                 long timeoutSeconds = 3600;
-                long timeoutMiliSeconds = timeoutSeconds * 1000;
+                long timeoutMilliSeconds = timeoutSeconds * 1000;
                 Date now = new Date();
-                long expiredEpoch = now.getTime() + timeoutMiliSeconds;
+                long expiredEpoch = now.getTime() + timeoutMilliSeconds;
                 Date expiredDate = new Date(expiredEpoch);
 
                 /*
@@ -273,7 +273,7 @@
                      */
                     s_logger.debug("waiting cache copy completion type: " + typeName + ", id: " + obj.getObjectId() + ", lock: " + lock.hashCode());
                     try {
-                        lock.wait(miliSeconds);
+                        lock.wait(milliSeconds);
                     } catch (InterruptedException e) {
                         s_logger.debug("[ignored] interrupted while waiting for cache copy completion.");
                     }
diff --git a/engine/storage/configdrive/pom.xml b/engine/storage/configdrive/pom.xml
index 122075c..041616d 100644
--- a/engine/storage/configdrive/pom.xml
+++ b/engine/storage/configdrive/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/datamotion/pom.xml b/engine/storage/datamotion/pom.xml
index 55d8a0e..aa3b986 100644
--- a/engine/storage/datamotion/pom.xml
+++ b/engine/storage/datamotion/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/image/pom.xml b/engine/storage/image/pom.xml
index a182ee2..bf7b7f8 100644
--- a/engine/storage/image/pom.xml
+++ b/engine/storage/image/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml
index 77f17d6..735c1a0 100644
--- a/engine/storage/integration-test/pom.xml
+++ b/engine/storage/integration-test/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/pom.xml b/engine/storage/pom.xml
index 40f03b6..86bb847 100644
--- a/engine/storage/pom.xml
+++ b/engine/storage/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/snapshot/pom.xml b/engine/storage/snapshot/pom.xml
index cf65a7f..9ba5e3a 100644
--- a/engine/storage/snapshot/pom.xml
+++ b/engine/storage/snapshot/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/StorageVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/StorageVMSnapshotStrategy.java
index 9582900..4030295 100644
--- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/StorageVMSnapshotStrategy.java
+++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/StorageVMSnapshotStrategy.java
@@ -177,7 +177,7 @@
                         thawAnswer = (FreezeThawVMAnswer) agentMgr.send(hostId, thawCmd);
                         throw new CloudRuntimeException("Could not take snapshot for volume with id=" + vol.getId());
                     }
-                    s_logger.info(String.format("Snapshot with id=%s, took  %s miliseconds", snapInfo.getId(),
+                    s_logger.info(String.format("Snapshot with id=%s, took  %s milliseconds", snapInfo.getId(),
                             TimeUnit.MILLISECONDS.convert(elapsedTime(startSnapshtot), TimeUnit.NANOSECONDS)));
                 }
                 answer = new CreateVMSnapshotAnswer(ccmd, true, "");
@@ -185,7 +185,7 @@
                 thawAnswer = (FreezeThawVMAnswer) agentMgr.send(hostId, thawCmd);
                 if (thawAnswer != null && thawAnswer.getResult()) {
                     s_logger.info(String.format(
-                            "Virtual machne is thawed. The freeze of virtual machine took %s miliseconds.",
+                            "Virtual machne is thawed. The freeze of virtual machine took %s milliseconds.",
                             TimeUnit.MILLISECONDS.convert(elapsedTime(startFreeze), TimeUnit.NANOSECONDS)));
                 }
             } else {
@@ -219,7 +219,7 @@
             throw new CloudRuntimeException(e.getMessage());
         } finally {
             if (thawAnswer == null && freezeAnswer != null) {
-                s_logger.info(String.format("Freeze of virtual machine took %s miliseconds.", TimeUnit.MILLISECONDS
+                s_logger.info(String.format("Freeze of virtual machine took %s milliseconds.", TimeUnit.MILLISECONDS
                                                 .convert(elapsedTime(startFreeze), TimeUnit.NANOSECONDS)));
                 try {
                     thawAnswer = (FreezeThawVMAnswer) agentMgr.send(hostId, thawCmd);
diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml
index 42b1b39..acaa38c 100644
--- a/engine/storage/volume/pom.xml
+++ b/engine/storage/volume/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-engine</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/engine/userdata/cloud-init/pom.xml b/engine/userdata/cloud-init/pom.xml
new file mode 100644
index 0000000..7e641bd
--- /dev/null
+++ b/engine/userdata/cloud-init/pom.xml
@@ -0,0 +1,36 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<modelVersion>4.0.0</modelVersion>
+<artifactId>cloud-engine-userdata-cloud-init</artifactId>
+<name>Apache CloudStack Engine Cloud-Init Userdata Component</name>
+<parent>
+    <artifactId>cloud-engine</artifactId>
+    <groupId>org.apache.cloudstack</groupId>
+    <version>4.19.0.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+</parent>
+<dependencies>
+    <dependency>
+        <groupId>org.apache.cloudstack</groupId>
+        <artifactId>cloud-engine-userdata</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+</dependencies>
+</project>
diff --git a/engine/userdata/cloud-init/src/main/java/org/apache/cloudstack/userdata/CloudInitUserDataProvider.java b/engine/userdata/cloud-init/src/main/java/org/apache/cloudstack/userdata/CloudInitUserDataProvider.java
new file mode 100644
index 0000000..65996f1
--- /dev/null
+++ b/engine/userdata/cloud-init/src/main/java/org/apache/cloudstack/userdata/CloudInitUserDataProvider.java
@@ -0,0 +1,286 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES 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.userdata;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+import java.util.zip.GZIPInputStream;
+
+import javax.mail.BodyPart;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.sun.mail.util.BASE64DecoderStream;
+
+public class CloudInitUserDataProvider extends AdapterBase implements UserDataProvider {
+
+    protected enum FormatType {
+        CLOUD_CONFIG, BASH_SCRIPT, MIME, CLOUD_BOOTHOOK, INCLUDE_FILE
+    }
+
+    private static final String CLOUD_CONFIG_CONTENT_TYPE = "text/cloud-config";
+    private static final String BASH_SCRIPT_CONTENT_TYPE = "text/x-shellscript";
+    private static final String INCLUDE_FILE_CONTENT_TYPE = "text/x-include-url";
+    private static final String CLOUD_BOOTHOOK_CONTENT_TYPE = "text/cloud-boothook";
+
+    private static final Map<FormatType, String> formatContentTypeMap = Map.ofEntries(
+            Map.entry(FormatType.CLOUD_CONFIG, CLOUD_CONFIG_CONTENT_TYPE),
+            Map.entry(FormatType.BASH_SCRIPT, BASH_SCRIPT_CONTENT_TYPE),
+            Map.entry(FormatType.CLOUD_BOOTHOOK, CLOUD_BOOTHOOK_CONTENT_TYPE),
+            Map.entry(FormatType.INCLUDE_FILE, INCLUDE_FILE_CONTENT_TYPE)
+    );
+
+    private static final Logger LOGGER = Logger.getLogger(CloudInitUserDataProvider.class);
+
+    private static final Session session = Session.getDefaultInstance(new Properties());
+
+    @Override
+    public String getName() {
+        return "cloud-init";
+    }
+
+    protected boolean isGZipped(String encodedUserdata) {
+        if (StringUtils.isEmpty(encodedUserdata)) {
+            return false;
+        }
+        byte[] data = Base64.decodeBase64(encodedUserdata);
+        if (data.length < 2) {
+            return false;
+        }
+        int magic = data[0] & 0xff | ((data[1] << 8) & 0xff00);
+        return magic == GZIPInputStream.GZIP_MAGIC;
+    }
+
+    protected String extractUserDataHeader(String userdata) {
+        List<String> lines = Arrays.stream(userdata.split("\n"))
+                .filter(x -> (x.startsWith("#") && !x.startsWith("##")) || (x.startsWith("Content-Type:")))
+                .collect(Collectors.toList());
+        if (CollectionUtils.isEmpty(lines)) {
+            throw new CloudRuntimeException("Failed to detect the user data format type as it " +
+                    "does not contain a header");
+        }
+        return lines.get(0);
+    }
+
+    protected FormatType mapUserDataHeaderToFormatType(String header) {
+        if (header.equalsIgnoreCase("#cloud-config")) {
+            return FormatType.CLOUD_CONFIG;
+        } else if (header.startsWith("#!")) {
+            return FormatType.BASH_SCRIPT;
+        } else if (header.equalsIgnoreCase("#cloud-boothook")) {
+            return FormatType.CLOUD_BOOTHOOK;
+        } else if (header.startsWith("#include")) {
+            return FormatType.INCLUDE_FILE;
+        } else if (header.startsWith("Content-Type:")) {
+            return FormatType.MIME;
+        } else {
+            String msg = String.format("Cannot recognise the user data format type from the header line: %s." +
+                    "Supported types are: cloud-config, bash script, cloud-boothook, include file or MIME", header);
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
+    /**
+     * Detect the user data type
+     * Reference: <a href="https://canonical-cloud-init.readthedocs-hosted.com/en/latest/explanation/format.html#user-data-formats" />
+     */
+    protected FormatType getUserDataFormatType(String userdata) {
+        if (StringUtils.isBlank(userdata)) {
+            String msg = "User data expected but provided empty user data";
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+
+        String header = extractUserDataHeader(userdata);
+        return mapUserDataHeaderToFormatType(header);
+    }
+
+    private String getContentType(String userData, FormatType formatType) throws MessagingException {
+        if (formatType == FormatType.MIME) {
+            NoIdMimeMessage msg = new NoIdMimeMessage(session, new ByteArrayInputStream(userData.getBytes()));
+            return msg.getContentType();
+        }
+        if (!formatContentTypeMap.containsKey(formatType)) {
+            throw new CloudRuntimeException(String.format("Cannot get the user data content type as " +
+                    "its format type %s is invalid", formatType.name()));
+        }
+        return formatContentTypeMap.get(formatType);
+    }
+
+    protected String getBodyPartContentAsString(BodyPart bodyPart) throws MessagingException, IOException {
+        Object content = bodyPart.getContent();
+        if (content instanceof BASE64DecoderStream) {
+            return new String(((BASE64DecoderStream)bodyPart.getContent()).readAllBytes());
+        } else if (content instanceof ByteArrayInputStream) {
+            return new String(((ByteArrayInputStream)bodyPart.getContent()).readAllBytes());
+        } else if (content instanceof String) {
+            return (String)bodyPart.getContent();
+        }
+        throw new CloudRuntimeException(String.format("Failed to get content for multipart data with content type: %s", getBodyPartContentType(bodyPart)));
+    }
+
+    private String getBodyPartContentType(BodyPart bodyPart) throws MessagingException {
+        String contentType = StringUtils.defaultString(bodyPart.getDataHandler().getContentType(), bodyPart.getContentType());
+        return  contentType.contains(";") ? contentType.substring(0, contentType.indexOf(';')) : contentType;
+    }
+
+    protected MimeBodyPart generateBodyPartMimeMessage(String userData, String contentType) throws MessagingException {
+        MimeBodyPart bodyPart = new MimeBodyPart();
+        bodyPart.setContent(userData, contentType);
+        bodyPart.addHeader("Content-Transfer-Encoding", "base64");
+        return bodyPart;
+    }
+
+    protected MimeBodyPart generateBodyPartMimeMessage(String userData, FormatType formatType) throws MessagingException {
+        return generateBodyPartMimeMessage(userData, getContentType(userData, formatType));
+    }
+
+    private Multipart getMessageContent(NoIdMimeMessage message) {
+        Multipart messageContent;
+        try {
+            messageContent = (MimeMultipart) message.getContent();
+        } catch (IOException | MessagingException e) {
+            messageContent = new MimeMultipart();
+        }
+        return messageContent;
+    }
+
+    private void addBodyPartToMultipart(Multipart existingMultipart, MimeBodyPart bodyPart) throws MessagingException, IOException {
+        boolean added = false;
+        final int existingCount = existingMultipart.getCount();
+        for (int j = 0; j < existingCount; ++j) {
+            MimeBodyPart existingBodyPart = (MimeBodyPart)existingMultipart.getBodyPart(j);
+            String existingContentType = getBodyPartContentType(existingBodyPart);
+            String newContentType = getBodyPartContentType(bodyPart);
+            if (existingContentType.equals(newContentType)) {
+                String existingContent = getBodyPartContentAsString(existingBodyPart);
+                String newContent = getBodyPartContentAsString(bodyPart);
+                // generating a combined content MimeBodyPart to replace
+                MimeBodyPart combinedBodyPart = generateBodyPartMimeMessage(
+                        simpleAppendSameFormatTypeUserData(existingContent, newContent), existingContentType);
+                existingMultipart.removeBodyPart(j);
+                existingMultipart.addBodyPart(combinedBodyPart, j);
+                added = true;
+                break;
+            }
+        }
+        if (!added) {
+            existingMultipart.addBodyPart(bodyPart);
+        }
+    }
+
+    private void addBodyPartsToMessageContentFromUserDataContent(Multipart existingMultipart,
+                                                                 NoIdMimeMessage msgFromUserdata) throws MessagingException, IOException {
+        MimeMultipart newMultipart = (MimeMultipart)msgFromUserdata.getContent();
+        final int existingCount = existingMultipart.getCount();
+        final int newCount = newMultipart.getCount();
+        for (int i = 0; i < newCount; ++i) {
+            BodyPart bodyPart = newMultipart.getBodyPart(i);
+            if (existingCount == 0) {
+                existingMultipart.addBodyPart(bodyPart);
+                continue;
+            }
+            addBodyPartToMultipart(existingMultipart, (MimeBodyPart)bodyPart);
+        }
+    }
+
+    private NoIdMimeMessage createMultipartMessageAddingUserdata(String userData, FormatType formatType,
+                                                           NoIdMimeMessage message) throws MessagingException, IOException {
+        NoIdMimeMessage newMessage = new NoIdMimeMessage(session);
+        Multipart messageContent = getMessageContent(message);
+
+        if (formatType == FormatType.MIME) {
+            NoIdMimeMessage msgFromUserdata = new NoIdMimeMessage(session, new ByteArrayInputStream(userData.getBytes()));
+            addBodyPartsToMessageContentFromUserDataContent(messageContent, msgFromUserdata);
+        } else {
+            MimeBodyPart part = generateBodyPartMimeMessage(userData, formatType);
+            addBodyPartToMultipart(messageContent, part);
+        }
+        newMessage.setContent(messageContent);
+        return newMessage;
+    }
+
+    private String simpleAppendSameFormatTypeUserData(String userData1, String userData2) {
+        return String.format("%s\n\n%s", userData1, userData2.substring(userData2.indexOf('\n')+1));
+    }
+
+    private void checkGzipAppend(String encodedUserData1, String encodedUserData2) {
+        if (isGZipped(encodedUserData1) || isGZipped(encodedUserData2)) {
+            throw new CloudRuntimeException("Gzipped user data can not be used together with other user data formats");
+        }
+    }
+
+    @Override
+    public String appendUserData(String encodedUserData1, String encodedUserData2) {
+        try {
+            checkGzipAppend(encodedUserData1, encodedUserData2);
+            String userData1 = new String(Base64.decodeBase64(encodedUserData1));
+            String userData2 = new String(Base64.decodeBase64(encodedUserData2));
+            FormatType formatType1 = getUserDataFormatType(userData1);
+            FormatType formatType2 = getUserDataFormatType(userData2);
+            if (formatType1.equals(formatType2) && List.of(FormatType.CLOUD_CONFIG, FormatType.BASH_SCRIPT).contains(formatType1)) {
+                return simpleAppendSameFormatTypeUserData(userData1, userData2);
+            }
+            NoIdMimeMessage message = new NoIdMimeMessage(session);
+            message = createMultipartMessageAddingUserdata(userData1, formatType1, message);
+            message = createMultipartMessageAddingUserdata(userData2, formatType2, message);
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            message.writeTo(output);
+            return output.toString();
+        } catch (MessagingException | IOException | CloudRuntimeException e) {
+            String msg = String.format("Error attempting to merge user data as a multipart user data. " +
+                    "Reason: %s", e.getMessage());
+            LOGGER.error(msg, e);
+            throw new CloudRuntimeException(msg, e);
+        }
+    }
+
+    /* This is a wrapper class just to remove Message-ID header from the resultant
+       multipart data which may contain server details.
+     */
+    private class NoIdMimeMessage extends MimeMessage {
+        NoIdMimeMessage (Session session) {
+            super(session);
+        }
+        NoIdMimeMessage (Session session, InputStream is) throws MessagingException {
+            super(session, is);
+        }
+        @Override
+        protected void updateMessageID() throws MessagingException {
+            removeHeader("Message-ID");
+        }
+    }
+}
diff --git a/engine/userdata/cloud-init/src/main/resources/META-INF/cloudstack/core/spring-userdata-cloud-init-context.xml b/engine/userdata/cloud-init/src/main/resources/META-INF/cloudstack/core/spring-userdata-cloud-init-context.xml
new file mode 100644
index 0000000..742398e
--- /dev/null
+++ b/engine/userdata/cloud-init/src/main/resources/META-INF/cloudstack/core/spring-userdata-cloud-init-context.xml
@@ -0,0 +1,27 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
+>
+    <bean id="cloudInitUserDataProvider" class="org.apache.cloudstack.userdata.CloudInitUserDataProvider">
+        <property name="name" value="cloud-init" />
+    </bean>
+</beans>
diff --git a/engine/userdata/cloud-init/src/test/java/org/apache/cloudstack/userdata/CloudInitUserDataProviderTest.java b/engine/userdata/cloud-init/src/test/java/org/apache/cloudstack/userdata/CloudInitUserDataProviderTest.java
new file mode 100644
index 0000000..4ca9fb7
--- /dev/null
+++ b/engine/userdata/cloud-init/src/test/java/org/apache/cloudstack/userdata/CloudInitUserDataProviderTest.java
@@ -0,0 +1,206 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.userdata;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Properties;
+import java.util.zip.GZIPOutputStream;
+
+import javax.mail.BodyPart;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class CloudInitUserDataProviderTest {
+
+    private final CloudInitUserDataProvider provider = new CloudInitUserDataProvider();
+    private final static String CLOUD_CONFIG_USERDATA = "## template: jinja\n" +
+            "#cloud-config\n" +
+            "runcmd:\n" +
+            "   - echo 'TestVariable {{ ds.meta_data.variable1 }}' >> /tmp/variable\n" +
+            "   - echo 'Hostname {{ ds.meta_data.public_hostname }}' > /tmp/hostname";
+    private final static String CLOUD_CONFIG_USERDATA1 = "#cloud-config\n" +
+            "password: atomic\n" +
+            "chpasswd: { expire: False }\n" +
+            "ssh_pwauth: True";
+    private final static String SHELL_SCRIPT_USERDATA = "#!/bin/bash\n" +
+            "date > /provisioned";
+    private final static String SHELL_SCRIPT_USERDATA1 = "#!/bin/bash\n" +
+            "mkdir /tmp/test";
+    private final static String SINGLE_BODYPART_CLOUDCONFIG_MULTIPART_USERDATA =
+            "Content-Type: multipart/mixed; boundary=\"//\"\n" +
+            "MIME-Version: 1.0\n" +
+            "\n" +
+            "--//\n" +
+            "Content-Type: text/cloud-config; charset=\"us-ascii\"\n" +
+            "MIME-Version: 1.0\n" +
+            "Content-Transfer-Encoding: 7bit\n" +
+            "Content-Disposition: attachment; filename=\"cloud-config.txt\"\n" +
+            "\n" +
+            "#cloud-config\n" +
+            "\n" +
+            "# Upgrade the instance on first boot\n" +
+            "# (ie run apt-get upgrade)\n" +
+            "#\n" +
+            "# Default: false\n" +
+            "# Aliases: apt_upgrade\n" +
+            "package_upgrade: true";
+    private static final Session session = Session.getDefaultInstance(new Properties());
+
+    @Test
+    public void testGetUserDataFormatType() {
+        CloudInitUserDataProvider.FormatType type = provider.getUserDataFormatType(CLOUD_CONFIG_USERDATA);
+        Assert.assertEquals(CloudInitUserDataProvider.FormatType.CLOUD_CONFIG, type);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testGetUserDataFormatTypeNoHeader() {
+        String userdata = "password: password\nchpasswd: { expire: False }\nssh_pwauth: True";
+        provider.getUserDataFormatType(userdata);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testGetUserDataFormatTypeInvalidType() {
+        String userdata = "#invalid-type\n" +
+                "password: password\nchpasswd: { expire: False }\nssh_pwauth: True";
+        provider.getUserDataFormatType(userdata);
+    }
+
+    private MimeMultipart getCheckedMultipartFromMultipartData(String multipartUserData, int count) {
+        MimeMultipart multipart = null;
+        Assert.assertTrue(multipartUserData.contains("Content-Type: multipart"));
+        try {
+            MimeMessage msgFromUserdata = new MimeMessage(session,
+                    new ByteArrayInputStream(multipartUserData.getBytes()));
+            multipart = (MimeMultipart)msgFromUserdata.getContent();
+            Assert.assertEquals(count, multipart.getCount());
+        } catch (MessagingException | IOException e) {
+            Assert.fail(String.format("Failed with exception, %s", e.getMessage()));
+        }
+        return multipart;
+    }
+
+    @Test
+    public void testAppendUserData() {
+        String multipartUserData = provider.appendUserData(Base64.encodeBase64String(CLOUD_CONFIG_USERDATA1.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA.getBytes()));
+        getCheckedMultipartFromMultipartData(multipartUserData, 2);
+    }
+
+    @Test
+    public void testAppendSameShellScriptTypeUserData() {
+        String result = SHELL_SCRIPT_USERDATA + "\n\n" +
+                SHELL_SCRIPT_USERDATA1.replace("#!/bin/bash\n", "");
+        String appendUserData = provider.appendUserData(Base64.encodeBase64String(SHELL_SCRIPT_USERDATA.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA1.getBytes()));
+        Assert.assertEquals(result, appendUserData);
+    }
+
+    @Test
+    public void testAppendSameCloudConfigTypeUserData() {
+        String result = CLOUD_CONFIG_USERDATA + "\n\n" +
+                CLOUD_CONFIG_USERDATA1.replace("#cloud-config\n", "");
+        String appendUserData = provider.appendUserData(Base64.encodeBase64String(CLOUD_CONFIG_USERDATA.getBytes()),
+                Base64.encodeBase64String(CLOUD_CONFIG_USERDATA1.getBytes()));
+        Assert.assertEquals(result, appendUserData);
+    }
+
+    @Test
+    public void testAppendUserDataMIMETemplateData() {
+        String multipartUserData = provider.appendUserData(
+                Base64.encodeBase64String(SINGLE_BODYPART_CLOUDCONFIG_MULTIPART_USERDATA.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA.getBytes()));
+        getCheckedMultipartFromMultipartData(multipartUserData, 2);
+    }
+
+    @Test
+    public void testAppendUserDataExistingMultipartWithSameType() {
+        String templateData = provider.appendUserData(Base64.encodeBase64String(CLOUD_CONFIG_USERDATA1.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA.getBytes()));
+        String multipartUserData = provider.appendUserData(Base64.encodeBase64String(templateData.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA1.getBytes()));
+        String resultantShellScript = SHELL_SCRIPT_USERDATA + "\n\n" +
+                SHELL_SCRIPT_USERDATA1.replace("#!/bin/bash\n", "");
+        MimeMultipart mimeMultipart = getCheckedMultipartFromMultipartData(multipartUserData, 2);
+        try {
+            for (int i = 0; i < mimeMultipart.getCount(); ++i) {
+                BodyPart bodyPart = mimeMultipart.getBodyPart(i);
+                if (bodyPart.getContentType().startsWith("text/x-shellscript")) {
+                    Assert.assertEquals(resultantShellScript, provider.getBodyPartContentAsString(bodyPart));
+                } else if (bodyPart.getContentType().startsWith("text/cloud-config")) {
+                    Assert.assertEquals(CLOUD_CONFIG_USERDATA1, provider.getBodyPartContentAsString(bodyPart));
+                }
+            }
+        } catch (MessagingException | IOException | CloudRuntimeException e) {
+            Assert.fail(String.format("Failed with exception, %s", e.getMessage()));
+        }
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAppendUserDataInvalidUserData() {
+        String templateData = CLOUD_CONFIG_USERDATA1.replace("#cloud-config\n", "");
+        provider.appendUserData(Base64.encodeBase64String(templateData.getBytes()),
+                Base64.encodeBase64String(SHELL_SCRIPT_USERDATA.getBytes()));
+    }
+
+    @Test
+    public void testIsGzippedUserDataWithCloudConfigData() {
+        Assert.assertFalse(provider.isGZipped(CLOUD_CONFIG_USERDATA));
+    }
+
+    private String createBase64EncodedGzipDataAsString() throws IOException {
+        byte[] input = CLOUD_CONFIG_USERDATA.getBytes(StandardCharsets.ISO_8859_1);
+
+        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
+        GZIPOutputStream outputStream = new GZIPOutputStream(arrayOutputStream);
+        outputStream.write(input,0, input.length);
+        outputStream.close();
+
+        return Base64.encodeBase64String(arrayOutputStream.toByteArray());
+    }
+
+    @Test
+    public void testIsGzippedUserDataWithValidGzipData() {
+        try {
+            String gzipped = createBase64EncodedGzipDataAsString();
+            Assert.assertTrue(provider.isGZipped(gzipped));
+        } catch (IOException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testAppendUserDataWithGzippedData() {
+        try {
+            provider.appendUserData(Base64.encodeBase64String(CLOUD_CONFIG_USERDATA.getBytes()),
+                    createBase64EncodedGzipDataAsString());
+            Assert.fail("Gzipped data shouldn't be appended with other data");
+        } catch (IOException e) {
+            Assert.fail("Exception encountered: " + e.getMessage());
+        }
+    }
+}
diff --git a/engine/userdata/pom.xml b/engine/userdata/pom.xml
new file mode 100644
index 0000000..75475b2
--- /dev/null
+++ b/engine/userdata/pom.xml
@@ -0,0 +1,53 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>cloud-engine-userdata</artifactId>
+    <name>Apache CloudStack Engine Userdata Component</name>
+    <parent>
+        <groupId>org.apache.cloudstack</groupId>
+        <artifactId>cloud-engine</artifactId>
+        <version>4.19.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-engine-components-api</artifactId>
+            <version>4.19.0.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataManagerImpl.java b/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataManagerImpl.java
new file mode 100644
index 0000000..91f24fe
--- /dev/null
+++ b/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataManagerImpl.java
@@ -0,0 +1,138 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.userdata;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+
+import com.cloud.configuration.ConfigurationManager;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class UserDataManagerImpl extends ManagerBase implements UserDataManager {
+
+
+    private static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
+    private static final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES;
+    private static final int NUM_OF_2K_BLOCKS = 512;
+    private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES;
+    private List<UserDataProvider> userDataProviders;
+    private static Map<String, UserDataProvider> userDataProvidersMap = new HashMap<>();
+
+    public void setUserDataProviders(final List<UserDataProvider> userDataProviders) {
+        this.userDataProviders = userDataProviders;
+    }
+
+    private void initializeUserdataProvidersMap() {
+        if (userDataProviders != null) {
+            for (final UserDataProvider provider : userDataProviders) {
+                userDataProvidersMap.put(provider.getName().toLowerCase(), provider);
+            }
+        }
+    }
+
+    @Override
+    public boolean start() {
+        initializeUserdataProvidersMap();
+        return true;
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return UserDataManagerImpl.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey[] {};
+    }
+
+    protected UserDataProvider getUserdataProvider(String name) {
+        if (StringUtils.isEmpty(name)) {
+            // Use cloud-init as the default userdata provider
+            name = "cloud-init";
+        }
+        if (!userDataProvidersMap.containsKey(name)) {
+            throw new CloudRuntimeException("Failed to find userdata provider by the name: " + name);
+        }
+        return userDataProvidersMap.get(name);
+    }
+
+    @Override
+    public String concatenateUserData(String userdata1, String userdata2, String userdataProvider) {
+        UserDataProvider provider = getUserdataProvider(userdataProvider);
+        String appendUserData = provider.appendUserData(userdata1, userdata2);
+        return Base64.encodeBase64String(appendUserData.getBytes());
+    }
+
+    @Override
+    public String validateUserData(String userData, BaseCmd.HTTPMethod httpmethod) {
+        byte[] decodedUserData = null;
+        if (userData != null) {
+
+            if (userData.contains("%")) {
+                try {
+                    userData = URLDecoder.decode(userData, "UTF-8");
+                } catch (UnsupportedEncodingException e) {
+                    throw new InvalidParameterValueException("Url decoding of userdata failed.");
+                }
+            }
+
+            if (!Base64.isBase64(userData)) {
+                throw new InvalidParameterValueException("User data is not base64 encoded");
+            }
+            // If GET, use 4K. If POST, support up to 1M.
+            if (httpmethod.equals(BaseCmd.HTTPMethod.GET)) {
+                decodedUserData = validateAndDecodeByHTTPMethod(userData, MAX_HTTP_GET_LENGTH, BaseCmd.HTTPMethod.GET);
+            } else if (httpmethod.equals(BaseCmd.HTTPMethod.POST)) {
+                decodedUserData = validateAndDecodeByHTTPMethod(userData, MAX_HTTP_POST_LENGTH, BaseCmd.HTTPMethod.POST);
+            }
+
+            if (decodedUserData == null || decodedUserData.length < 1) {
+                throw new InvalidParameterValueException("User data is too short");
+            }
+            // Re-encode so that the '=' paddings are added if necessary since 'isBase64' does not require it, but python does on the VR.
+            return Base64.encodeBase64String(decodedUserData);
+        }
+        return null;
+    }
+
+    private byte[] validateAndDecodeByHTTPMethod(String userData, int maxHTTPLength, BaseCmd.HTTPMethod httpMethod) {
+        byte[] decodedUserData = null;
+
+        if (userData.length() >= maxHTTPLength) {
+            throw new InvalidParameterValueException(String.format("User data is too long for an http %s request", httpMethod.toString()));
+        }
+        if (userData.length() > ConfigurationManager.VM_USERDATA_MAX_LENGTH.value()) {
+            throw new InvalidParameterValueException("User data has exceeded configurable max length : " + ConfigurationManager.VM_USERDATA_MAX_LENGTH.value());
+        }
+        decodedUserData = Base64.decodeBase64(userData.getBytes());
+        if (decodedUserData.length > maxHTTPLength) {
+            throw new InvalidParameterValueException(String.format("User data is too long for http %s request", httpMethod.toString()));
+        }
+        return decodedUserData;
+    }
+}
diff --git a/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataProvider.java b/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataProvider.java
new file mode 100644
index 0000000..4cdcd51
--- /dev/null
+++ b/engine/userdata/src/main/java/org/apache/cloudstack/userdata/UserDataProvider.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.userdata;
+
+public interface UserDataProvider {
+    String getName();
+
+    /**
+     * Append user data into a single user data.
+     * NOTE: userData1 and userData2 are Base64 encoded user data strings
+     * @return a non-encrypted string containing both user data inputs
+     */
+    String appendUserData(String encodedUserData1, String encodedUserData2);
+}
diff --git a/engine/userdata/src/main/resources/META-INF/cloudstack/core/spring-engine-userdata-core-context.xml b/engine/userdata/src/main/resources/META-INF/cloudstack/core/spring-engine-userdata-core-context.xml
new file mode 100644
index 0000000..3e06704
--- /dev/null
+++ b/engine/userdata/src/main/resources/META-INF/cloudstack/core/spring-engine-userdata-core-context.xml
@@ -0,0 +1,34 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd
+                      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
+                      http://www.springframework.org/schema/context
+                      http://www.springframework.org/schema/context/spring-context.xsd"
+                      >
+
+    <bean id="userDataManager" class="org.apache.cloudstack.userdata.UserDataManagerImpl">
+        <property name="userDataProviders" value="#{userDataProvidersRegistry.registered}" />
+    </bean>
+ 
+</beans>
\ No newline at end of file
diff --git a/engine/userdata/src/test/java/org/apache/cloudstack/userdata/UserDataManagerImplTest.java b/engine/userdata/src/test/java/org/apache/cloudstack/userdata/UserDataManagerImplTest.java
new file mode 100644
index 0000000..67e7b38
--- /dev/null
+++ b/engine/userdata/src/test/java/org/apache/cloudstack/userdata/UserDataManagerImplTest.java
@@ -0,0 +1,59 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES 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.userdata;
+
+import static org.junit.Assert.assertEquals;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.cloudstack.api.BaseCmd;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class UserDataManagerImplTest {
+
+    @Spy
+    @InjectMocks
+    private UserDataManagerImpl userDataManager;
+
+    @Test
+    public void testValidateBase64WithoutPadding() {
+        // fo should be encoded in base64 either as Zm8 or Zm8=
+        String encodedUserdata = "Zm8";
+        String encodedUserdataWithPadding = "Zm8=";
+
+        // Verify that we accept both but return the padded version
+        assertEquals("validate return the value with padding", encodedUserdataWithPadding, userDataManager.validateUserData(encodedUserdata, BaseCmd.HTTPMethod.GET));
+        assertEquals("validate return the value with padding", encodedUserdataWithPadding, userDataManager.validateUserData(encodedUserdataWithPadding, BaseCmd.HTTPMethod.GET));
+    }
+
+    @Test
+    public void testValidateUrlEncodedBase64() {
+        // fo should be encoded in base64 either as Zm8 or Zm8=
+        String encodedUserdata = "Zm+8/w8=";
+        String urlEncodedUserdata = java.net.URLEncoder.encode(encodedUserdata, StandardCharsets.UTF_8);
+
+        // Verify that we accept both but return the padded version
+        assertEquals("validate return the value with padding", encodedUserdata, userDataManager.validateUserData(encodedUserdata, BaseCmd.HTTPMethod.GET));
+        assertEquals("validate return the value with padding", encodedUserdata, userDataManager.validateUserData(urlEncodedUserdata, BaseCmd.HTTPMethod.GET));
+    }
+
+}
diff --git a/framework/agent-lb/pom.xml b/framework/agent-lb/pom.xml
index 6737a4f..db75099 100644
--- a/framework/agent-lb/pom.xml
+++ b/framework/agent-lb/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <artifactId>cloudstack-framework</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/framework/ca/pom.xml b/framework/ca/pom.xml
index c49aea0..9e1cffc 100644
--- a/framework/ca/pom.xml
+++ b/framework/ca/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/framework/cluster/pom.xml b/framework/cluster/pom.xml
index 3d891f1..7cb8334 100644
--- a/framework/cluster/pom.xml
+++ b/framework/cluster/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostVO.java b/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostVO.java
index 121b939..2918ccd 100644
--- a/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostVO.java
+++ b/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostVO.java
@@ -125,6 +125,11 @@
     }
 
     @Override
+    public Class<?> getEntityType() {
+        return ManagementServerHost.class;
+    }
+
+    @Override
     public String getName() {
         return name;
     }
@@ -196,4 +201,14 @@
     public String toString() {
         return new StringBuilder("ManagementServer[").append("-").append(id).append("-").append(msid).append("-").append(state).append("]").toString();
     }
+
+    @Override
+    public long getDomainId() {
+        return 1L;
+    }
+
+    @Override
+    public long getAccountId() {
+        return 1L;
+    }
 }
diff --git a/framework/config/pom.xml b/framework/config/pom.xml
index d49eef4..7805869 100644
--- a/framework/config/pom.xml
+++ b/framework/config/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/db/pom.xml b/framework/db/pom.xml
index 73e304c..c3f99ae 100644
--- a/framework/db/pom.xml
+++ b/framework/db/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml
index 13c00b6..e4eb631 100644
--- a/framework/direct-download/pom.xml
+++ b/framework/direct-download/pom.xml
@@ -25,14 +25,14 @@
         <dependency>
             <groupId>org.apache.cloudstack</groupId>
             <artifactId>cloud-utils</artifactId>
-            <version>4.18.1.0-SNAPSHOT</version>
+            <version>4.19.0.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
     </dependencies>
     <parent>
         <artifactId>cloudstack-framework</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 </project>
\ No newline at end of file
diff --git a/framework/events/pom.xml b/framework/events/pom.xml
index 91023e6..050c445 100644
--- a/framework/events/pom.xml
+++ b/framework/events/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/ipc/pom.xml b/framework/ipc/pom.xml
index 05a18ba..02b508b 100644
--- a/framework/ipc/pom.xml
+++ b/framework/ipc/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/ipc/src/main/java/org/apache/cloudstack/framework/messagebus/MessageDetector.java b/framework/ipc/src/main/java/org/apache/cloudstack/framework/messagebus/MessageDetector.java
index c771d6b..4bcf9b1 100644
--- a/framework/ipc/src/main/java/org/apache/cloudstack/framework/messagebus/MessageDetector.java
+++ b/framework/ipc/src/main/java/org/apache/cloudstack/framework/messagebus/MessageDetector.java
@@ -31,15 +31,15 @@
         _subjects = null;
     }
 
-    public void waitAny(long timeoutInMiliseconds) {
-        if (timeoutInMiliseconds < 100) {
-            s_logger.warn("waitAny is passed with a too short time-out interval. " + timeoutInMiliseconds + "ms");
-            timeoutInMiliseconds = 100;
+    public void waitAny(long timeoutInMilliseconds) {
+        if (timeoutInMilliseconds < 100) {
+            s_logger.warn("waitAny is passed with a too short time-out interval. " + timeoutInMilliseconds + "ms");
+            timeoutInMilliseconds = 100;
         }
 
         synchronized (this) {
             try {
-                wait(timeoutInMiliseconds);
+                wait(timeoutInMilliseconds);
             } catch (InterruptedException e) {
                 s_logger.debug("[ignored] interrupted while waiting on any message.");
             }
diff --git a/framework/jobs/pom.xml b/framework/jobs/pom.xml
index a3d6b14..b00875e 100644
--- a/framework/jobs/pom.xml
+++ b/framework/jobs/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJob.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJob.java
index b8200bf..bde9b4a 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJob.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJob.java
@@ -90,6 +90,8 @@
     @Override
     Long getExecutingMsid();
 
+    void setExecutingMsid(Long msid);
+
     @Override
     Long getCompleteMsid();
 
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJobManager.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJobManager.java
index 8542407..9f50a54 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJobManager.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJobManager.java
@@ -117,12 +117,12 @@
      *
      * @param wakupTopicsOnMessageBus topic on message bus to wakeup the wait
      * @param checkIntervalInMilliSeconds time to break out wait for checking predicate condition
-     * @param timeoutInMiliseconds time out to break out the whole wait process
+     * @param timeoutInMilliseconds time out to break out the whole wait process
      * @param predicate
      * @return true, predicate condition is satisfied
      *             false, wait is timed out
      */
-    boolean waitAndCheck(AsyncJob job, String[] wakupTopicsOnMessageBus, long checkIntervalInMilliSeconds, long timeoutInMiliseconds, Predicate predicate);
+    boolean waitAndCheck(AsyncJob job, String[] wakupTopicsOnMessageBus, long checkIntervalInMilliSeconds, long timeoutInMilliseconds, Predicate predicate);
 
     AsyncJob queryJob(long jobId, boolean updatePollTime);
 
@@ -133,4 +133,14 @@
     List<AsyncJobVO> findFailureAsyncJobs(String... cmds);
 
     long countPendingJobs(String havingInfo, String... cmds);
+
+    // Returns the number of pending jobs for the given Management server msids.
+    // NOTE: This is the msid and NOT the id
+    long countPendingNonPseudoJobs(Long... msIds);
+
+    void enableAsyncJobs();
+
+    void disableAsyncJobs();
+
+    boolean isAsyncJobsEnabled();
 }
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDao.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDao.java
index 2696e10..9f7a4ad 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDao.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDao.java
@@ -46,4 +46,8 @@
     List<AsyncJobVO> getFailureJobsSinceLastMsStart(long msId, String... cmds);
 
     long countPendingJobs(String havingInfo, String... cmds);
+
+    // Returns the number of pending jobs for the given Management server msids.
+    // NOTE: This is the msid and NOT the id
+    long countPendingNonPseudoJobs(Long... msIds);
 }
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDaoImpl.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDaoImpl.java
index 7dd7343..1914ff7 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDaoImpl.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDaoImpl.java
@@ -48,6 +48,7 @@
     private final SearchBuilder<AsyncJobVO> expiringCompletedAsyncJobSearch;
     private final SearchBuilder<AsyncJobVO> failureMsidAsyncJobSearch;
     private final GenericSearchBuilder<AsyncJobVO, Long> asyncJobTypeSearch;
+    private final GenericSearchBuilder<AsyncJobVO, Long> pendingNonPseudoAsyncJobsSearch;
 
     public AsyncJobDaoImpl() {
         pendingAsyncJobSearch = createSearchBuilder();
@@ -103,6 +104,11 @@
         asyncJobTypeSearch.and("status", asyncJobTypeSearch.entity().getStatus(), SearchCriteria.Op.EQ);
         asyncJobTypeSearch.done();
 
+        pendingNonPseudoAsyncJobsSearch = createSearchBuilder(Long.class);
+        pendingNonPseudoAsyncJobsSearch.select(null, SearchCriteria.Func.COUNT, pendingNonPseudoAsyncJobsSearch.entity().getId());
+        pendingNonPseudoAsyncJobsSearch.and("instanceTypeNEQ", pendingNonPseudoAsyncJobsSearch.entity().getInstanceType(), SearchCriteria.Op.NEQ);
+        pendingNonPseudoAsyncJobsSearch.and("jobStatusEQ", pendingNonPseudoAsyncJobsSearch.entity().getStatus(), SearchCriteria.Op.EQ);
+        pendingNonPseudoAsyncJobsSearch.and("executingMsidIN", pendingNonPseudoAsyncJobsSearch.entity().getExecutingMsid(), SearchCriteria.Op.IN);
     }
 
     @Override
@@ -237,6 +243,20 @@
         return listBy(sc);
     }
 
+    // Returns the number of pending jobs for the given Management server msids.
+    // NOTE: This is the msid and NOT the id
+    @Override
+    public long countPendingNonPseudoJobs(Long... msIds) {
+        SearchCriteria<Long> sc = pendingNonPseudoAsyncJobsSearch.create();
+        sc.setParameters("instanceTypeNEQ", AsyncJobVO.PSEUDO_JOB_INSTANCE_TYPE);
+        sc.setParameters("jobStatusEQ", JobInfo.Status.IN_PROGRESS);
+        if (msIds != null) {
+            sc.setParameters("executingMsidIN", (Object[])msIds);
+        }
+        List<Long> results = customSearch(sc, null);
+        return results.get(0);
+    }
+
     @Override
     public long countPendingJobs(String havingInfo, String... cmds) {
         SearchCriteria<Long> sc = asyncJobTypeSearch.create();
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
index a963357..15fe75b 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
@@ -64,6 +64,7 @@
 
 import com.cloud.cluster.ClusterManagerListener;
 import org.apache.cloudstack.management.ManagementServerHost;
+
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.dao.SnapshotDao;
@@ -155,6 +156,8 @@
     private ExecutorService _apiJobExecutor;
     private ExecutorService _workerJobExecutor;
 
+    private boolean asyncJobsEnabled = true;
+
     @Override
     public String getConfigComponentName() {
         return AsyncJobManager.class.getSimpleName();
@@ -197,12 +200,21 @@
         return submitAsyncJob(job, false);
     }
 
+    private void checkShutdown() {
+        if (!isAsyncJobsEnabled()) {
+            throw new CloudRuntimeException("A shutdown has been triggered. Can not accept new jobs");
+        }
+    }
+
     @SuppressWarnings("unchecked")
     @DB
     public long submitAsyncJob(AsyncJob job, boolean scheduleJobExecutionInContext) {
+        checkShutdown();
+
         @SuppressWarnings("rawtypes")
         GenericDao dao = GenericDaoBase.getDao(job.getClass());
         job.setInitMsid(getMsid());
+        job.setExecutingMsid(getMsid());
         job.setSyncSource(null);        // no sync source originally
         dao.persist(job);
 
@@ -218,6 +230,8 @@
     @Override
     @DB
     public long submitAsyncJob(final AsyncJob job, final String syncObjType, final long syncObjId) {
+        checkShutdown();
+
         try {
             @SuppressWarnings("rawtypes")
             final GenericDao dao = GenericDaoBase.getDao(job.getClass());
@@ -744,7 +758,7 @@
     }
 
     @Override
-    public boolean waitAndCheck(AsyncJob job, String[] wakeupTopicsOnMessageBus, long checkIntervalInMilliSeconds, long timeoutInMiliseconds, Predicate predicate) {
+    public boolean waitAndCheck(AsyncJob job, String[] wakeupTopicsOnMessageBus, long checkIntervalInMilliSeconds, long timeoutInMilliseconds, Predicate predicate) {
 
         MessageDetector msgDetector = new MessageDetector();
         String[] topics = Arrays.copyOf(wakeupTopicsOnMessageBus, wakeupTopicsOnMessageBus.length + 1);
@@ -753,7 +767,7 @@
         msgDetector.open(_messageBus, topics);
         try {
             long startTick = System.currentTimeMillis();
-            while (timeoutInMiliseconds < 0 || System.currentTimeMillis() - startTick < timeoutInMiliseconds) {
+            while (timeoutInMilliseconds < 0 || System.currentTimeMillis() - startTick < timeoutInMilliseconds) {
                 msgDetector.waitAny(checkIntervalInMilliSeconds);
                 job = _jobDao.findById(job.getId());
                 if (job != null && job.getStatus().done()) {
@@ -827,6 +841,11 @@
 
             protected void reallyRun() {
                 try {
+                    if (!isAsyncJobsEnabled()) {
+                        s_logger.info("A shutdown has been triggered. Not executing any async job");
+                        return;
+                    }
+
                     List<SyncQueueItemVO> l = _queueMgr.dequeueFromAny(getMsid(), MAX_ONETIME_SCHEDULE_SIZE);
                     if (l != null && l.size() > 0) {
                         for (SyncQueueItemVO item : l) {
@@ -1171,4 +1190,26 @@
     public long countPendingJobs(String havingInfo, String... cmds) {
         return _jobDao.countPendingJobs(havingInfo, cmds);
     }
+
+    // Returns the number of pending jobs for the given Management server msids.
+    // NOTE: This is the msid and NOT the id
+    @Override
+    public long countPendingNonPseudoJobs(Long... msIds) {
+    return _jobDao.countPendingNonPseudoJobs(msIds);
+    }
+
+    @Override
+    public void enableAsyncJobs() {
+        this.asyncJobsEnabled = true;
+    }
+
+    @Override
+    public void disableAsyncJobs() {
+        this.asyncJobsEnabled = false;
+    }
+
+    @Override
+    public boolean isAsyncJobsEnabled() {
+        return asyncJobsEnabled;
+    }
 }
diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobVO.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobVO.java
index 8f3c033..6b85ae2 100644
--- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobVO.java
+++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobVO.java
@@ -294,6 +294,7 @@
         return executingMsid;
     }
 
+    @Override
     public void setExecutingMsid(Long executingMsid) {
         this.executingMsid = executingMsid;
     }
diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml
index c4cfeed..b8ac33e 100644
--- a/framework/managed-context/pom.xml
+++ b/framework/managed-context/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/pom.xml b/framework/pom.xml
index 226a3c1..679796c 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <build>
         <plugins>
diff --git a/framework/quota/pom.xml b/framework/quota/pom.xml
index 1ad07c4..437577f 100644
--- a/framework/quota/pom.xml
+++ b/framework/quota/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/rest/pom.xml b/framework/rest/pom.xml
index 04a6130..5895ce0 100644
--- a/framework/rest/pom.xml
+++ b/framework/rest/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>cloud-framework-rest</artifactId>
diff --git a/framework/security/pom.xml b/framework/security/pom.xml
index 7a83cf5..956b1f9 100644
--- a/framework/security/pom.xml
+++ b/framework/security/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-framework</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/spring/lifecycle/pom.xml b/framework/spring/lifecycle/pom.xml
index da74ff2..b27c583 100644
--- a/framework/spring/lifecycle/pom.xml
+++ b/framework/spring/lifecycle/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/framework/spring/module/pom.xml b/framework/spring/module/pom.xml
index d56fdf6..cdeb4d3 100644
--- a/framework/spring/module/pom.xml
+++ b/framework/spring/module/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/acl/dynamic-role-based/pom.xml b/plugins/acl/dynamic-role-based/pom.xml
index 3b9854e..786d69d 100644
--- a/plugins/acl/dynamic-role-based/pom.xml
+++ b/plugins/acl/dynamic-role-based/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/acl/project-role-based/pom.xml b/plugins/acl/project-role-based/pom.xml
index b673a35..fbbf688 100644
--- a/plugins/acl/project-role-based/pom.xml
+++ b/plugins/acl/project-role-based/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/acl/project-role-based/src/main/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java b/plugins/acl/project-role-based/src/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java
similarity index 86%
rename from plugins/acl/project-role-based/src/main/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java
rename to plugins/acl/project-role-based/src/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java
index 5505975..81061d3 100644
--- a/plugins/acl/project-role-based/src/main/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java
+++ b/plugins/acl/project-role-based/src/test/java/org/apache/cloudstack/acl/ProjectRoleBasedApiAccessCheckerTest.java
@@ -26,25 +26,24 @@
 import com.cloud.user.UserVO;
 
 import org.apache.cloudstack.context.CallContext;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 import junit.framework.TestCase;
+import org.mockito.junit.MockitoJUnitRunner;
 
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(CallContext.class)
+@RunWith(MockitoJUnitRunner.class)
 public class ProjectRoleBasedApiAccessCheckerTest extends TestCase {
     @Mock
     ProjectAccountDao projectAccountDaoMock;
@@ -63,12 +62,21 @@
 
     List<String> apiNames = new ArrayList<>(Arrays.asList("apiName"));
 
+    MockedStatic<CallContext> callContextMocked;
+
     @Before
     public void setup() {
-
+        callContextMocked = Mockito.mockStatic(CallContext.class);
+        callContextMocked.when(CallContext::current).thenReturn(callContextMock);
         Mockito.doReturn(true).when(roleServiceMock).isEnabled();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        callContextMocked.close();
+    }
+
     public Project getTestProject() {
         return new ProjectVO("Teste", "Teste", 1L, 1L);
     }
@@ -88,8 +96,6 @@
 
     @Test
     public void getApisAllowedToUserTestProjectIsNullShouldReturnUnchangedApiList() {
-        PowerMockito.mockStatic(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
 
         Mockito.doReturn(null).when(callContextMock).getProject();
 
@@ -99,8 +105,6 @@
 
     @Test (expected = PermissionDeniedException.class)
     public void getApisAllowedToUserTestProjectAccountIsNullThrowPermissionDeniedException() {
-        PowerMockito.mockStatic(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
 
         Mockito.when(callContextMock.getProject()).thenReturn(getTestProject());
         Mockito.when(projectAccountDaoMock.findByProjectIdAccountId(Mockito.anyLong(), Mockito.anyLong())).thenReturn(null);
@@ -111,8 +115,6 @@
 
     @Test
     public void getApisAllowedToUserTestProjectAccountHasAdminRoleReturnsUnchangedApiList() {
-        PowerMockito.mockStatic(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
 
         Mockito.doReturn(getTestProject()).when(callContextMock).getProject();
         Mockito.doReturn(projectAccountVOMock).when(projectAccountDaoMock).findByProjectIdUserId(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
@@ -124,8 +126,6 @@
 
     @Test
     public void getApisAllowedToUserTestProjectAccountNotPermittedForTheApiListShouldReturnEmptyList() {
-        PowerMockito.mockStatic(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
 
         Mockito.doReturn(getTestProject()).when(callContextMock).getProject();
         Mockito.doReturn(projectAccountVOMock).when(projectAccountDaoMock).findByProjectIdUserId(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
@@ -139,8 +139,6 @@
 
     @Test
     public void getApisAllowedToUserTestProjectAccountPermittedForTheApiListShouldReturnTheSameList() {
-        PowerMockito.mockStatic(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
 
         Mockito.doReturn(getTestProject()).when(callContextMock).getProject();
         Mockito.doReturn(projectAccountVOMock).when(projectAccountDaoMock).findByProjectIdUserId(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
diff --git a/plugins/acl/project-role-based/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/plugins/acl/project-role-based/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d4
--- /dev/null
+++ b/plugins/acl/project-role-based/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/plugins/acl/static-role-based/pom.xml b/plugins/acl/static-role-based/pom.xml
index 937dc27..b6bb633 100644
--- a/plugins/acl/static-role-based/pom.xml
+++ b/plugins/acl/static-role-based/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/affinity-group-processors/explicit-dedication/pom.xml b/plugins/affinity-group-processors/explicit-dedication/pom.xml
index 584ca25..870cb40 100644
--- a/plugins/affinity-group-processors/explicit-dedication/pom.xml
+++ b/plugins/affinity-group-processors/explicit-dedication/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/affinity-group-processors/host-affinity/pom.xml b/plugins/affinity-group-processors/host-affinity/pom.xml
index 721d0c4..f30e8ba 100644
--- a/plugins/affinity-group-processors/host-affinity/pom.xml
+++ b/plugins/affinity-group-processors/host-affinity/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/affinity-group-processors/host-anti-affinity/pom.xml b/plugins/affinity-group-processors/host-anti-affinity/pom.xml
index 74124fa..0e44868 100644
--- a/plugins/affinity-group-processors/host-anti-affinity/pom.xml
+++ b/plugins/affinity-group-processors/host-anti-affinity/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml
index 5b66d00..f6fbb9e 100644
--- a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml
+++ b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml
index a0ac53a..adb4de2 100644
--- a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml
+++ b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml
@@ -25,14 +25,14 @@
         <dependency>
             <groupId>org.apache.cloudstack</groupId>
             <artifactId>cloud-plugin-non-strict-host-affinity</artifactId>
-            <version>4.18.1.0-SNAPSHOT</version>
+            <version>4.19.0.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
     </dependencies>
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/alert-handlers/snmp-alerts/pom.xml b/plugins/alert-handlers/snmp-alerts/pom.xml
index a64d1f7..04e5de2 100644
--- a/plugins/alert-handlers/snmp-alerts/pom.xml
+++ b/plugins/alert-handlers/snmp-alerts/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <artifactId>cloudstack-plugins</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/alert-handlers/syslog-alerts/pom.xml b/plugins/alert-handlers/syslog-alerts/pom.xml
index 41ef708..48af97b 100644
--- a/plugins/alert-handlers/syslog-alerts/pom.xml
+++ b/plugins/alert-handlers/syslog-alerts/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <artifactId>cloudstack-plugins</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/api/discovery/pom.xml b/plugins/api/discovery/pom.xml
index 391019b..0026c4f 100644
--- a/plugins/api/discovery/pom.xml
+++ b/plugins/api/discovery/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/api/rate-limit/pom.xml b/plugins/api/rate-limit/pom.xml
index af81331..e951fe3 100644
--- a/plugins/api/rate-limit/pom.xml
+++ b/plugins/api/rate-limit/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/plugins/api/solidfire-intg-test/pom.xml b/plugins/api/solidfire-intg-test/pom.xml
index 7baf426..f33aa33 100644
--- a/plugins/api/solidfire-intg-test/pom.xml
+++ b/plugins/api/solidfire-intg-test/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/api/vmware-sioc/pom.xml b/plugins/api/vmware-sioc/pom.xml
index b9c4973..8dd0cfc 100644
--- a/plugins/api/vmware-sioc/pom.xml
+++ b/plugins/api/vmware-sioc/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/backup/dummy/pom.xml b/plugins/backup/dummy/pom.xml
index 05cec8f..3c3d5cf 100644
--- a/plugins/backup/dummy/pom.xml
+++ b/plugins/backup/dummy/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <artifactId>cloudstack-plugins</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/backup/networker/pom.xml b/plugins/backup/networker/pom.xml
index 17fb70a..1672409 100644
--- a/plugins/backup/networker/pom.xml
+++ b/plugins/backup/networker/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <artifactId>cloudstack-plugins</artifactId>
         <groupId>org.apache.cloudstack</groupId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/backup/veeam/pom.xml b/plugins/backup/veeam/pom.xml
index e5750a2..ee59fa8 100644
--- a/plugins/backup/veeam/pom.xml
+++ b/plugins/backup/veeam/pom.xml
@@ -23,7 +23,7 @@
   <parent>
     <artifactId>cloudstack-plugins</artifactId>
     <groupId>org.apache.cloudstack</groupId>
-    <version>4.18.1.0-SNAPSHOT</version>
+    <version>4.19.0.0-SNAPSHOT</version>
     <relativePath>../../pom.xml</relativePath>
   </parent>
 
diff --git a/plugins/ca/root-ca/pom.xml b/plugins/ca/root-ca/pom.xml
index 676709b..824d423 100644
--- a/plugins/ca/root-ca/pom.xml
+++ b/plugins/ca/root-ca/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/database/mysql-ha/pom.xml b/plugins/database/mysql-ha/pom.xml
index 80cdbf0..82806f5 100644
--- a/plugins/database/mysql-ha/pom.xml
+++ b/plugins/database/mysql-ha/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml
index 8336f93..129f343 100644
--- a/plugins/database/quota/pom.xml
+++ b/plugins/database/quota/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/dedicated-resources/pom.xml b/plugins/dedicated-resources/pom.xml
index 435879d..084e8fd 100644
--- a/plugins/dedicated-resources/pom.xml
+++ b/plugins/dedicated-resources/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/deployment-planners/implicit-dedication/pom.xml b/plugins/deployment-planners/implicit-dedication/pom.xml
index fb6dc0a..0d78434 100644
--- a/plugins/deployment-planners/implicit-dedication/pom.xml
+++ b/plugins/deployment-planners/implicit-dedication/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/deployment-planners/user-concentrated-pod/pom.xml b/plugins/deployment-planners/user-concentrated-pod/pom.xml
index d64800a..2fe6404 100644
--- a/plugins/deployment-planners/user-concentrated-pod/pom.xml
+++ b/plugins/deployment-planners/user-concentrated-pod/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/deployment-planners/user-dispersing/pom.xml b/plugins/deployment-planners/user-dispersing/pom.xml
index 584ec2c..3d7682a 100644
--- a/plugins/deployment-planners/user-dispersing/pom.xml
+++ b/plugins/deployment-planners/user-dispersing/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/event-bus/inmemory/pom.xml b/plugins/event-bus/inmemory/pom.xml
index 22497a8..2ed5a91 100644
--- a/plugins/event-bus/inmemory/pom.xml
+++ b/plugins/event-bus/inmemory/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/event-bus/kafka/pom.xml b/plugins/event-bus/kafka/pom.xml
index 6836798..6756ea2 100644
--- a/plugins/event-bus/kafka/pom.xml
+++ b/plugins/event-bus/kafka/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/event-bus/rabbitmq/pom.xml b/plugins/event-bus/rabbitmq/pom.xml
index a8472be..6019fb9 100644
--- a/plugins/event-bus/rabbitmq/pom.xml
+++ b/plugins/event-bus/rabbitmq/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/ha-planners/skip-heurestics/pom.xml b/plugins/ha-planners/skip-heurestics/pom.xml
index 27091c0..59a2600 100644
--- a/plugins/ha-planners/skip-heurestics/pom.xml
+++ b/plugins/ha-planners/skip-heurestics/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/host-allocators/random/pom.xml b/plugins/host-allocators/random/pom.xml
index 111606a..8c8bd99 100644
--- a/plugins/host-allocators/random/pom.xml
+++ b/plugins/host-allocators/random/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml
index 83430fc..6150bed 100755
--- a/plugins/hypervisors/baremetal/pom.xml
+++ b/plugins/hypervisors/baremetal/pom.xml
@@ -22,7 +22,7 @@
     <parent>

         <groupId>org.apache.cloudstack</groupId>

         <artifactId>cloudstack-plugins</artifactId>

-        <version>4.18.1.0-SNAPSHOT</version>

+        <version>4.19.0.0-SNAPSHOT</version>

         <relativePath>../../pom.xml</relativePath>

     </parent>

     <artifactId>cloud-plugin-hypervisor-baremetal</artifactId>

diff --git a/plugins/hypervisors/hyperv/pom.xml b/plugins/hypervisors/hyperv/pom.xml
index 8962fe4..946e5f4 100644
--- a/plugins/hypervisors/hyperv/pom.xml
+++ b/plugins/hypervisors/hyperv/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <properties>
diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml
index a74cb92..9265d66 100644
--- a/plugins/hypervisors/kvm/pom.xml
+++ b/plugins/hypervisors/kvm/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 3f711fa..f4ce29c 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -322,6 +322,7 @@
     private String _dcId;
     private String _clusterId;
     private final Properties _uefiProperties = new Properties();
+    private String hostHealthCheckScriptPath;
 
     private long _hvVersion;
     private Duration _timeout;
@@ -717,6 +718,10 @@
         NATIVE, OPENVSWITCH, TUNGSTEN
     }
 
+    protected enum HealthCheckResult {
+        SUCCESS, FAILURE, IGNORE
+    }
+
     protected BridgeType _bridgeType;
 
     protected StorageSubsystemCommandHandler storageHandler;
@@ -943,6 +948,12 @@
             throw new ConfigurationException("Unable to find the ovs-pvlan-kvm-vm.sh");
         }
 
+        hostHealthCheckScriptPath = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEALTH_CHECK_SCRIPT_PATH);
+        if (StringUtils.isNotBlank(hostHealthCheckScriptPath) && !new File(hostHealthCheckScriptPath).exists()) {
+            s_logger.info(String.format("Unable to find the host health check script at: %s, " +
+                    "discarding it", hostHealthCheckScriptPath));
+        }
+
         setupTungstenVrouterPath = Script.findScript(tungstenScriptsDir, "setup_tungsten_vrouter.sh");
         if (setupTungstenVrouterPath == null) {
             throw new ConfigurationException("Unable to find the setup_tungsten_vrouter.sh");
@@ -2832,7 +2843,7 @@
         for (int i = 0; i < nics.length; i++) {
             for (final NicTO nic : vmSpec.getNics()) {
                 if (nic.getDeviceId() == i) {
-                    createVif(vm, nic, nicAdapter, extraConfig);
+                    createVif(vm, vmSpec, nic, nicAdapter, extraConfig);
                 }
             }
         }
@@ -3213,12 +3224,16 @@
         }
     }
 
-    private void createVif(final LibvirtVMDef vm, final NicTO nic, final String nicAdapter, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException {
+    private void createVif(final LibvirtVMDef vm, final VirtualMachineTO vmSpec, final NicTO nic, final String nicAdapter, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException {
         if (vm.getDevices() == null) {
             s_logger.error("LibvirtVMDef object get devices with null result");
             throw new InternalErrorException("LibvirtVMDef object get devices with null result");
         }
-        vm.getDevices().addDevice(getVifDriver(nic.getType(), nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter, extraConfig));
+        final InterfaceDef interfaceDef = getVifDriver(nic.getType(), nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter, extraConfig);
+        if (vmSpec.getDetails() != null) {
+            setInterfaceDefQueueSettings(vmSpec.getDetails(), vmSpec.getCpus(), interfaceDef);
+        }
+        vm.getDevices().addDevice(interfaceDef);
     }
 
     public boolean cleanupDisk(Map<String, String> volumeToDisconnect) {
@@ -3436,13 +3451,54 @@
 
     @Override
     public PingCommand getCurrentStatus(final long id) {
-
+        PingRoutingCommand pingRoutingCommand;
         if (!_canBridgeFirewall) {
-            return new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, this.getHostVmStateReport());
+            pingRoutingCommand = new PingRoutingCommand(com.cloud.host.Host.Type.Routing, id, this.getHostVmStateReport());
         } else {
             final HashMap<String, Pair<Long, Long>> nwGrpStates = syncNetworkGroups(id);
-            return new PingRoutingWithNwGroupsCommand(getType(), id, this.getHostVmStateReport(), nwGrpStates);
+            pingRoutingCommand = new PingRoutingWithNwGroupsCommand(getType(), id, this.getHostVmStateReport(), nwGrpStates);
         }
+        HealthCheckResult healthCheckResult = getHostHealthCheckResult();
+        if (healthCheckResult != HealthCheckResult.IGNORE) {
+            pingRoutingCommand.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
+        }
+        return pingRoutingCommand;
+    }
+
+    /**
+     * The health check result is true, if the script is executed successfully and the exit code is 0
+     * The health check result is false, if the script is executed successfully and the exit code is 1
+     * The health check result is null, if
+     * - Script file is not specified, or
+     * - Script file does not exist, or
+     * - Script file is not accessible by the user of the cloudstack-agent process, or
+     * - Script file is not executable
+     * - There are errors when the script is executed (exit codes other than 0 or 1)
+     */
+    private HealthCheckResult getHostHealthCheckResult() {
+        if (StringUtils.isBlank(hostHealthCheckScriptPath)) {
+            s_logger.debug("Host health check script path is not specified");
+            return HealthCheckResult.IGNORE;
+        }
+        File script = new File(hostHealthCheckScriptPath);
+        if (!script.exists() || !script.isFile() || !script.canExecute()) {
+            s_logger.warn(String.format("The host health check script file set at: %s cannot be executed, " +
+                            "reason: %s", hostHealthCheckScriptPath,
+                    !script.exists() ? "file does not exist" : "please check file permissions to execute this file"));
+            return HealthCheckResult.IGNORE;
+        }
+        int exitCode = executeBashScriptAndRetrieveExitValue(hostHealthCheckScriptPath);
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug(String.format("Host health check script exit code: %s", exitCode));
+        }
+        return retrieveHealthCheckResultFromExitCode(exitCode);
+    }
+
+    private HealthCheckResult retrieveHealthCheckResultFromExitCode(int exitCode) {
+        if (exitCode != 0 && exitCode != 1) {
+            return HealthCheckResult.IGNORE;
+        }
+        return exitCode == 0 ? HealthCheckResult.SUCCESS : HealthCheckResult.FAILURE;
     }
 
     @Override
@@ -3484,6 +3540,10 @@
         cmd.setGatewayIpAddress(_localGateway);
         cmd.setIqn(getIqn());
         cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
+        HealthCheckResult healthCheckResult = getHostHealthCheckResult();
+        if (healthCheckResult != HealthCheckResult.IGNORE) {
+            cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
+        }
 
         if (cmd.getHostDetails().containsKey("Host.OS")) {
             _hostDistro = cmd.getHostDetails().get("Host.OS");
@@ -5087,4 +5147,30 @@
     public static String generateSecretUUIDFromString(String seed) {
         return UUID.nameUUIDFromBytes(seed.getBytes()).toString();
     }
+
+    public void setInterfaceDefQueueSettings(Map<String, String> details, Integer cpus, InterfaceDef interfaceDef) {
+        String nicMultiqueueNumber = details.get(VmDetailConstants.NIC_MULTIQUEUE_NUMBER);
+        if (nicMultiqueueNumber != null) {
+            try {
+                Integer nicMultiqueueNumberInteger = Integer.valueOf(nicMultiqueueNumber);
+                if (nicMultiqueueNumberInteger == InterfaceDef.MULTI_QUEUE_NUMBER_MEANS_CPU_CORES) {
+                    if (cpus != null) {
+                        interfaceDef.setMultiQueueNumber(cpus);
+                    }
+                } else {
+                    interfaceDef.setMultiQueueNumber(nicMultiqueueNumberInteger);
+                }
+            } catch (NumberFormatException ex) {
+                s_logger.warn(String.format("VM details %s is not a valid integer value %s", VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber));
+            }
+        }
+        String nicPackedEnabled = details.get(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED);
+        if (nicPackedEnabled != null) {
+            try {
+                interfaceDef.setPackedVirtQueues(Boolean.valueOf(nicPackedEnabled));
+            } catch (NumberFormatException ex) {
+                s_logger.warn(String.format("VM details %s is not a valid Boolean value %s", VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, nicPackedEnabled));
+            }
+        }
+    }
 }
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index cb650e2..9a27e5e 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -249,6 +249,15 @@
                     def.setDpdkOvsPath(ovsPath);
                     def.setInterfaceMode(mode);
                 }
+                String multiQueueNumber = getAttrValue("driver", "queues", nic);
+                if (StringUtils.isNotBlank(multiQueueNumber)) {
+                    def.setMultiQueueNumber(Integer.valueOf(multiQueueNumber));
+                }
+
+                String packedOn = getAttrValue("driver", "packed", nic);
+                if (StringUtils.isNotBlank(packedOn)) {
+                    def.setPackedVirtQueues("on".equalsIgnoreCase(packedOn));
+                }
 
                 if (StringUtils.isNotBlank(slot)) {
                     def.setSlot(Integer.parseInt(slot, 16));
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index b739c0e..aac44fc 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -1280,6 +1280,8 @@
             DIRECT_ATTACHED_WITHOUT_DHCP, DIRECT_ATTACHED_WITH_DHCP, VNET, VLAN;
         }
 
+        public static final int MULTI_QUEUE_NUMBER_MEANS_CPU_CORES = -1;
+
         private GuestNetType _netType; /*
          * bridge, ethernet, network, user,
          * internal, vhostuser
@@ -1305,6 +1307,8 @@
         private String _interfaceMode;
         private String _userIp4Network;
         private Integer _userIp4Prefix;
+        private Integer _multiQueueNumber;
+        private Boolean _packedVirtQueues;
 
         public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model) {
             defBridgeNet(brName, targetBrName, macAddr, model, 0);
@@ -1493,6 +1497,14 @@
             _interfaceMode = mode;
         }
 
+        public void setMultiQueueNumber(Integer multiQueueNumber) {
+            this._multiQueueNumber = multiQueueNumber;
+        }
+
+        public void setPackedVirtQueues(Boolean packedVirtQueues) {
+            this._packedVirtQueues = packedVirtQueues;
+        }
+
         public String getContent() {
             StringBuilder netBuilder = new StringBuilder();
             if (_netType == GuestNetType.BRIDGE) {
@@ -1515,6 +1527,21 @@
             if (_model != null) {
                 netBuilder.append("<model type='" + _model + "'/>\n");
             }
+            if (NicModel.VIRTIO.equals(_model)) {
+                boolean isMultiQueueNumberSpecified = _multiQueueNumber != null;
+                boolean isPackedVirtQueuesEnabled = _packedVirtQueues != null && _packedVirtQueues
+                        && s_qemuVersion >= 4200000 && s_libvirtVersion >= 6300000;
+                if (isMultiQueueNumberSpecified || isPackedVirtQueuesEnabled) {
+                    netBuilder.append("<driver");
+                    if (isMultiQueueNumberSpecified) {
+                        netBuilder.append(" queues='" + _multiQueueNumber + "'");
+                    }
+                    if (isPackedVirtQueuesEnabled) {
+                        netBuilder.append(" packed='on'");
+                    }
+                    netBuilder.append("/>\n");
+                }
+            }
             if ((s_libvirtVersion >= 9004) && (_networkRateKBps > 0)) { // supported from libvirt 0.9.4
                 netBuilder.append("<bandwidth>\n");
                 netBuilder.append("<inbound average='" + _networkRateKBps + "' peak='" + _networkRateKBps + "'/>\n");
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
index ca78c71..dffa836 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java
@@ -64,6 +64,9 @@
             }
             final VifDriver vifDriver = libvirtComputingResource.getVifDriver(nic.getType(), nic.getName());
             final InterfaceDef interfaceDef = vifDriver.plug(nic, "Other PV", "", null);
+            if (command.getDetails() != null) {
+                libvirtComputingResource.setInterfaceDefQueueSettings(command.getDetails(), null, interfaceDef);
+            }
             vm.attachDevice(interfaceDef.toString());
 
             // apply default network rules on new nic
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
index 9109d57..ec9e67e 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
@@ -80,6 +80,9 @@
 
             for (final NicTO nic : nics) {
                 LibvirtVMDef.InterfaceDef interfaceDef = libvirtComputingResource.getVifDriver(nic.getType(), nic.getName()).plug(nic, null, "", vm.getExtraConfig());
+                if (vm.getDetails() != null) {
+                    libvirtComputingResource.setInterfaceDefQueueSettings(vm.getDetails(), vm.getCpus(), interfaceDef);
+                }
                 if (interfaceDef != null && interfaceDef.getNetType() == GuestNetType.VHOSTUSER) {
                     DpdkTO to = new DpdkTO(interfaceDef.getDpdkOvsPath(), interfaceDef.getDpdkSourcePort(), interfaceDef.getInterfaceMode());
                     dpdkInterfaceMapping.put(nic.getMac(), to);
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
index ff9ae6d..558c7f0 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java
@@ -66,7 +66,9 @@
 
             final VifDriver newVifDriver = libvirtComputingResource.getVifDriver(nic.getType(), nic.getName());
             final InterfaceDef interfaceDef = newVifDriver.plug(nic, "Other PV", oldPluggedNic.getModel().toString(), null);
-
+            if (command.getDetails() != null) {
+                libvirtComputingResource.setInterfaceDefQueueSettings(command.getDetails(), null, interfaceDef);
+            }
             interfaceDef.setSlot(oldPluggedNic.getSlot());
             interfaceDef.setDevName(oldPluggedNic.getDevName());
             interfaceDef.setLinkStateUp(false);
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
index ec59265..57bdf81 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java
@@ -151,6 +151,58 @@
     }
 
     @Test
+    public void testInterfaceWithMultiQueueAndPacked() {
+        LibvirtVMDef.InterfaceDef ifDef = new LibvirtVMDef.InterfaceDef();
+        ifDef.defBridgeNet("targetDeviceName", null, "00:11:22:aa:bb:dd", LibvirtVMDef.InterfaceDef.NicModel.VIRTIO);
+        ifDef.setMultiQueueNumber(6);
+
+        LibvirtVMDef.setGlobalQemuVersion(5000000L);
+        LibvirtVMDef.setGlobalLibvirtVersion(6400000L);
+
+        String expected =
+                "<interface type='" + LibvirtVMDef.InterfaceDef.GuestNetType.BRIDGE + "'>\n"
+                        + "<source bridge='targetDeviceName'/>\n"
+                        + "<mac address='00:11:22:aa:bb:dd'/>\n"
+                        + "<model type='virtio'/>\n"
+                        + "<driver queues='6'/>\n"
+                        + "<link state='up'/>\n"
+                        + "</interface>\n";
+        assertEquals(expected, ifDef.toString());
+
+        ifDef.setPackedVirtQueues(true);
+        expected =
+                "<interface type='" + LibvirtVMDef.InterfaceDef.GuestNetType.BRIDGE + "'>\n"
+                        + "<source bridge='targetDeviceName'/>\n"
+                        + "<mac address='00:11:22:aa:bb:dd'/>\n"
+                        + "<model type='virtio'/>\n"
+                        + "<driver queues='6' packed='on'/>\n"
+                        + "<link state='up'/>\n"
+                        + "</interface>\n";
+        assertEquals(expected, ifDef.toString());
+
+        ifDef.setMultiQueueNumber(null);
+        expected =
+                "<interface type='" + LibvirtVMDef.InterfaceDef.GuestNetType.BRIDGE + "'>\n"
+                        + "<source bridge='targetDeviceName'/>\n"
+                        + "<mac address='00:11:22:aa:bb:dd'/>\n"
+                        + "<model type='virtio'/>\n"
+                        + "<driver packed='on'/>\n"
+                        + "<link state='up'/>\n"
+                        + "</interface>\n";
+        assertEquals(expected, ifDef.toString());
+
+        LibvirtVMDef.setGlobalLibvirtVersion(300000L);
+        expected =
+                "<interface type='" + LibvirtVMDef.InterfaceDef.GuestNetType.BRIDGE + "'>\n"
+                        + "<source bridge='targetDeviceName'/>\n"
+                        + "<mac address='00:11:22:aa:bb:dd'/>\n"
+                        + "<model type='virtio'/>\n"
+                        + "<link state='up'/>\n"
+                        + "</interface>\n";
+        assertEquals(expected, ifDef.toString());
+        }
+
+    @Test
     public void testCpuModeDef() {
         LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
         cpuModeDef.setMode("custom");
diff --git a/plugins/hypervisors/ovm/pom.xml b/plugins/hypervisors/ovm/pom.xml
index 511e29f..24ce660 100644
--- a/plugins/hypervisors/ovm/pom.xml
+++ b/plugins/hypervisors/ovm/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/hypervisors/ovm3/pom.xml b/plugins/hypervisors/ovm3/pom.xml
index 3020d1e..2be28bd 100644
--- a/plugins/hypervisors/ovm3/pom.xml
+++ b/plugins/hypervisors/ovm3/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/hypervisors/simulator/pom.xml b/plugins/hypervisors/simulator/pom.xml
index ce50a45..59c0553 100644
--- a/plugins/hypervisors/simulator/pom.xml
+++ b/plugins/hypervisors/simulator/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <artifactId>cloud-plugin-hypervisor-simulator</artifactId>
diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml
index b21bc2f..c7e1871 100644
--- a/plugins/hypervisors/ucs/pom.xml
+++ b/plugins/hypervisors/ucs/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <artifactId>cloud-plugin-hypervisor-ucs</artifactId>
diff --git a/plugins/hypervisors/vmware/pom.xml b/plugins/hypervisors/vmware/pom.xml
index 5edc0ee..030da6d 100644
--- a/plugins/hypervisors/vmware/pom.xml
+++ b/plugins/hypervisors/vmware/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index fe35d56..8c49860 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -781,8 +781,7 @@
                 volume = createVolume(disk, vmToImport, domainId, zoneId, accountId, instanceId, poolId, templateId, backup, true);
                 operation = "created";
             }
-            s_logger.debug(String.format("VM [id: %s, instanceName: %s] backup restore operation %s volume [id: %s].", instanceId, vmInstanceVO.getInstanceName(),
-                    operation, volume.getUuid()));
+            s_logger.debug(String.format("Sync volumes to %s in backup restore operation: %s volume [id: %s].", vmInstanceVO, operation, volume.getUuid()));
         }
     }
 
@@ -879,9 +878,13 @@
         String tag = parts[parts.length - 1];
         String[] tagSplit = tag.split("-");
         tag = tagSplit[tagSplit.length - 1];
+
+        s_logger.debug(String.format("Trying to find network with vlan: [%s].", vlan));
         NetworkVO networkVO = networkDao.findByVlan(vlan);
         if (networkVO == null) {
             networkVO = createNetworkRecord(zoneId, tag, vlan, accountId, domainId);
+            s_logger.debug(String.format("Created new network record [id: %s] with details [zoneId: %s, tag: %s, vlan: %s, accountId: %s and domainId: %s].",
+                    networkVO.getUuid(), zoneId, tag, vlan, accountId, domainId));
         }
         return networkVO;
     }
@@ -893,6 +896,7 @@
         Map<String, NetworkVO> mapping = new HashMap<>();
         for (String networkName : vmNetworkNames) {
             NetworkVO networkVO = getGuestNetworkFromNetworkMorName(networkName, accountId, zoneId, domainId);
+            s_logger.debug(String.format("Mapping network name [%s] to networkVO [id: %s].", networkName, networkVO.getUuid()));
             mapping.put(networkName, networkVO);
         }
         return mapping;
@@ -927,12 +931,19 @@
             String macAddress = pair.first();
             String networkName = pair.second();
             NetworkVO networkVO = networksMapping.get(networkName);
-            NicVO nicVO = nicDao.findByNetworkIdAndMacAddress(networkVO.getId(), macAddress);
+            NicVO nicVO = nicDao.findByNetworkIdAndMacAddressIncludingRemoved(networkVO.getId(), macAddress);
             if (nicVO != null) {
+                s_logger.warn(String.format("Find NIC in DB with networkId [%s] and MAC Address [%s], so this NIC will be removed from list of unmapped NICs of VM [id: %s, name: %s].",
+                        networkVO.getId(), macAddress, vm.getUuid(), vm.getInstanceName()));
                 allNics.remove(nicVO);
+
+                if (nicVO.getRemoved() != null) {
+                    nicDao.unremove(nicVO.getId());
+                }
             }
         }
         for (final NicVO unMappedNic : allNics) {
+            s_logger.debug(String.format("Removing NIC [%s] from backup restored %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(unMappedNic, "uuid", "macAddress"), vm));
             vmManager.removeNicFromVm(vm, unMappedNic);
         }
     }
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
index 5b94858..80a4362 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
@@ -198,7 +198,7 @@
     }
 
     //Fang: new command added;
-    // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature)
+    // Important! we need to sync file system before we can safely use tar to work around a linux kernel bug(or feature)
     public String createOvaForVolume(VolumeObjectTO volume, int archiveTimeout) {
         DataStoreTO storeTO = volume.getDataStore();
         if (!(storeTO instanceof NfsTO)) {
@@ -1054,7 +1054,7 @@
             }
             String exportDir = ova_metafile.getParent();
             s_logger.info("exportDir: " + exportDir);
-            // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature)
+            // Important! we need to sync file system before we can safely use tar to work around a linux kernel bug(or feature)
             s_logger.info("Sync file system before we package OVA..., before tar ");
             s_logger.info("ova: " + ovaFileName + ", ovf:" + ovfFileName + ", vmdk:" + disks[0] + ".");
             Script commandSync = new Script(true, "sync", 0, s_logger);
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 8b833b8..90bfdd6 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -73,6 +73,8 @@
 import com.cloud.agent.api.AttachIsoCommand;
 import com.cloud.agent.api.BackupSnapshotAnswer;
 import com.cloud.agent.api.BackupSnapshotCommand;
+import com.cloud.agent.api.CheckGuestOsMappingAnswer;
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
 import com.cloud.agent.api.CheckHealthAnswer;
 import com.cloud.agent.api.CheckHealthCommand;
 import com.cloud.agent.api.CheckNetworkAnswer;
@@ -94,6 +96,8 @@
 import com.cloud.agent.api.DeleteVMSnapshotCommand;
 import com.cloud.agent.api.GetHostStatsAnswer;
 import com.cloud.agent.api.GetHostStatsCommand;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
 import com.cloud.agent.api.GetStoragePoolCapabilitiesAnswer;
 import com.cloud.agent.api.GetStoragePoolCapabilitiesCommand;
 import com.cloud.agent.api.GetStorageStatsAnswer;
@@ -308,6 +312,7 @@
 import com.vmware.vim25.DynamicProperty;
 import com.vmware.vim25.GuestInfo;
 import com.vmware.vim25.GuestNicInfo;
+import com.vmware.vim25.GuestOsDescriptor;
 import com.vmware.vim25.HostCapability;
 import com.vmware.vim25.HostConfigInfo;
 import com.vmware.vim25.HostFileSystemMountInfo;
@@ -606,6 +611,10 @@
                 answer = execute((GetVmVncTicketCommand) cmd);
             } else if (clz == GetAutoScaleMetricsCommand.class) {
                 answer = execute((GetAutoScaleMetricsCommand) cmd);
+            } else if (clz == CheckGuestOsMappingCommand.class) {
+                answer = execute((CheckGuestOsMappingCommand) cmd);
+            } else if (clz == GetHypervisorGuestOsNamesCommand.class) {
+                answer = execute((GetHypervisorGuestOsNamesCommand) cmd);
             } else {
                 answer = Answer.createUnsupportedCommandAnswer(cmd);
             }
@@ -7727,6 +7736,60 @@
         }
     }
 
+    protected CheckGuestOsMappingAnswer execute(CheckGuestOsMappingCommand cmd) {
+        String guestOsName = cmd.getGuestOsName();
+        String guestOsMappingName = cmd.getGuestOsHypervisorMappingName();
+        s_logger.info("Checking guest os mapping name: " + guestOsMappingName + " for the guest os: " + guestOsName + " in the hypervisor");
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareHypervisorHost hyperHost = getHyperHost(context);
+            GuestOsDescriptor guestOsDescriptor = hyperHost.getGuestOsDescriptor(guestOsMappingName);
+            if (guestOsDescriptor == null) {
+                return new CheckGuestOsMappingAnswer(cmd, "Guest os mapping name: " + guestOsMappingName + " not found in the hypervisor");
+            }
+            s_logger.debug("Matching hypervisor guest os - id: " + guestOsDescriptor.getId() + ", full name: " + guestOsDescriptor.getFullName() + ", family: " + guestOsDescriptor.getFamily());
+            if (guestOsDescriptor.getFullName().equalsIgnoreCase(guestOsName)) {
+                s_logger.debug("Hypervisor guest os name in the descriptor matches with os name: " + guestOsName);
+            }
+            s_logger.info("Hypervisor guest os name in the descriptor matches with os mapping: " + guestOsMappingName + " from user");
+            return new CheckGuestOsMappingAnswer(cmd);
+        } catch (Exception e) {
+            s_logger.error("Failed to check the hypervisor guest os mapping name: " + guestOsMappingName, e);
+            return new CheckGuestOsMappingAnswer(cmd, e.getLocalizedMessage());
+        }
+    }
+
+    protected GetHypervisorGuestOsNamesAnswer execute(GetHypervisorGuestOsNamesCommand cmd) {
+        String keyword = cmd.getKeyword();
+        s_logger.info("Getting guest os names in the hypervisor");
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareHypervisorHost hyperHost = getHyperHost(context);
+            List<GuestOsDescriptor> guestOsDescriptors = hyperHost.getGuestOsDescriptors();
+            if (guestOsDescriptors == null) {
+                return new GetHypervisorGuestOsNamesAnswer(cmd, "Guest os names not found in the hypervisor");
+            }
+            List<Pair<String, String>> hypervisorGuestOsNames = new ArrayList<>();
+            for (GuestOsDescriptor guestOsDescriptor : guestOsDescriptors) {
+                String osDescriptorFullName = guestOsDescriptor.getFullName();
+                String osDescriptorId = guestOsDescriptor.getId();
+                if (StringUtils.isNotBlank(keyword)) {
+                    if (osDescriptorFullName.toLowerCase().contains(keyword.toLowerCase()) || osDescriptorId.toLowerCase().contains(keyword.toLowerCase())) {
+                        Pair<String, String> hypervisorGuestOs = new Pair<>(osDescriptorFullName, osDescriptorId);
+                        hypervisorGuestOsNames.add(hypervisorGuestOs);
+                    }
+                } else {
+                    Pair<String, String> hypervisorGuestOs = new Pair<>(osDescriptorFullName, osDescriptorId);
+                    hypervisorGuestOsNames.add(hypervisorGuestOs);
+                }
+            }
+            return new GetHypervisorGuestOsNamesAnswer(cmd, hypervisorGuestOsNames);
+        } catch (Exception e) {
+            s_logger.error("Failed to get the hypervisor guest names due to: " + e.getLocalizedMessage(), e);
+            return new GetHypervisorGuestOsNamesAnswer(cmd, e.getLocalizedMessage());
+        }
+    }
+
     private Integer getVmwareWindowTimeInterval() {
         Integer windowInterval = VmwareManager.VMWARE_STATS_TIME_WINDOW.value();
         if (windowInterval == null || windowInterval < 20) {
diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java
index e974c23..e47ec24 100644
--- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java
+++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java
@@ -53,6 +53,10 @@
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
+import com.cloud.agent.api.CheckGuestOsMappingAnswer;
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
 import com.cloud.agent.api.ScaleVmAnswer;
 import com.cloud.agent.api.ScaleVmCommand;
 import com.cloud.agent.api.routing.GetAutoScaleMetricsAnswer;
@@ -79,6 +83,7 @@
 import com.cloud.utils.ExecutionResult;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.VmDetailConstants;
+import com.vmware.vim25.GuestOsDescriptor;
 import com.vmware.vim25.HostCapability;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.VimPortType;
@@ -554,4 +559,102 @@
         assertEquals(1, stats.length);
         assertEquals(lbStats[0], stats[0]);
     }
+
+    @Test
+    public void testCheckGuestOsMappingCommandFailure() throws Exception {
+        CheckGuestOsMappingCommand cmd = Mockito.mock(CheckGuestOsMappingCommand.class);
+        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
+        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centosWrongName");
+        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
+        when(hyperHost.getGuestOsDescriptor("centosWrongName")).thenReturn(null);
+
+        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);
+
+        assertFalse(answer.getResult());
+    }
+
+    @Test
+    public void testCheckGuestOsMappingCommandSuccess() throws Exception {
+        CheckGuestOsMappingCommand cmd = Mockito.mock(CheckGuestOsMappingCommand.class);
+        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
+        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centos64Guest");
+        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
+        GuestOsDescriptor guestOsDescriptor = Mockito.mock(GuestOsDescriptor.class);
+        when(hyperHost.getGuestOsDescriptor("centos64Guest")).thenReturn(guestOsDescriptor);
+        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");
+
+        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);
+
+        assertTrue(answer.getResult());
+    }
+
+    @Test
+    public void testCheckGuestOsMappingCommandException() {
+        CheckGuestOsMappingCommand cmd = Mockito.mock(CheckGuestOsMappingCommand.class);
+        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
+        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centos64Guest");
+        when(_resource.getHyperHost(context, null)).thenReturn(null);
+
+        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);
+
+        assertFalse(answer.getResult());
+    }
+
+    @Test
+    public void testGetHypervisorGuestOsNamesCommandFailure() throws Exception {
+        GetHypervisorGuestOsNamesCommand cmd = Mockito.mock(GetHypervisorGuestOsNamesCommand.class);
+        when(cmd.getKeyword()).thenReturn("CentOS");
+        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
+        when(hyperHost.getGuestOsDescriptors()).thenReturn(null);
+
+        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);
+
+        assertFalse(answer.getResult());
+    }
+
+    @Test
+    public void testGetHypervisorGuestOsNamesCommandSuccessWithKeyword() throws Exception {
+        GetHypervisorGuestOsNamesCommand cmd = Mockito.mock(GetHypervisorGuestOsNamesCommand.class);
+        when(cmd.getKeyword()).thenReturn("CentOS");
+        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
+        GuestOsDescriptor guestOsDescriptor = Mockito.mock(GuestOsDescriptor.class);
+        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");
+        when(guestOsDescriptor.getId()).thenReturn("centos64Guest");
+        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
+        guestOsDescriptors.add(guestOsDescriptor);
+        when(hyperHost.getGuestOsDescriptors()).thenReturn(guestOsDescriptors);
+
+        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);
+
+        assertTrue(answer.getResult());
+        assertEquals("centos64Guest", answer.getHypervisorGuestOsNames().get(0).first());
+    }
+
+    @Test
+    public void testGetHypervisorGuestOsNamesCommandSuccessWithoutKeyword() throws Exception {
+        GetHypervisorGuestOsNamesCommand cmd = Mockito.mock(GetHypervisorGuestOsNamesCommand.class);
+        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
+        GuestOsDescriptor guestOsDescriptor = Mockito.mock(GuestOsDescriptor.class);
+        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");
+        when(guestOsDescriptor.getId()).thenReturn("centos64Guest");
+        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
+        guestOsDescriptors.add(guestOsDescriptor);
+        when(hyperHost.getGuestOsDescriptors()).thenReturn(guestOsDescriptors);
+
+        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);
+
+        assertTrue(answer.getResult());
+        assertEquals("centos64Guest", answer.getHypervisorGuestOsNames().get(0).first());
+    }
+
+    @Test
+    public void testGetHypervisorGuestOsNamesCommandException() throws Exception {
+        GetHypervisorGuestOsNamesCommand cmd = Mockito.mock(GetHypervisorGuestOsNamesCommand.class);
+        when(cmd.getKeyword()).thenReturn("CentOS");
+        when(_resource.getHyperHost(context, null)).thenReturn(null);
+
+        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);
+
+        assertFalse(answer.getResult());
+    }
 }
diff --git a/plugins/hypervisors/xenserver/pom.xml b/plugins/hypervisors/xenserver/pom.xml
index 721200f..3326d15 100644
--- a/plugins/hypervisors/xenserver/pom.xml
+++ b/plugins/hypervisors/xenserver/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCheckGuestOsMappingCommandWrapper.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCheckGuestOsMappingCommandWrapper.java
new file mode 100644
index 0000000..68403d7
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixCheckGuestOsMappingCommandWrapper.java
@@ -0,0 +1,67 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
+
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CheckGuestOsMappingAnswer;
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
+import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.VM;
+
+@ResourceWrapper(handles =  CheckGuestOsMappingCommand.class)
+public final class CitrixCheckGuestOsMappingCommandWrapper extends CommandWrapper<CheckGuestOsMappingCommand, Answer, CitrixResourceBase> {
+
+    private static final Logger s_logger = Logger.getLogger(CitrixCheckGuestOsMappingCommandWrapper.class);
+
+    @Override
+    public Answer execute(final CheckGuestOsMappingCommand command, final CitrixResourceBase citrixResourceBase) {
+        final Connection conn = citrixResourceBase.getConnection();
+        String guestOsName = command.getGuestOsName();
+        String guestOsMappingName = command.getGuestOsHypervisorMappingName();
+        try {
+            s_logger.info("Checking guest os mapping name: " + guestOsMappingName + " for the guest os: " + guestOsName + " in the hypervisor");
+            final Set<VM> vms = VM.getAll(conn);
+            if (CollectionUtils.isEmpty(vms)) {
+                return new CheckGuestOsMappingAnswer(command, "Unable to match guest os mapping name: " + guestOsMappingName + " in the hypervisor");
+            }
+            for (VM vm : vms) {
+                if (vm != null && vm.getIsATemplate(conn) && guestOsMappingName.equalsIgnoreCase(vm.getNameLabel(conn))) {
+                    if (guestOsName.equalsIgnoreCase(vm.getNameLabel(conn))) {
+                        s_logger.debug("Hypervisor guest os name label matches with os name: " + guestOsName);
+                    }
+                    s_logger.info("Hypervisor guest os name label matches with os mapping: " + guestOsMappingName + " from user");
+                    return new CheckGuestOsMappingAnswer(command);
+                }
+            }
+            return new CheckGuestOsMappingAnswer(command, "Guest os mapping name: " + guestOsMappingName + " not found in the hypervisor");
+        } catch (final Exception e) {
+            s_logger.error("Failed to find the hypervisor guest os mapping name: " + guestOsMappingName, e);
+            return new CheckGuestOsMappingAnswer(command, e.getLocalizedMessage());
+        }
+    }
+}
diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetHypervisorGuestOsNamesCommandWrapper.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetHypervisorGuestOsNamesCommandWrapper.java
new file mode 100644
index 0000000..0be3e5a
--- /dev/null
+++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetHypervisorGuestOsNamesCommandWrapper.java
@@ -0,0 +1,76 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
+import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.utils.Pair;
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.VM;
+
+@ResourceWrapper(handles =  GetHypervisorGuestOsNamesCommand.class)
+public final class CitrixGetHypervisorGuestOsNamesCommandWrapper extends CommandWrapper<GetHypervisorGuestOsNamesCommand, Answer, CitrixResourceBase> {
+
+    private static final Logger s_logger = Logger.getLogger(CitrixGetHypervisorGuestOsNamesCommandWrapper.class);
+
+    @Override
+    public Answer execute(final GetHypervisorGuestOsNamesCommand command, final CitrixResourceBase citrixResourceBase) {
+        final Connection conn = citrixResourceBase.getConnection();
+        String keyword = command.getKeyword();
+        try {
+            s_logger.info("Getting guest os names in the hypervisor");
+            final Set<VM> vms = VM.getAll(conn);
+            if (CollectionUtils.isEmpty(vms)) {
+                return new GetHypervisorGuestOsNamesAnswer(command, "Guest os names not found in the hypervisor");
+            }
+            List<Pair<String, String>> hypervisorGuestOsNames = new ArrayList<>();
+            for (VM vm : vms) {
+                if (vm != null && vm.getIsATemplate(conn)) {
+                    String guestOSNameLabel = vm.getNameLabel(conn);
+                    if (StringUtils.isNotBlank(keyword)) {
+                        if (guestOSNameLabel.toLowerCase().contains(keyword.toLowerCase())) {
+                            Pair<String, String> hypervisorGuestOs = new Pair<>(guestOSNameLabel, guestOSNameLabel);
+                            hypervisorGuestOsNames.add(hypervisorGuestOs);
+                        }
+                    } else {
+                        Pair<String, String> hypervisorGuestOs = new Pair<>(guestOSNameLabel, guestOSNameLabel);
+                        hypervisorGuestOsNames.add(hypervisorGuestOs);
+                    }
+                }
+            }
+            return new GetHypervisorGuestOsNamesAnswer(command, hypervisorGuestOsNames);
+        } catch (final Exception e) {
+            s_logger.error("Failed to fetch hypervisor guest os names due to: " + e.getLocalizedMessage(), e);
+            return new GetHypervisorGuestOsNamesAnswer(command, e.getLocalizedMessage());
+        }
+    }
+}
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
index df9e9a4..661200f 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBaseTest.java
@@ -99,7 +99,7 @@
 
     }
 
-    public void testGetPathFilesExeption() {
+    public void testGetPathFilesException() {
         String patch = citrixResourceBase.getPatchFilePath();
 
         PowerMockito.mockStatic(Script.class);
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpOssResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpOssResourceTest.java
index 8f703ed..e02279b 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpOssResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpOssResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpServerResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpServerResourceTest.java
index 6a926a7..ed074be 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpServerResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XcpServerResourceTest.java
@@ -46,8 +46,8 @@
     }
 
     @Test(expected = CloudRuntimeException.class)
-    public void testGetFilesExeption() {
-        testGetPathFilesExeption();
+    public void testGetFilesException() {
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56FP1ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56FP1ResourceTest.java
index 902e8fd..d7f1774 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56FP1ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56FP1ResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56ResourceTest.java
index 1762772..a33549a 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56ResourceTest.java
@@ -47,7 +47,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56SP2ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56SP2ResourceTest.java
index 65a9b47..c3c8d46 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56SP2ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer56SP2ResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer600ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer600ResourceTest.java
index 2175884..773dd57 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer600ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer600ResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer625ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer625ResourceTest.java
index fc14d9f..7fb3f7e 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer625ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer625ResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer650ResourceTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer650ResourceTest.java
index be41840..dbb4198 100644
--- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer650ResourceTest.java
+++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/XenServer650ResourceTest.java
@@ -43,7 +43,7 @@
 
     @Test(expected = CloudRuntimeException.class)
     public void testGetFiles() {
-        testGetPathFilesExeption();
+        testGetPathFilesException();
     }
 
     @Test
diff --git a/plugins/integrations/cloudian/pom.xml b/plugins/integrations/cloudian/pom.xml
index 3f5c9d1..bf57a44 100644
--- a/plugins/integrations/cloudian/pom.xml
+++ b/plugins/integrations/cloudian/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/integrations/kubernetes-service/pom.xml b/plugins/integrations/kubernetes-service/pom.xml
index 7be1dec..5526358 100644
--- a/plugins/integrations/kubernetes-service/pom.xml
+++ b/plugins/integrations/kubernetes-service/pom.xml
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
index b8f399b..591da07 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
@@ -32,6 +32,10 @@
  */
 public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm.StateObject<KubernetesCluster.State>, Identity, InternalIdentity, Displayable {
 
+    enum ClusterType {
+        CloudManaged, ExternalManaged;
+    };
+
     enum Event {
         StartRequested,
         StopRequested,
@@ -115,10 +119,10 @@
     String getName();
     String getDescription();
     long getZoneId();
-    long getKubernetesVersionId();
-    long getServiceOfferingId();
-    long getTemplateId();
-    long getNetworkId();
+    Long getKubernetesVersionId();
+    Long getServiceOfferingId();
+    Long getTemplateId();
+    Long getNetworkId();
     long getDomainId();
     long getAccountId();
     long getControlNodeCount();
@@ -137,4 +141,5 @@
     Long getMinSize();
     Long getMaxSize();
     Long getSecurityGroupId();
+    ClusterType getClusterType();
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
index 0c07268..3551144 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
@@ -17,6 +17,7 @@
 package com.cloud.kubernetes.cluster;
 
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
+import static com.cloud.vm.UserVmManager.AllowUserExpungeRecoverVm;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -25,8 +26,11 @@
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
@@ -36,17 +40,22 @@
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.uservm.UserVm;
+import com.cloud.vm.UserVmService;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.SecurityChecker;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiConstants.VMDetails;
+import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.GetKubernetesClusterConfigCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.ListKubernetesClustersCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
@@ -54,6 +63,7 @@
 import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
 import org.apache.cloudstack.api.response.KubernetesClusterResponse;
 import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.RemoveVirtualMachinesFromKubernetesClusterResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.config.ApiServiceConfiguration;
 import org.apache.cloudstack.context.CallContext;
@@ -244,6 +254,9 @@
     @Inject
     public SecurityGroupService securityGroupService;
 
+    @Inject
+    private UserVmService userVmService;
+
     private void logMessage(final Level logLevel, final String message, final Exception e) {
         if (logLevel == Level.WARN) {
             if (e != null) {
@@ -535,10 +548,14 @@
         response.setControlNodes(kubernetesCluster.getControlNodeCount());
         response.setClusterSize(kubernetesCluster.getNodeCount());
         VMTemplateVO template = ApiDBUtils.findTemplateById(kubernetesCluster.getTemplateId());
-        response.setTemplateId(template.getUuid());
+        if (template != null) {
+            response.setTemplateId(template.getUuid());
+        }
         ServiceOfferingVO offering = serviceOfferingDao.findById(kubernetesCluster.getServiceOfferingId());
-        response.setServiceOfferingId(offering.getUuid());
-        response.setServiceOfferingName(offering.getName());
+        if (offering != null) {
+            response.setServiceOfferingId(offering.getUuid());
+            response.setServiceOfferingName(offering.getName());
+        }
         KubernetesSupportedVersionVO version = kubernetesSupportedVersionDao.findById(kubernetesCluster.getKubernetesVersionId());
         if (version != null) {
             response.setKubernetesVersionId(version.getUuid());
@@ -561,13 +578,15 @@
         response.setMemory(String.valueOf(kubernetesCluster.getMemory()));
         NetworkVO ntwk = networkDao.findByIdIncludingRemoved(kubernetesCluster.getNetworkId());
         response.setEndpoint(kubernetesCluster.getEndpoint());
-        response.setNetworkId(ntwk.getUuid());
-        response.setAssociatedNetworkName(ntwk.getName());
-        if (ntwk.getGuestType() == Network.GuestType.Isolated) {
-            List<IPAddressVO> ipAddresses = ipAddressDao.listByAssociatedNetwork(ntwk.getId(), true);
-            if (ipAddresses != null && ipAddresses.size() == 1) {
-                response.setIpAddress(ipAddresses.get(0).getAddress().addr());
-                response.setIpAddressId(ipAddresses.get(0).getUuid());
+        if (ntwk != null) {
+            response.setNetworkId(ntwk.getUuid());
+            response.setAssociatedNetworkName(ntwk.getName());
+            if (ntwk.getGuestType() == Network.GuestType.Isolated) {
+                List<IPAddressVO> ipAddresses = ipAddressDao.listByAssociatedNetwork(ntwk.getId(), true);
+                if (ipAddresses != null && ipAddresses.size() == 1) {
+                    response.setIpAddress(ipAddresses.get(0).getAddress().addr());
+                    response.setIpAddressId(ipAddresses.get(0).getUuid());
+                }
             }
         }
 
@@ -595,6 +614,7 @@
         response.setAutoscalingEnabled(kubernetesCluster.getAutoscalingEnabled());
         response.setMinSize(kubernetesCluster.getMinSize());
         response.setMaxSize(kubernetesCluster.getMaxSize());
+        response.setClusterType(kubernetesCluster.getClusterType());
         response.setCreated(kubernetesCluster.getCreated());
         return response;
     }
@@ -608,7 +628,95 @@
         }
     }
 
-    private void validateKubernetesClusterCreateParameters(final CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
+    private DataCenter validateAndGetZoneForKubernetesCreateParameters(Long zoneId) {
+        DataCenter zone = dataCenterDao.findById(zoneId);
+        if (zone == null) {
+            throw new InvalidParameterValueException("Unable to find zone by ID: " + zoneId);
+        }
+        if (zone.getAllocationState() == Grouping.AllocationState.Disabled) {
+            throw new PermissionDeniedException(String.format("Cannot perform this operation, zone ID: %s is currently disabled", zone.getUuid()));
+        }
+        return zone;
+    }
+
+    private void validateSshKeyPairForKubernetesCreateParameters(String sshKeyPair, Account owner) {
+        if (!StringUtils.isBlank(sshKeyPair)) {
+            SSHKeyPairVO sshKeyPairVO = sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), sshKeyPair);
+            if (sshKeyPairVO == null) {
+                throw new InvalidParameterValueException(String.format("Given SSH key pair with name: %s was not found for the account %s", sshKeyPair, owner.getAccountName()));
+            }
+        }
+    }
+
+    private Network validateAndGetNetworkForKubernetesCreateParameters(Long networkId) {
+        Network network = null;
+        if (networkId != null) {
+            network = networkService.getNetwork(networkId);
+            if (network == null) {
+                throw new InvalidParameterValueException("Unable to find network with given ID");
+            }
+        }
+        return network;
+    }
+
+    private void validateUnmanagedKubernetesClusterCreateParameters(final CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
+        final String name = cmd.getName();
+        final Long zoneId = cmd.getZoneId();
+        final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
+        final Long networkId = cmd.getNetworkId();
+        final String sshKeyPair = cmd.getSSHKeyPairName();
+        final String dockerRegistryUserName = cmd.getDockerRegistryUserName();
+        final String dockerRegistryPassword = cmd.getDockerRegistryPassword();
+        final String dockerRegistryUrl = cmd.getDockerRegistryUrl();
+        final Long nodeRootDiskSize = cmd.getNodeRootDiskSize();
+        final String externalLoadBalancerIpAddress = cmd.getExternalLoadBalancerIpAddress();
+
+        if (name == null || name.isEmpty()) {
+            throw new InvalidParameterValueException("Invalid name for the Kubernetes cluster name: " + name);
+        }
+
+        validateAndGetZoneForKubernetesCreateParameters(zoneId);
+        validateSshKeyPairForKubernetesCreateParameters(sshKeyPair, owner);
+
+        if (nodeRootDiskSize != null && nodeRootDiskSize <= 0) {
+            throw new InvalidParameterValueException(String.format("Invalid value for %s", ApiConstants.NODE_ROOT_DISK_SIZE));
+        }
+
+        validateDockerRegistryParams(dockerRegistryUserName, dockerRegistryPassword, dockerRegistryUrl);
+
+        validateAndGetNetworkForKubernetesCreateParameters(networkId);
+
+        if (StringUtils.isNotEmpty(externalLoadBalancerIpAddress) && (!NetUtils.isValidIp4(externalLoadBalancerIpAddress) && !NetUtils.isValidIp6(externalLoadBalancerIpAddress))) {
+            throw new InvalidParameterValueException("Invalid external load balancer IP address");
+        }
+    }
+
+    public boolean isCommandSupported(KubernetesCluster cluster, String cmdName) {
+        switch (cluster.getClusterType()) {
+            case CloudManaged:
+                return Arrays.asList(
+                        BaseCmd.getCommandNameByClass(CreateKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(ListKubernetesClustersCmd.class),
+                        BaseCmd.getCommandNameByClass(DeleteKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(ScaleKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(StartKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(StopKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(UpgradeKubernetesClusterCmd.class)
+                ).contains(cmdName);
+            case ExternalManaged:
+                return Arrays.asList(
+                        BaseCmd.getCommandNameByClass(CreateKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(ListKubernetesClustersCmd.class),
+                        BaseCmd.getCommandNameByClass(DeleteKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(AddVirtualMachinesToKubernetesClusterCmd.class),
+                        BaseCmd.getCommandNameByClass(RemoveVirtualMachinesFromKubernetesClusterCmd.class)
+                ).contains(cmdName);
+            default:
+                return false;
+        }
+    }
+
+    private void validateManagedKubernetesClusterCreateParameters(final CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
         validateEndpointUrl();
         final String name = cmd.getName();
         final Long zoneId = cmd.getZoneId();
@@ -619,7 +727,6 @@
         final String sshKeyPair = cmd.getSSHKeyPairName();
         final Long controlNodeCount = cmd.getControlNodes();
         final Long clusterSize = cmd.getClusterSize();
-        final long totalNodeCount = controlNodeCount + clusterSize;
         final String dockerRegistryUserName = cmd.getDockerRegistryUserName();
         final String dockerRegistryPassword = cmd.getDockerRegistryPassword();
         final String dockerRegistryUrl = cmd.getDockerRegistryUrl();
@@ -627,31 +734,24 @@
         final String externalLoadBalancerIpAddress = cmd.getExternalLoadBalancerIpAddress();
 
         if (name == null || name.isEmpty()) {
-            throw new InvalidParameterValueException("Invalid name for the Kubernetes cluster name:" + name);
+            throw new InvalidParameterValueException("Invalid name for the Kubernetes cluster name: " + name);
         }
 
         if (controlNodeCount < 1) {
             throw new InvalidParameterValueException("Invalid cluster control nodes count: " + controlNodeCount);
         }
-
-        if (clusterSize < 1) {
+        if (clusterSize == null || clusterSize < 1) {
             throw new InvalidParameterValueException("Invalid cluster size: " + clusterSize);
         }
 
         int maxClusterSize = KubernetesMaxClusterSize.valueIn(owner.getId());
+        final long totalNodeCount = controlNodeCount + clusterSize;
         if (totalNodeCount > maxClusterSize) {
             throw new InvalidParameterValueException(
                 String.format("Maximum cluster size can not exceed %d. Please contact your administrator", maxClusterSize));
         }
 
-        DataCenter zone = dataCenterDao.findById(zoneId);
-        if (zone == null) {
-            throw new InvalidParameterValueException("Unable to find zone by ID: " + zoneId);
-        }
-
-        if (Grouping.AllocationState.Disabled == zone.getAllocationState()) {
-            throw new PermissionDeniedException(String.format("Cannot perform this operation, zone ID: %s is currently disabled", zone.getUuid()));
-        }
+        DataCenter zone = validateAndGetZoneForKubernetesCreateParameters(zoneId);
 
         if (!isKubernetesServiceConfigured(zone)) {
             throw new CloudRuntimeException("Kubernetes service has not been configured properly to provision Kubernetes clusters");
@@ -694,12 +794,7 @@
             throw new InvalidParameterValueException("No service offering with ID: " + serviceOfferingId);
         }
 
-        if (sshKeyPair != null && !sshKeyPair.isEmpty()) {
-            SSHKeyPairVO sshKeyPairVO = sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), sshKeyPair);
-            if (sshKeyPairVO == null) {
-                throw new InvalidParameterValueException(String.format("Given SSH key pair with name: %s was not found for the account %s", sshKeyPair, owner.getAccountName()));
-            }
-        }
+        validateSshKeyPairForKubernetesCreateParameters(sshKeyPair, owner);
 
         if (nodeRootDiskSize != null && nodeRootDiskSize <= 0) {
             throw new InvalidParameterValueException(String.format("Invalid value for %s", ApiConstants.NODE_ROOT_DISK_SIZE));
@@ -711,13 +806,7 @@
 
         validateDockerRegistryParams(dockerRegistryUserName, dockerRegistryPassword, dockerRegistryUrl);
 
-        Network network = null;
-        if (networkId != null) {
-            network = networkService.getNetwork(networkId);
-            if (network == null) {
-                throw new InvalidParameterValueException("Unable to find network with given ID");
-            }
-        }
+        Network network = validateAndGetNetworkForKubernetesCreateParameters(networkId);
 
         if (StringUtils.isNotEmpty(externalLoadBalancerIpAddress)) {
             if (!NetUtils.isValidIp4(externalLoadBalancerIpAddress) && !NetUtils.isValidIp6(externalLoadBalancerIpAddress)) {
@@ -788,15 +877,16 @@
                 List<KubernetesClusterDetailsVO> details = new ArrayList<>();
                 long kubernetesClusterId = kubernetesCluster.getId();
 
-                if (Network.GuestType.Shared.equals(network.getGuestType())) {
+                if ((network != null && Network.GuestType.Shared.equals(network.getGuestType())) || kubernetesCluster.getClusterType() == KubernetesCluster.ClusterType.ExternalManaged) {
                     addKubernetesClusterDetailIfIsNotEmpty(details, kubernetesClusterId, ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS, externalLoadBalancerIpAddress, true);
                 }
 
                 addKubernetesClusterDetailIfIsNotEmpty(details, kubernetesClusterId, ApiConstants.DOCKER_REGISTRY_USER_NAME, dockerRegistryUserName, true);
                 addKubernetesClusterDetailIfIsNotEmpty(details, kubernetesClusterId, ApiConstants.DOCKER_REGISTRY_PASSWORD, dockerRegistryPassword, false);
                 addKubernetesClusterDetailIfIsNotEmpty(details, kubernetesClusterId, ApiConstants.DOCKER_REGISTRY_URL, dockerRegistryUrl, true);
-
-                details.add(new KubernetesClusterDetailsVO(kubernetesClusterId, "networkCleanup", String.valueOf(networkCleanup), true));
+                if (kubernetesCluster.getClusterType() == KubernetesCluster.ClusterType.CloudManaged) {
+                    details.add(new KubernetesClusterDetailsVO(kubernetesClusterId, "networkCleanup", String.valueOf(networkCleanup), true));
+                }
                 kubernetesClusterDetailsDao.saveDetails(details);
             }
         });
@@ -865,6 +955,9 @@
 
         Account caller = CallContext.current().getCallingAccount();
         accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
+        if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
+            throw new InvalidParameterValueException(String.format("Scale kubernetes cluster is not supported for an externally managed cluster (%s)", kubernetesCluster.getName()));
+        }
 
         final KubernetesSupportedVersion clusterVersion = kubernetesSupportedVersionDao.findById(kubernetesCluster.getKubernetesVersionId());
         if (clusterVersion == null) {
@@ -973,6 +1066,9 @@
         if (kubernetesCluster == null || kubernetesCluster.getRemoved() != null) {
             throw new InvalidParameterValueException("Invalid Kubernetes cluster ID");
         }
+        if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
+            throw new InvalidParameterValueException(String.format("Upgrade kubernetes cluster is not supported for an externally managed cluster (%s)", kubernetesCluster.getName()));
+        }
         accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
         if (!KubernetesCluster.State.Running.equals(kubernetesCluster.getState())) {
             throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s is not in running state", kubernetesCluster.getName()));
@@ -1032,12 +1128,62 @@
     }
 
     @Override
-    public KubernetesCluster createKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
+    public KubernetesCluster createUnmanagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
         if (!KubernetesServiceEnabled.value()) {
             logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
         }
 
-        validateKubernetesClusterCreateParameters(cmd);
+        validateUnmanagedKubernetesClusterCreateParameters(cmd);
+
+        final DataCenter zone = dataCenterDao.findById(cmd.getZoneId());
+        final long controlNodeCount = cmd.getControlNodes();
+        final long clusterSize = Objects.requireNonNullElse(cmd.getClusterSize(), 0L);
+        final ServiceOffering serviceOffering = serviceOfferingDao.findById(cmd.getServiceOfferingId());
+        final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
+        final KubernetesSupportedVersion clusterKubernetesVersion = kubernetesSupportedVersionDao.findById(cmd.getKubernetesVersionId());
+
+        final Network network = networkDao.findById(cmd.getNetworkId());
+        long cores = 0;
+        long memory = 0;
+        Long serviceOfferingId = null;
+        if (serviceOffering != null) {
+            serviceOfferingId = serviceOffering.getId();
+            cores = serviceOffering.getCpu() * (controlNodeCount + clusterSize);
+            memory = serviceOffering.getRamSize() * (controlNodeCount + clusterSize);
+        }
+
+        final Long finalServiceOfferingId = serviceOfferingId;
+        final Long defaultNetworkId = network == null ? null : network.getId();
+        final Long clusterKubernetesVersionId = clusterKubernetesVersion == null ? null : clusterKubernetesVersion.getId();
+        final long finalCores = cores;
+        final long finalMemory = memory;
+        final KubernetesClusterVO cluster = Transaction.execute(new TransactionCallback<KubernetesClusterVO>() {
+            @Override
+            public KubernetesClusterVO doInTransaction(TransactionStatus status) {
+                KubernetesClusterVO newCluster = new KubernetesClusterVO(cmd.getName(), cmd.getDisplayName(), zone.getId(), clusterKubernetesVersionId,
+                        finalServiceOfferingId, null, defaultNetworkId, owner.getDomainId(),
+                        owner.getAccountId(), controlNodeCount, clusterSize, KubernetesCluster.State.Running, cmd.getSSHKeyPairName(), finalCores, finalMemory,
+                        cmd.getNodeRootDiskSize(), "", KubernetesCluster.ClusterType.ExternalManaged);
+                kubernetesClusterDao.persist(newCluster);
+                return newCluster;
+            }
+        });
+
+        addKubernetesClusterDetails(cluster, network, cmd);
+
+        if (LOGGER.isInfoEnabled()) {
+            LOGGER.info(String.format("Kubernetes cluster with name: %s and ID: %s has been created", cluster.getName(), cluster.getUuid()));
+        }
+        return cluster;
+    }
+
+    @Override
+    public KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException {
+        if (!KubernetesServiceEnabled.value()) {
+            logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
+        }
+
+        validateManagedKubernetesClusterCreateParameters(cmd);
 
         final DataCenter zone = dataCenterDao.findById(cmd.getZoneId());
         final long controlNodeCount = cmd.getControlNodes();
@@ -1086,7 +1232,8 @@
             public KubernetesClusterVO doInTransaction(TransactionStatus status) {
                 KubernetesClusterVO newCluster = new KubernetesClusterVO(cmd.getName(), cmd.getDisplayName(), zone.getId(), clusterKubernetesVersion.getId(),
                         serviceOffering.getId(), finalTemplate.getId(), defaultNetwork.getId(), owner.getDomainId(),
-                        owner.getAccountId(), controlNodeCount, clusterSize, KubernetesCluster.State.Created, cmd.getSSHKeyPairName(), cores, memory, cmd.getNodeRootDiskSize(), "");
+                        owner.getAccountId(), controlNodeCount, clusterSize, KubernetesCluster.State.Created, cmd.getSSHKeyPairName(), cores, memory,
+                        cmd.getNodeRootDiskSize(), "", KubernetesCluster.ClusterType.CloudManaged);
                 if (zone.isSecurityGroupEnabled()) {
                     newCluster.setSecurityGroupId(finalSecurityGroupVO.getId());
                 }
@@ -1183,7 +1330,8 @@
     }
 
     @Override
-    public boolean stopKubernetesCluster(long kubernetesClusterId) throws CloudRuntimeException {
+    public boolean stopKubernetesCluster(StopKubernetesClusterCmd cmd) throws CloudRuntimeException {
+        long kubernetesClusterId = cmd.getId();
         if (!KubernetesServiceEnabled.value()) {
             logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
         }
@@ -1191,6 +1339,9 @@
         if (kubernetesCluster == null) {
             throw new InvalidParameterValueException("Failed to find Kubernetes cluster with given ID");
         }
+        if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
+            throw new InvalidParameterValueException(String.format("Stop kubernetes cluster is not supported for an externally managed cluster (%s)", kubernetesCluster.getName()));
+        }
         if (kubernetesCluster.getRemoved() != null) {
             throw new InvalidParameterValueException(String.format("Kubernetes cluster : %s is already deleted", kubernetesCluster.getName()));
         }
@@ -1213,18 +1364,51 @@
     }
 
     @Override
-    public boolean deleteKubernetesCluster(Long kubernetesClusterId) throws CloudRuntimeException {
+    public boolean deleteKubernetesCluster(DeleteKubernetesClusterCmd cmd) throws CloudRuntimeException {
         if (!KubernetesServiceEnabled.value()) {
             logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
         }
+        Long kubernetesClusterId = cmd.getId();
         KubernetesClusterVO cluster = kubernetesClusterDao.findById(kubernetesClusterId);
         if (cluster == null) {
             throw new InvalidParameterValueException("Invalid cluster id specified");
         }
         accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, cluster);
-        KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(cluster, this);
-        destroyWorker = ComponentContext.inject(destroyWorker);
-        return destroyWorker.destroy();
+        if (cluster.getClusterType() == KubernetesCluster.ClusterType.CloudManaged) {
+            KubernetesClusterDestroyWorker destroyWorker = new KubernetesClusterDestroyWorker(cluster, this);
+            destroyWorker = ComponentContext.inject(destroyWorker);
+            return destroyWorker.destroy();
+        } else {
+            boolean cleanup = cmd.getCleanup();
+            boolean expunge = cmd.getExpunge();
+            if (cleanup || expunge) {
+                CallContext ctx = CallContext.current();
+
+                if (expunge && !accountManager.isAdmin(ctx.getCallingAccount().getId()) && !AllowUserExpungeRecoverVm.valueIn(cmd.getEntityOwnerId())) {
+                    throw new PermissionDeniedException("Parameter " + ApiConstants.EXPUNGE + " can be passed by Admin only. Or when the allow.user.expunge.recover.vm key is set.");
+                }
+
+                List<KubernetesClusterVmMapVO> vmMapList = kubernetesClusterVmMapDao.listByClusterId(kubernetesClusterId);
+                for (KubernetesClusterVmMapVO vmMap : vmMapList) {
+                    try {
+                        userVmService.destroyVm(vmMap.getVmId(), expunge);
+                        if (expunge) {
+                            userVmService.expungeVm(vmMap.getVmId());
+                        }
+                    } catch (Exception exception) {
+                        logMessage(Level.WARN, String.format("Failed to destroy vm %d", vmMap.getVmId()), exception);
+                    }
+                }
+            }
+            return Transaction.execute(new TransactionCallback<Boolean>() {
+                @Override
+                public Boolean doInTransaction(TransactionStatus status) {
+                    kubernetesClusterDetailsDao.removeDetails(kubernetesClusterId);
+                    kubernetesClusterVmMapDao.removeByClusterId(kubernetesClusterId);
+                    return kubernetesClusterDao.remove(kubernetesClusterId);
+                }
+            });
+        }
     }
 
     @Override
@@ -1238,6 +1422,7 @@
         final String state = cmd.getState();
         final String name = cmd.getName();
         final String keyword = cmd.getKeyword();
+        final String cmdClusterType = cmd.getClusterType();
         List<KubernetesClusterResponse> responsesList = new ArrayList<KubernetesClusterResponse>();
         List<Long> permittedAccounts = new ArrayList<Long>();
         Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, Project.ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
@@ -1245,6 +1430,17 @@
         Long domainId = domainIdRecursiveListProject.first();
         Boolean isRecursive = domainIdRecursiveListProject.second();
         Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+
+        KubernetesCluster.ClusterType clusterType = null;
+
+        if (cmdClusterType != null) {
+            try {
+             clusterType = KubernetesCluster.ClusterType.valueOf(cmdClusterType);
+            } catch (IllegalArgumentException exception) {
+                throw new InvalidParameterValueException("Unable to resolve cluster type " + cmdClusterType + " to a supported value (CloudManaged, ExternalManaged)");
+            }
+        }
+
         Filter searchFilter = new Filter(KubernetesClusterVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
         SearchBuilder<KubernetesClusterVO> sb = kubernetesClusterDao.createSearchBuilder();
         accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
@@ -1252,6 +1448,7 @@
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
         sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
         sb.and("state", sb.entity().getState(), SearchCriteria.Op.IN);
+        sb.and("cluster_type", sb.entity().getClusterType(), SearchCriteria.Op.EQ);
         SearchCriteria<KubernetesClusterVO> sc = sb.create();
         accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
         if (state != null) {
@@ -1266,6 +1463,9 @@
         if (name != null) {
             sc.setParameters("name", name);
         }
+        if (clusterType != null) {
+            sc.setParameters("cluster_type", clusterType);
+        }
         List<KubernetesClusterVO> kubernetesClusters = kubernetesClusterDao.search(sc, searchFilter);
         for (KubernetesClusterVO cluster : kubernetesClusters) {
             KubernetesClusterResponse clusterResponse = createKubernetesClusterResponse(cluster.getId());
@@ -1342,6 +1542,114 @@
         return upgradeWorker.upgradeCluster();
     }
 
+    private void updateNodeCount(KubernetesClusterVO kubernetesCluster) {
+        List<KubernetesClusterVmMapVO> nodeList = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
+        kubernetesCluster.setControlNodeCount(nodeList.stream().filter(KubernetesClusterVmMapVO::isControlNode).count());
+        kubernetesCluster.setNodeCount(nodeList.size());
+        kubernetesCluster.setNodeCount(nodeList.size());
+        kubernetesClusterDao.persist(kubernetesCluster);
+    }
+
+    @Override
+    public boolean addVmsToCluster(AddVirtualMachinesToKubernetesClusterCmd cmd) {
+        if (!KubernetesServiceEnabled.value()) {
+            logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
+        }
+        List<Long> vmIds = cmd.getVmIds();
+        Long clusterId = cmd.getId();
+
+        KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(clusterId);
+        if (kubernetesCluster == null) {
+            throw new InvalidParameterValueException("Invalid Kubernetes cluster ID specified");
+        }
+        if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
+            throw new InvalidParameterValueException("VM cannot be added to a CloudStack managed Kubernetes cluster");
+        }
+
+        // User should have access to both VM and Kubernetes cluster
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
+
+        for (Long vmId : vmIds) {
+            VMInstanceVO vmInstance = vmInstanceDao.findById(vmId);
+            if (vmInstance == null) {
+                throw new InvalidParameterValueException("Invalid VM ID specified");
+            }
+            accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, vmInstance);
+        }
+
+        KubernetesClusterVmMapVO clusterVmMap = null;
+        List<KubernetesClusterVmMapVO> clusterVmMapList = kubernetesClusterVmMapDao.listByClusterIdAndVmIdsIn(clusterId, vmIds);
+        ArrayList<Long> alreadyExistingVmIds = new ArrayList<>();
+        for (KubernetesClusterVmMapVO clusterVmMapVO : clusterVmMapList) {
+            alreadyExistingVmIds.add(clusterVmMapVO.getVmId());
+        }
+        vmIds.removeAll(alreadyExistingVmIds);
+        for (Long vmId : vmIds) {
+            clusterVmMap = new KubernetesClusterVmMapVO(clusterId, vmId, cmd.isControlNode());
+            kubernetesClusterVmMapDao.persist(clusterVmMap);
+        }
+        updateNodeCount(kubernetesCluster);
+        return true;
+    }
+
+    @Override
+    public List<RemoveVirtualMachinesFromKubernetesClusterResponse> removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd) {
+        if (!KubernetesServiceEnabled.value()) {
+            logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
+        }
+        List<Long> vmIds = cmd.getVmIds();
+        Long clusterId = cmd.getId();
+
+        KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(clusterId);
+        if (kubernetesCluster == null) {
+            throw new InvalidParameterValueException("Invalid Kubernetes cluster ID specified");
+        }
+        if (!isCommandSupported(kubernetesCluster, cmd.getActualCommandName())) {
+            throw new InvalidParameterValueException("VM cannot be removed from a CloudStack Managed Kubernetes cluster");
+        }
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
+
+        List<KubernetesClusterVmMapVO> kubernetesClusterVmMap = kubernetesClusterVmMapDao.listByClusterIdAndVmIdsIn(clusterId, vmIds);
+        List<RemoveVirtualMachinesFromKubernetesClusterResponse> responseList = new ArrayList<>();
+
+        Set<Long> vmIdsRemoved = new HashSet<>();
+
+        for (KubernetesClusterVmMapVO clusterVmMap : kubernetesClusterVmMap) {
+            RemoveVirtualMachinesFromKubernetesClusterResponse response = new RemoveVirtualMachinesFromKubernetesClusterResponse();
+            UserVm vm = userVmService.getUserVm(clusterVmMap.getVmId());
+            response.setVmId(vm.getUuid());
+            response.setSuccess(kubernetesClusterVmMapDao.remove(clusterVmMap.getId()));
+            response.setObjectName(cmd.getCommandName());
+            responseList.add(response);
+            vmIdsRemoved.add(clusterVmMap.getVmId());
+        }
+
+        for (Long vmId : vmIds) {
+            if (!vmIdsRemoved.contains(vmId)) {
+                RemoveVirtualMachinesFromKubernetesClusterResponse response = new RemoveVirtualMachinesFromKubernetesClusterResponse();
+                VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(vmId);
+                if (vm == null) {
+                    response.setVmId(vmId.toString());
+                    response.setDisplayText("Not a valid vm id");
+                    vmIdsRemoved.add(vmId);
+                } else {
+                    response.setVmId(vm.getUuid());
+                    vmIdsRemoved.add(vmId);
+                    if (vm.isRemoved()) {
+                        response.setDisplayText("VM is already removed");
+                    } else {
+                        response.setDisplayText("VM is not part of the cluster");
+                    }
+                }
+                response.setObjectName(cmd.getCommandName());
+                response.setSuccess(false);
+                responseList.add(response);
+            }
+        }
+        updateNodeCount(kubernetesCluster);
+        return responseList;
+    }
+
     @Override
     public List<Class<?>> getCommands() {
         List<Class<?>> cmdList = new ArrayList<Class<?>>();
@@ -1356,6 +1664,8 @@
         cmdList.add(GetKubernetesClusterConfigCmd.class);
         cmdList.add(ScaleKubernetesClusterCmd.class);
         cmdList.add(UpgradeKubernetesClusterCmd.class);
+        cmdList.add(AddVirtualMachinesToKubernetesClusterCmd.class);
+        cmdList.add(RemoveVirtualMachinesFromKubernetesClusterCmd.class);
         return cmdList;
     }
 
@@ -1442,7 +1752,7 @@
         public void reallyRun() {
             try {
                 // run through Kubernetes clusters in 'Running' state and ensure all the VM's are Running in the cluster
-                List<KubernetesClusterVO> runningKubernetesClusters = kubernetesClusterDao.findKubernetesClustersInState(KubernetesCluster.State.Running);
+                List<KubernetesClusterVO> runningKubernetesClusters = kubernetesClusterDao.findManagedKubernetesClustersInState(KubernetesCluster.State.Running);
                 for (KubernetesCluster kubernetesCluster : runningKubernetesClusters) {
                     if (LOGGER.isInfoEnabled()) {
                         LOGGER.info(String.format("Running Kubernetes cluster state scanner on Kubernetes cluster : %s", kubernetesCluster.getName()));
@@ -1457,7 +1767,7 @@
                 }
 
                 // run through Kubernetes clusters in 'Stopped' state and ensure all the VM's are Stopped in the cluster
-                List<KubernetesClusterVO> stoppedKubernetesClusters = kubernetesClusterDao.findKubernetesClustersInState(KubernetesCluster.State.Stopped);
+                List<KubernetesClusterVO> stoppedKubernetesClusters = kubernetesClusterDao.findManagedKubernetesClustersInState(KubernetesCluster.State.Stopped);
                 for (KubernetesCluster kubernetesCluster : stoppedKubernetesClusters) {
                     if (LOGGER.isInfoEnabled()) {
                         LOGGER.info(String.format("Running Kubernetes cluster state scanner on Kubernetes cluster : %s for state: %s", kubernetesCluster.getName(), KubernetesCluster.State.Stopped.toString()));
@@ -1472,7 +1782,7 @@
                 }
 
                 // run through Kubernetes clusters in 'Alert' state and reconcile state as 'Running' if the VM's are running or 'Stopped' if VM's are stopped
-                List<KubernetesClusterVO> alertKubernetesClusters = kubernetesClusterDao.findKubernetesClustersInState(KubernetesCluster.State.Alert);
+                List<KubernetesClusterVO> alertKubernetesClusters = kubernetesClusterDao.findManagedKubernetesClustersInState(KubernetesCluster.State.Alert);
                 for (KubernetesClusterVO kubernetesCluster : alertKubernetesClusters) {
                     if (LOGGER.isInfoEnabled()) {
                         LOGGER.info(String.format("Running Kubernetes cluster state scanner on Kubernetes cluster : %s for state: %s", kubernetesCluster.getName(), KubernetesCluster.State.Alert.toString()));
@@ -1495,7 +1805,7 @@
 
                 if (firstRun) {
                     // run through Kubernetes clusters in 'Starting' state and reconcile state as 'Alert' or 'Error' if the VM's are running
-                    List<KubernetesClusterVO> startingKubernetesClusters = kubernetesClusterDao.findKubernetesClustersInState(KubernetesCluster.State.Starting);
+                    List<KubernetesClusterVO> startingKubernetesClusters = kubernetesClusterDao.findManagedKubernetesClustersInState(KubernetesCluster.State.Starting);
                     for (KubernetesCluster kubernetesCluster : startingKubernetesClusters) {
                         if ((new Date()).getTime() - kubernetesCluster.getCreated().getTime() < 10*60*1000) {
                             continue;
@@ -1513,7 +1823,7 @@
                             LOGGER.warn(String.format("Failed to run Kubernetes cluster Starting state scanner on Kubernetes cluster : %s status scanner", kubernetesCluster.getName()), e);
                         }
                     }
-                    List<KubernetesClusterVO> destroyingKubernetesClusters = kubernetesClusterDao.findKubernetesClustersInState(KubernetesCluster.State.Destroying);
+                    List<KubernetesClusterVO> destroyingKubernetesClusters = kubernetesClusterDao.findManagedKubernetesClustersInState(KubernetesCluster.State.Destroying);
                     for (KubernetesCluster kubernetesCluster : destroyingKubernetesClusters) {
                         if (LOGGER.isInfoEnabled()) {
                             LOGGER.info(String.format("Running Kubernetes cluster state scanner on Kubernetes cluster : %s for state: %s", kubernetesCluster.getName(), KubernetesCluster.State.Destroying.toString()));
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java
index 420f355..0a988e5 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterService.java
@@ -16,20 +16,27 @@
 // under the License.
 package com.cloud.kubernetes.cluster;
 
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.GetKubernetesClusterConfigCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.ListKubernetesClustersCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
 import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
 import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
 import org.apache.cloudstack.api.response.KubernetesClusterResponse;
 import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.RemoveVirtualMachinesFromKubernetesClusterResponse;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 
 import com.cloud.utils.component.PluggableService;
 import com.cloud.utils.exception.CloudRuntimeException;
 
+import java.util.List;
+
 public interface KubernetesClusterService extends PluggableService, Configurable {
     static final String MIN_KUBERNETES_VERSION_HA_SUPPORT = "1.16.0";
     static final int MIN_KUBERNETES_CLUSTER_NODE_CPU = 2;
@@ -38,7 +45,7 @@
 
     static final ConfigKey<Boolean> KubernetesServiceEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
             "cloud.kubernetes.service.enabled",
-            "false",
+            "true",
             "Indicates whether Kubernetes Service plugin is enabled or not. Management server restart needed on change",
             false);
     static final ConfigKey<String> KubernetesClusterNetworkOffering = new ConfigKey<String>("Advanced", String.class,
@@ -81,13 +88,17 @@
 
     KubernetesCluster findById(final Long id);
 
-    KubernetesCluster createKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException;
+    KubernetesCluster createUnmanagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException;
+
+    KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterCmd cmd) throws CloudRuntimeException;
 
     boolean startKubernetesCluster(long kubernetesClusterId, boolean onCreate) throws CloudRuntimeException;
 
-    boolean stopKubernetesCluster(long kubernetesClusterId) throws CloudRuntimeException;
+    boolean stopKubernetesCluster(StopKubernetesClusterCmd cmd) throws CloudRuntimeException;
 
-    boolean deleteKubernetesCluster(Long kubernetesClusterId) throws CloudRuntimeException;
+    boolean deleteKubernetesCluster(DeleteKubernetesClusterCmd cmd) throws CloudRuntimeException;
+
+    boolean isCommandSupported(KubernetesCluster cluster, String cmdName);
 
     ListResponse<KubernetesClusterResponse> listKubernetesClusters(ListKubernetesClustersCmd cmd);
 
@@ -98,4 +109,8 @@
     boolean scaleKubernetesCluster(ScaleKubernetesClusterCmd cmd) throws CloudRuntimeException;
 
     boolean upgradeKubernetesCluster(UpgradeKubernetesClusterCmd cmd) throws CloudRuntimeException;
+
+    boolean addVmsToCluster(AddVirtualMachinesToKubernetesClusterCmd cmd);
+
+    List<RemoveVirtualMachinesFromKubernetesClusterResponse> removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd);
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
index 1b30b1b..270916a 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterVO.java
@@ -52,16 +52,16 @@
     private long zoneId;
 
     @Column(name = "kubernetes_version_id")
-    private long kubernetesVersionId;
+    private Long kubernetesVersionId;
 
     @Column(name = "service_offering_id")
-    private long serviceOfferingId;
+    private Long serviceOfferingId;
 
     @Column(name = "template_id")
-    private long templateId;
+    private Long templateId;
 
     @Column(name = "network_id")
-    private long networkId;
+    private Long networkId;
 
     @Column(name = "domain_id")
     private long domainId;
@@ -114,6 +114,9 @@
     @Column(name = "security_group_id")
     private Long securityGroupId;
 
+    @Column(name = "cluster_type")
+    private ClusterType clusterType;
+
     @Override
     public long getId() {
         return id;
@@ -160,7 +163,7 @@
     }
 
     @Override
-    public long getKubernetesVersionId() {
+    public Long getKubernetesVersionId() {
         return kubernetesVersionId;
     }
 
@@ -169,7 +172,7 @@
     }
 
     @Override
-    public long getServiceOfferingId() {
+    public Long getServiceOfferingId() {
         return serviceOfferingId;
     }
 
@@ -178,7 +181,7 @@
     }
 
     @Override
-    public long getTemplateId() {
+    public Long getTemplateId() {
         return templateId;
     }
 
@@ -187,7 +190,7 @@
     }
 
     @Override
-    public long getNetworkId() {
+    public Long getNetworkId() {
         return networkId;
     }
 
@@ -350,13 +353,21 @@
         return securityGroupId;
     }
 
+    public ClusterType getClusterType() {
+        return clusterType;
+    }
+
+    public void setClusterType(ClusterType clusterType) {
+        this.clusterType = clusterType;
+    }
+
     public KubernetesClusterVO() {
         this.uuid = UUID.randomUUID().toString();
     }
 
-    public KubernetesClusterVO(String name, String description, long zoneId, long kubernetesVersionId, long serviceOfferingId, long templateId,
-                               long networkId, long domainId, long accountId, long controlNodeCount, long nodeCount, State state,
-                               String keyPair, long cores, long memory, Long nodeRootDiskSize, String endpoint) {
+    public KubernetesClusterVO(String name, String description, long zoneId, Long kubernetesVersionId, Long serviceOfferingId, Long templateId,
+                               Long networkId, long domainId, long accountId, long controlNodeCount, long nodeCount, State state,
+                               String keyPair, long cores, long memory, Long nodeRootDiskSize, String endpoint, ClusterType clusterType) {
         this.uuid = UUID.randomUUID().toString();
         this.name = name;
         this.description = description;
@@ -377,14 +388,15 @@
             this.nodeRootDiskSize = nodeRootDiskSize;
         }
         this.endpoint = endpoint;
+        this.clusterType = clusterType;
         this.checkForGc = false;
     }
 
     public KubernetesClusterVO(String name, String description, long zoneId, long kubernetesVersionId, long serviceOfferingId, long templateId,
         long networkId, long domainId, long accountId, long controlNodeCount, long nodeCount, State state, String keyPair, long cores,
-        long memory, Long nodeRootDiskSize, String endpoint, boolean autoscalingEnabled, Long minSize, Long maxSize) {
+        long memory, Long nodeRootDiskSize, String endpoint, ClusterType clusterType, boolean autoscalingEnabled, Long minSize, Long maxSize) {
         this(name, description, zoneId, kubernetesVersionId, serviceOfferingId, templateId, networkId, domainId, accountId, controlNodeCount,
-            nodeCount, state, keyPair, cores, memory, nodeRootDiskSize, endpoint);
+            nodeCount, state, keyPair, cores, memory, nodeRootDiskSize, endpoint, clusterType);
         this.autoscalingEnabled = autoscalingEnabled;
         this.minSize = minSize;
         this.maxSize = maxSize;
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDao.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDao.java
index fe67323..9341912 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDao.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDao.java
@@ -28,7 +28,7 @@
 
     List<KubernetesClusterVO> listByAccount(long accountId);
     List<KubernetesClusterVO> findKubernetesClustersToGarbageCollect();
-    List<KubernetesClusterVO> findKubernetesClustersInState(KubernetesCluster.State state);
+    List<KubernetesClusterVO> findManagedKubernetesClustersInState(KubernetesCluster.State state);
     List<KubernetesClusterVO> listByNetworkId(long networkId);
     List<KubernetesClusterVO> listAllByKubernetesVersion(long kubernetesVersionId);
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDaoImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDaoImpl.java
index 003286c..63cca35 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDaoImpl.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterDaoImpl.java
@@ -32,7 +32,7 @@
 
     private final SearchBuilder<KubernetesClusterVO> AccountIdSearch;
     private final SearchBuilder<KubernetesClusterVO> GarbageCollectedSearch;
-    private final SearchBuilder<KubernetesClusterVO> StateSearch;
+    private final SearchBuilder<KubernetesClusterVO> ManagedStateSearch;
     private final SearchBuilder<KubernetesClusterVO> SameNetworkSearch;
     private final SearchBuilder<KubernetesClusterVO> KubernetesVersionSearch;
 
@@ -44,11 +44,13 @@
         GarbageCollectedSearch = createSearchBuilder();
         GarbageCollectedSearch.and("gc", GarbageCollectedSearch.entity().isCheckForGc(), SearchCriteria.Op.EQ);
         GarbageCollectedSearch.and("state", GarbageCollectedSearch.entity().getState(), SearchCriteria.Op.EQ);
+        GarbageCollectedSearch.and("cluster_type", GarbageCollectedSearch.entity().getClusterType(), SearchCriteria.Op.EQ);
         GarbageCollectedSearch.done();
 
-        StateSearch = createSearchBuilder();
-        StateSearch.and("state", StateSearch.entity().getState(), SearchCriteria.Op.EQ);
-        StateSearch.done();
+        ManagedStateSearch = createSearchBuilder();
+        ManagedStateSearch.and("state", ManagedStateSearch.entity().getState(), SearchCriteria.Op.EQ);
+        ManagedStateSearch.and("cluster_type", ManagedStateSearch.entity().getClusterType(), SearchCriteria.Op.EQ);
+        ManagedStateSearch.done();
 
         SameNetworkSearch = createSearchBuilder();
         SameNetworkSearch.and("network_id", SameNetworkSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
@@ -71,13 +73,15 @@
         SearchCriteria<KubernetesClusterVO> sc = GarbageCollectedSearch.create();
         sc.setParameters("gc", true);
         sc.setParameters("state", KubernetesCluster.State.Destroying);
+        sc.setParameters("cluster_type", KubernetesCluster.ClusterType.CloudManaged);
         return listBy(sc);
     }
 
     @Override
-    public List<KubernetesClusterVO> findKubernetesClustersInState(KubernetesCluster.State state) {
-        SearchCriteria<KubernetesClusterVO> sc = StateSearch.create();
+    public List<KubernetesClusterVO> findManagedKubernetesClustersInState(KubernetesCluster.State state) {
+        SearchCriteria<KubernetesClusterVO> sc = ManagedStateSearch.create();
         sc.setParameters("state", state);
+        sc.setParameters("cluster_type", KubernetesCluster.ClusterType.CloudManaged);
         return listBy(sc);
     }
 
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java
index 42061cd..688a611 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java
@@ -24,4 +24,8 @@
 public interface KubernetesClusterVmMapDao extends GenericDao<KubernetesClusterVmMapVO, Long> {
     public List<KubernetesClusterVmMapVO> listByClusterId(long clusterId);
     public List<KubernetesClusterVmMapVO> listByClusterIdAndVmIdsIn(long clusterId, List<Long> vmIds);
+
+    int removeByClusterIdAndVmIdsIn(long clusterId, List<Long> vmIds);
+
+    public int removeByClusterId(long clusterId);
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java
index c5a9ad4..b9f2ec9 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java
@@ -54,4 +54,19 @@
         sc.setParameters("vmIdsIN", vmIds.toArray());
         return listBy(sc);
     }
+
+    @Override
+    public int removeByClusterIdAndVmIdsIn(long clusterId, List<Long> vmIds) {
+        SearchCriteria<KubernetesClusterVmMapVO> sc = clusterIdSearch.create();
+        sc.setParameters("clusterId", clusterId);
+        sc.setParameters("vmIdsIN", vmIds.toArray());
+        return remove(sc);
+    }
+
+    @Override
+    public int removeByClusterId(long clusterId) {
+        SearchCriteria<KubernetesClusterVmMapVO> sc = clusterIdSearch.create();
+        sc.setParameters("clusterId", clusterId);
+        return remove(sc);
+    }
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/AddVirtualMachinesToKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/AddVirtualMachinesToKubernetesClusterCmd.java
new file mode 100644
index 0000000..a7134f5
--- /dev/null
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/AddVirtualMachinesToKubernetesClusterCmd.java
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.user.kubernetes.cluster;
+
+import com.cloud.kubernetes.cluster.KubernetesClusterService;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.KubernetesClusterResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.List;
+
+@APICommand(name = "addVirtualMachinesToKubernetesCluster",
+        description = "Add VMs to an ExternalManaged kubernetes cluster. Not applicable for CloudManaged kubernetes clusters.",
+        responseObject = SuccessResponse.class,
+        since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class AddVirtualMachinesToKubernetesClusterCmd extends BaseCmd {
+    public static final Logger LOGGER = Logger.getLogger(AddVirtualMachinesToKubernetesClusterCmd.class.getName());
+
+    @Inject
+    public KubernetesClusterService kubernetesClusterService;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID, type = CommandType.UUID,
+            entityType = KubernetesClusterResponse.class,
+            required = true,
+            description = "the ID of the Kubernetes cluster")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_IDS, type = CommandType.LIST,
+            collectionType=CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "the IDs of the VMs to add to the cluster")
+    private List<Long> vmIds;
+
+    @Parameter(name = ApiConstants.IS_CONTROL_NODE, type = CommandType.BOOLEAN,
+            description = "Is control node or not? Defaults to false.")
+    private Boolean isControlNode;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public List<Long> getVmIds() {
+        return vmIds;
+    }
+
+    public boolean isControlNode() {
+        return (isControlNode != null) && isControlNode;
+    }
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            if (!kubernetesClusterService.addVmsToCluster(this)) {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VMs to cluster");
+            }
+            final SuccessResponse response = new SuccessResponse();
+            response.setResponseName(getCommandName());
+            setResponseObject(response);
+        } catch (CloudRuntimeException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+}
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
index 5e4bd39..aa53a05 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/CreateKubernetesClusterCmd.java
@@ -20,6 +20,7 @@
 
 import javax.inject.Inject;
 
+import com.cloud.exception.InvalidParameterValueException;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.api.ACL;
@@ -39,6 +40,7 @@
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.kubernetes.cluster.KubernetesCluster;
@@ -68,7 +70,7 @@
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name for the Kubernetes cluster")
     private String name;
 
-    @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = true, description = "description for the Kubernetes cluster")
+    @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "description for the Kubernetes cluster")
     private String description;
 
     @ACL(accessType = AccessType.UseEntry)
@@ -76,13 +78,13 @@
             description = "availability zone in which Kubernetes cluster to be launched")
     private Long zoneId;
 
-    @Parameter(name = ApiConstants.KUBERNETES_VERSION_ID, type = CommandType.UUID, entityType = KubernetesSupportedVersionResponse.class, required = true,
+    @Parameter(name = ApiConstants.KUBERNETES_VERSION_ID, type = CommandType.UUID, entityType = KubernetesSupportedVersionResponse.class,
             description = "Kubernetes version with which cluster to be launched")
     private Long kubernetesVersionId;
 
     @ACL(accessType = AccessType.UseEntry)
     @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class,
-            required = true, description = "the ID of the service offering for the virtual machines in the cluster.")
+            description = "the ID of the service offering for the virtual machines in the cluster.")
     private Long serviceOfferingId;
 
     @ACL(accessType = AccessType.UseEntry)
@@ -124,7 +126,7 @@
     private String externalLoadBalancerIpAddress;
 
     @Parameter(name=ApiConstants.SIZE, type = CommandType.LONG,
-            required = true, description = "number of Kubernetes cluster worker nodes")
+            description = "number of Kubernetes cluster worker nodes")
     private Long clusterSize;
 
     @Parameter(name = ApiConstants.DOCKER_REGISTRY_USER_NAME, type = CommandType.STRING,
@@ -143,6 +145,9 @@
             description = "root disk size in GB for each node")
     private Long nodeRootDiskSize;
 
+    @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, required = true, description = "type of the cluster: CloudManaged, ExternalManaged", since="4.19.0")
+    private String clusterType;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -155,7 +160,7 @@
     }
 
     public String getDisplayName() {
-        return description;
+        return StringUtils.firstNonEmpty(description, name);
     }
 
     public Long getDomainId() {
@@ -232,6 +237,13 @@
         }
     }
 
+    public String getClusterType() {
+        if (clusterType == null) {
+            return KubernetesCluster.ClusterType.CloudManaged.toString();
+        }
+        return clusterType;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -278,7 +290,8 @@
     @Override
     public void execute() {
         try {
-            if (!kubernetesClusterService.startKubernetesCluster(getEntityId(), true)) {
+            if (KubernetesCluster.ClusterType.valueOf(getClusterType()) == KubernetesCluster.ClusterType.CloudManaged
+                    && !kubernetesClusterService.startKubernetesCluster(getEntityId(), true)) {
                 throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Kubernetes cluster");
             }
             KubernetesClusterResponse response = kubernetesClusterService.createKubernetesClusterResponse(getEntityId());
@@ -291,8 +304,20 @@
 
     @Override
     public void create() throws CloudRuntimeException {
+        KubernetesCluster cluster;
+        KubernetesCluster.ClusterType type;
         try {
-            KubernetesCluster cluster = kubernetesClusterService.createKubernetesCluster(this);
+            type = KubernetesCluster.ClusterType.valueOf(getClusterType());
+        } catch (IllegalArgumentException e) {
+            throw new InvalidParameterValueException("Unable to resolve cluster type " + getClusterType() + " to a supported value (CloudManaged, ExternalManaged)");
+        }
+
+        try {
+            if (type == KubernetesCluster.ClusterType.CloudManaged) {
+                cluster = kubernetesClusterService.createManagedKubernetesCluster(this);
+            } else {
+                cluster = kubernetesClusterService.createUnmanagedKubernetesCluster(this);
+            }
             if (cluster == null) {
                 throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Kubernetes cluster");
             }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/DeleteKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/DeleteKubernetesClusterCmd.java
index 564ae78..2b4a128 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/DeleteKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/DeleteKubernetesClusterCmd.java
@@ -58,6 +58,18 @@
             description = "the ID of the Kubernetes cluster")
     private Long id;
 
+    @Parameter(name = ApiConstants.CLEANUP,
+            type = CommandType.BOOLEAN,
+            since = "4.19.0",
+            description = "Destroy attached instances of the ExternalManaged Cluster. Default: false")
+    private Boolean cleanup;
+
+    @Parameter(name = ApiConstants.EXPUNGE,
+            type = CommandType.BOOLEAN,
+            since = "4.19.0",
+            description = "Expunge attached instances of the ExternalManaged Cluster. If true, value of cleanup is ignored. Default: false")
+    private Boolean expunge;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -66,6 +78,14 @@
         return id;
     }
 
+    public Boolean getCleanup() {
+        return cleanup != null && cleanup;
+    }
+
+    public Boolean getExpunge() {
+        return expunge != null && expunge;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -73,7 +93,7 @@
     @Override
     public void execute() throws ServerApiException, ConcurrentOperationException {
         try {
-            if (!kubernetesClusterService.deleteKubernetesCluster(id)) {
+            if (!kubernetesClusterService.deleteKubernetesCluster(this)) {
                 throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to delete Kubernetes cluster ID: %d", getId()));
             }
             SuccessResponse response = new SuccessResponse(getCommandName());
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ListKubernetesClustersCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ListKubernetesClustersCmd.java
index 692b934..33eab2c 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ListKubernetesClustersCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ListKubernetesClustersCmd.java
@@ -61,6 +61,10 @@
             " (a substring match is made against the parameter value, data for all matching Kubernetes clusters will be returned)")
     private String name;
 
+    @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, since = "4.19.0",
+            description = "type of the cluster: CloudManaged, ExternalManaged")
+    private String clusterType;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -77,6 +81,10 @@
         return name;
     }
 
+    public String getClusterType() {
+        return clusterType;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/RemoveVirtualMachinesFromKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/RemoveVirtualMachinesFromKubernetesClusterCmd.java
new file mode 100644
index 0000000..704d0b2
--- /dev/null
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/RemoveVirtualMachinesFromKubernetesClusterCmd.java
@@ -0,0 +1,103 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.user.kubernetes.cluster;
+
+import com.cloud.kubernetes.cluster.KubernetesClusterService;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.KubernetesClusterResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.RemoveVirtualMachinesFromKubernetesClusterResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.List;
+
+@APICommand(name = "removeVirtualMachinesFromKubernetesCluster",
+        description = "Remove VMs from an ExternalManaged kubernetes cluster. Not applicable for CloudManaged kubernetes clusters.",
+        responseObject = RemoveVirtualMachinesFromKubernetesClusterResponse.class,
+        since = "4.19.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class RemoveVirtualMachinesFromKubernetesClusterCmd extends BaseListCmd {
+    public static final Logger LOGGER = Logger.getLogger(RemoveVirtualMachinesFromKubernetesClusterCmd.class.getName());
+
+    @Inject
+    public KubernetesClusterService kubernetesClusterService;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID,
+            entityType = KubernetesClusterResponse.class,
+            required = true,
+            description = "the ID of the Kubernetes cluster")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_IDS, type = CommandType.LIST,
+            collectionType=CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "the IDs of the VMs to remove from the cluster")
+    private List<Long> vmIds;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public List<Long> getVmIds() {
+        return vmIds;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            List<RemoveVirtualMachinesFromKubernetesClusterResponse> responseList = kubernetesClusterService.removeVmsFromCluster(this);
+            if (responseList.size() < 1) {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Provided VMs are not part of the CKS cluster");
+            }
+            ListResponse<RemoveVirtualMachinesFromKubernetesClusterResponse> listResponse = new ListResponse<>();
+            listResponse.setResponseName(getCommandName());
+            listResponse.setResponses(responseList);
+            setResponseObject(listResponse);
+        } catch (CloudRuntimeException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+}
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java
index f3c9939..e5a5c90 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/ScaleKubernetesClusterCmd.java
@@ -43,7 +43,7 @@
 import com.cloud.utils.exception.CloudRuntimeException;
 
 @APICommand(name = "scaleKubernetesCluster",
-        description = "Scales a created, running or stopped Kubernetes cluster",
+        description = "Scales a created, running or stopped CloudManaged Kubernetes cluster",
         responseObject = KubernetesClusterResponse.class,
         responseView = ResponseObject.ResponseView.Restricted,
         entityType = {KubernetesCluster.class},
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StartKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StartKubernetesClusterCmd.java
index 1140cac..7a7c1e8 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StartKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StartKubernetesClusterCmd.java
@@ -36,7 +36,7 @@
 import com.cloud.kubernetes.cluster.KubernetesClusterService;
 import com.cloud.utils.exception.CloudRuntimeException;
 
-@APICommand(name = "startKubernetesCluster", description = "Starts a stopped Kubernetes cluster",
+@APICommand(name = "startKubernetesCluster", description = "Starts a stopped CloudManaged Kubernetes cluster",
         responseObject = KubernetesClusterResponse.class,
         responseView = ResponseObject.ResponseView.Restricted,
         entityType = {KubernetesCluster.class},
@@ -99,6 +99,10 @@
         if (kubernetesCluster == null) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Given Kubernetes cluster was not found");
         }
+        if (!kubernetesClusterService.isCommandSupported(kubernetesCluster, getActualCommandName())) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
+                    String.format("Start kubernetes cluster is not supported for an externally managed cluster (%s)", kubernetesCluster.getName()));
+        }
         return kubernetesCluster;
     }
 
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StopKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StopKubernetesClusterCmd.java
index 393786f..866a7a8 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StopKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/StopKubernetesClusterCmd.java
@@ -37,7 +37,7 @@
 import com.cloud.kubernetes.cluster.KubernetesClusterService;
 import com.cloud.utils.exception.CloudRuntimeException;
 
-@APICommand(name = "stopKubernetesCluster", description = "Stops a running Kubernetes cluster",
+@APICommand(name = "stopKubernetesCluster", description = "Stops a running CloudManaged Kubernetes cluster",
         responseObject = SuccessResponse.class,
         responseView = ResponseObject.ResponseView.Restricted,
         entityType = {KubernetesCluster.class},
@@ -95,7 +95,7 @@
     @Override
     public void execute() throws ServerApiException, ConcurrentOperationException {
         try {
-            if (!kubernetesClusterService.stopKubernetesCluster(getId())) {
+            if (!kubernetesClusterService.stopKubernetesCluster(this)) {
                 throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to start Kubernetes cluster ID: %d", getId()));
             }
             final SuccessResponse response = new SuccessResponse(getCommandName());
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/UpgradeKubernetesClusterCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/UpgradeKubernetesClusterCmd.java
index 3283ea0..2cbedf5 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/UpgradeKubernetesClusterCmd.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/user/kubernetes/cluster/UpgradeKubernetesClusterCmd.java
@@ -38,7 +38,7 @@
 import com.cloud.kubernetes.cluster.KubernetesClusterService;
 import com.cloud.utils.exception.CloudRuntimeException;
 
-@APICommand(name = "upgradeKubernetesCluster", description = "Upgrades a running Kubernetes cluster",
+@APICommand(name = "upgradeKubernetesCluster", description = "Upgrades a running CloudManaged Kubernetes cluster",
         responseObject = KubernetesClusterResponse.class,
         responseView = ResponseObject.ResponseView.Restricted,
         entityType = {KubernetesCluster.class},
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesClusterResponse.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesClusterResponse.java
index a67d41a..168dfaf 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesClusterResponse.java
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/KubernetesClusterResponse.java
@@ -159,6 +159,10 @@
     @Param(description = "Maximum size of the cluster")
     private Long maxSize;
 
+    @SerializedName(ApiConstants.CLUSTER_TYPE)
+    @Param(description = "the type of the cluster")
+    private KubernetesCluster.ClusterType clusterType;
+
     @SerializedName(ApiConstants.CREATED)
     @Param(description = "the date when this Kubernetes cluster was created")
     private Date created;
@@ -386,4 +390,12 @@
     public void setCreated(Date created) {
         this.created = created;
     }
+
+    public KubernetesCluster.ClusterType getClusterType() {
+        return clusterType;
+    }
+
+    public void setClusterType(KubernetesCluster.ClusterType clusterType) {
+        this.clusterType = clusterType;
+    }
 }
diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/RemoveVirtualMachinesFromKubernetesClusterResponse.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/RemoveVirtualMachinesFromKubernetesClusterResponse.java
new file mode 100644
index 0000000..eb2dfce
--- /dev/null
+++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/response/RemoveVirtualMachinesFromKubernetesClusterResponse.java
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
+
+public class RemoveVirtualMachinesFromKubernetesClusterResponse extends SuccessResponse {
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "the id of the Kubernetes cluster")
+    private String vmId;
+
+    public RemoveVirtualMachinesFromKubernetesClusterResponse() {
+    }
+
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+}
diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
index 52e555c..a6d46ff 100644
--- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
+++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
@@ -1,25 +1,52 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.kubernetes.cluster;
 
-
-import java.util.ArrayList;
-import java.util.List;
-
+import com.cloud.api.query.dao.TemplateJoinDao;
+import com.cloud.api.query.vo.TemplateJoinVO;
+import com.cloud.dc.DataCenter;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterActionWorker;
+import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
+import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
+import com.cloud.network.Network;
+import com.cloud.network.dao.FirewallRulesDao;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.FirewallRuleVO;
+import com.cloud.network.vpc.NetworkACL;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.User;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
+import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
@@ -28,19 +55,11 @@
 import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import com.cloud.api.query.dao.TemplateJoinDao;
-import com.cloud.api.query.vo.TemplateJoinVO;
-import com.cloud.dc.DataCenter;
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterActionWorker;
-import com.cloud.network.Network;
-import com.cloud.network.dao.FirewallRulesDao;
-import com.cloud.network.rules.FirewallRule;
-import com.cloud.network.rules.FirewallRuleVO;
-import com.cloud.network.vpc.NetworkACL;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.dao.VMTemplateDao;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 @RunWith(MockitoJUnitRunner.class)
 public class KubernetesClusterManagerImplTest {
@@ -54,6 +73,18 @@
     @Mock
     TemplateJoinDao templateJoinDao;
 
+    @Mock
+    KubernetesClusterDao kubernetesClusterDao;
+
+    @Mock
+    KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
+
+    @Mock
+    VMInstanceDao vmInstanceDao;
+
+    @Mock
+    private AccountManager accountManager;
+
     @Spy
     @InjectMocks
     KubernetesClusterManagerImpl kubernetesClusterManager;
@@ -171,7 +202,7 @@
     }
 
     @Test
-    public void testValidateKubernetesClusterScaleSizeDownsacaleNoError() {
+    public void testValidateKubernetesClusterScaleSizeDownscaleNoError() {
         KubernetesClusterVO clusterVO = Mockito.mock(KubernetesClusterVO.class);
         Mockito.when(clusterVO.getState()).thenReturn(KubernetesCluster.State.Running);
         Mockito.when(clusterVO.getControlNodeCount()).thenReturn(1L);
@@ -209,5 +240,56 @@
         Mockito.when(templateDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(VMTemplateVO.class));
         Mockito.when(templateJoinDao.newTemplateView(Mockito.any(VMTemplateVO.class), Mockito.anyLong(), Mockito.anyBoolean())).thenReturn(List.of(Mockito.mock(TemplateJoinVO.class)));
         kubernetesClusterManager.validateKubernetesClusterScaleSize(clusterVO, 4L, 10, Mockito.mock(DataCenter.class));
+
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
+        overrideDefaultConfigValue(KubernetesClusterService.KubernetesServiceEnabled, "_defaultValue", "true");
+        Mockito.doNothing().when(accountManager).checkAccess(
+                Mockito.any(Account.class), Mockito.any(), Mockito.anyBoolean(), Mockito.any());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        CallContext.unregister();
+    }
+
+    private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
+        Field f = ConfigKey.class.getDeclaredField(name);
+        f.setAccessible(true);
+        f.set(configKey, o);
+    }
+
+    @Test
+    public void addVmsToCluster() {
+        KubernetesClusterVO cluster = Mockito.mock(KubernetesClusterVO.class);
+        VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
+        AddVirtualMachinesToKubernetesClusterCmd cmd = Mockito.mock(AddVirtualMachinesToKubernetesClusterCmd.class);
+        List<Long> vmIds = Arrays.asList(1L, 2L, 3L);
+
+        Mockito.when(cmd.getId()).thenReturn(1L);
+        Mockito.when(cmd.getVmIds()).thenReturn(vmIds);
+        Mockito.when(cmd.getActualCommandName()).thenReturn(BaseCmd.getCommandNameByClass(RemoveVirtualMachinesFromKubernetesClusterCmd.class));
+        Mockito.when(cluster.getClusterType()).thenReturn(KubernetesCluster.ClusterType.ExternalManaged);
+        Mockito.when(vmInstanceDao.findById(Mockito.anyLong())).thenReturn(vm);
+        Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
+        Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmIdsIn(1L, vmIds)).thenReturn(Collections.emptyList());
+        Assert.assertTrue(kubernetesClusterManager.addVmsToCluster(cmd));
+    }
+
+    @Test
+    public void removeVmsFromCluster() {
+        KubernetesClusterVO cluster = Mockito.mock(KubernetesClusterVO.class);
+        RemoveVirtualMachinesFromKubernetesClusterCmd cmd = Mockito.mock(RemoveVirtualMachinesFromKubernetesClusterCmd.class);
+        List<Long> vmIds = Arrays.asList(1L, 2L, 3L);
+
+        Mockito.when(cmd.getId()).thenReturn(1L);
+        Mockito.when(cmd.getVmIds()).thenReturn(vmIds);
+        Mockito.when(cmd.getActualCommandName()).thenReturn(BaseCmd.getCommandNameByClass(RemoveVirtualMachinesFromKubernetesClusterCmd.class));
+        Mockito.when(cluster.getClusterType()).thenReturn(KubernetesCluster.ClusterType.ExternalManaged);
+        Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
+        Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 0);
     }
 }
diff --git a/plugins/integrations/prometheus/pom.xml b/plugins/integrations/prometheus/pom.xml
index c5552f4..102deca 100644
--- a/plugins/integrations/prometheus/pom.xml
+++ b/plugins/integrations/prometheus/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterImpl.java b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
index 7d6ed87..3f69ccd 100644
--- a/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
+++ b/plugins/integrations/prometheus/src/main/java/org/apache/cloudstack/metrics/PrometheusExporterImpl.java
@@ -212,9 +212,9 @@
     private String markTagMaps(HostVO host, Map<String, Integer> totalHosts, Map<String, Integer> upHosts, Map<String, Integer> downHosts) {
         List<String> hostTags = _hostTagsDao.getHostTags(host.getId());
         markTags(hostTags,totalHosts);
-        if (host.getStatus() == Status.Up) {
+        if (host.getStatus() == Status.Up && !host.isInMaintenanceStates()) {
             markTags(hostTags, upHosts);
-        } else if (host.getStatus() == Status.Disconnected || host.getStatus() == Status.Down) {
+        } else if (host.getStatus() == Status.Disconnected || host.getStatus() == Status.Down || host.isInMaintenanceStates()) {
             markTags(hostTags, downHosts);
         }
         return StringUtils.join(hostTags, ",");
diff --git a/plugins/metrics/pom.xml b/plugins/metrics/pom.xml
index c1d6b96..a5bfa46 100644
--- a/plugins/metrics/pom.xml
+++ b/plugins/metrics/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/bigswitch/pom.xml b/plugins/network-elements/bigswitch/pom.xml
index c5f22df..f0a1e3d 100644
--- a/plugins/network-elements/bigswitch/pom.xml
+++ b/plugins/network-elements/bigswitch/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/brocade-vcs/pom.xml b/plugins/network-elements/brocade-vcs/pom.xml
index a9d6903..d71d840 100644
--- a/plugins/network-elements/brocade-vcs/pom.xml
+++ b/plugins/network-elements/brocade-vcs/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/plugins/network-elements/cisco-vnmc/pom.xml b/plugins/network-elements/cisco-vnmc/pom.xml
index f759de0..8360811 100644
--- a/plugins/network-elements/cisco-vnmc/pom.xml
+++ b/plugins/network-elements/cisco-vnmc/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/dns-notifier/pom.xml b/plugins/network-elements/dns-notifier/pom.xml
index 714f4c7..c862a28 100644
--- a/plugins/network-elements/dns-notifier/pom.xml
+++ b/plugins/network-elements/dns-notifier/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <artifactId>cloud-plugin-example-dns-notifier</artifactId>
diff --git a/plugins/network-elements/elastic-loadbalancer/pom.xml b/plugins/network-elements/elastic-loadbalancer/pom.xml
index 8b3cbb4..abb64f0 100644
--- a/plugins/network-elements/elastic-loadbalancer/pom.xml
+++ b/plugins/network-elements/elastic-loadbalancer/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml
index 3a14369..6aa8fb9 100644
--- a/plugins/network-elements/globodns/pom.xml
+++ b/plugins/network-elements/globodns/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/internal-loadbalancer/pom.xml b/plugins/network-elements/internal-loadbalancer/pom.xml
index ae01363..c1e4224 100644
--- a/plugins/network-elements/internal-loadbalancer/pom.xml
+++ b/plugins/network-elements/internal-loadbalancer/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml
index e959418..53eddab 100644
--- a/plugins/network-elements/juniper-contrail/pom.xml
+++ b/plugins/network-elements/juniper-contrail/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <repositories>
diff --git a/plugins/network-elements/netscaler/pom.xml b/plugins/network-elements/netscaler/pom.xml
index 3fcaafb..931e89e 100644
--- a/plugins/network-elements/netscaler/pom.xml
+++ b/plugins/network-elements/netscaler/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/nicira-nvp/pom.xml b/plugins/network-elements/nicira-nvp/pom.xml
index 770612c..6eecde3 100644
--- a/plugins/network-elements/nicira-nvp/pom.xml
+++ b/plugins/network-elements/nicira-nvp/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/network-elements/opendaylight/pom.xml b/plugins/network-elements/opendaylight/pom.xml
index fc29d14..24ac605 100644
--- a/plugins/network-elements/opendaylight/pom.xml
+++ b/plugins/network-elements/opendaylight/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <profiles>
diff --git a/plugins/network-elements/ovs/pom.xml b/plugins/network-elements/ovs/pom.xml
index f7ab995..5b3b26d 100644
--- a/plugins/network-elements/ovs/pom.xml
+++ b/plugins/network-elements/ovs/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml
index 8ecc0a3..d2056c1 100644
--- a/plugins/network-elements/palo-alto/pom.xml
+++ b/plugins/network-elements/palo-alto/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/stratosphere-ssp/pom.xml b/plugins/network-elements/stratosphere-ssp/pom.xml
index ac0f856..e7e2c2b 100644
--- a/plugins/network-elements/stratosphere-ssp/pom.xml
+++ b/plugins/network-elements/stratosphere-ssp/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/network-elements/tungsten/pom.xml b/plugins/network-elements/tungsten/pom.xml
index d0f37c9..a831993 100644
--- a/plugins/network-elements/tungsten/pom.xml
+++ b/plugins/network-elements/tungsten/pom.xml
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 
diff --git a/plugins/network-elements/vxlan/pom.xml b/plugins/network-elements/vxlan/pom.xml
index 25e75da..a208ec2 100644
--- a/plugins/network-elements/vxlan/pom.xml
+++ b/plugins/network-elements/vxlan/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml
index 4ddc980..faf3f54 100644
--- a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml
+++ b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml
index d8e4941..826d93d 100644
--- a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml
+++ b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/outofbandmanagement-drivers/redfish/pom.xml b/plugins/outofbandmanagement-drivers/redfish/pom.xml
index 88dcdec..fd2bf0b 100644
--- a/plugins/outofbandmanagement-drivers/redfish/pom.xml
+++ b/plugins/outofbandmanagement-drivers/redfish/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 6eea563..d0661c0 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <build>
         <plugins>
@@ -115,6 +115,8 @@
         <module>outofbandmanagement-drivers/nested-cloudstack</module>
         <module>outofbandmanagement-drivers/redfish</module>
 
+        <module>shutdown</module>
+
         <module>storage/image/default</module>
         <module>storage/image/s3</module>
         <module>storage/image/sample</module>
diff --git a/plugins/shutdown/pom.xml b/plugins/shutdown/pom.xml
new file mode 100644
index 0000000..0f34cc0
--- /dev/null
+++ b/plugins/shutdown/pom.xml
@@ -0,0 +1,44 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>cloud-plugin-shutdown</artifactId>
+    <name>Apache CloudStack Plugin - Safe Shutdown</name>
+    <parent>
+        <groupId>org.apache.cloudstack</groupId>
+        <artifactId>cloudstack-plugins</artifactId>
+        <version>4.19.0.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/BaseShutdownActionCmd.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/BaseShutdownActionCmd.java
new file mode 100644
index 0000000..d7f4953
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/BaseShutdownActionCmd.java
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+
+
+import org.apache.cloudstack.api.response.ManagementServerResponse;
+import org.apache.cloudstack.shutdown.ShutdownManager;
+
+public abstract class BaseShutdownActionCmd extends BaseCmd {
+
+    @Inject
+    protected ShutdownManager shutdownManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "the uuid of the management server", required = true)
+    private Long managementServerId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getManagementServerId() {
+        return managementServerId;
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/CancelShutdownCmd.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/CancelShutdownCmd.java
new file mode 100644
index 0000000..fe6204f
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/CancelShutdownCmd.java
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+ package org.apache.cloudstack.api.command;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.log4j.Logger;
+
+import com.cloud.user.Account;
+
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+import org.apache.cloudstack.acl.RoleType;
+
+@APICommand(name = CancelShutdownCmd.APINAME,
+            description = "Cancels a triggered shutdown",
+            since = "4.19.0",
+            responseObject = ReadyForShutdownResponse.class,
+            requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+            authorized = {RoleType.Admin})
+
+public class CancelShutdownCmd extends BaseShutdownActionCmd {
+
+    public static final Logger LOG = Logger.getLogger(CancelShutdownCmd.class);
+    public static final String APINAME = "cancelShutdown";
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        final ReadyForShutdownResponse response = shutdownManager.cancelShutdown(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName("cancelshutdown");
+        setResponseObject(response);
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/PrepareForShutdownCmd.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/PrepareForShutdownCmd.java
new file mode 100644
index 0000000..01ea179
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/PrepareForShutdownCmd.java
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command;
+
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.log4j.Logger;
+
+import com.cloud.user.Account;
+
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+import org.apache.cloudstack.acl.RoleType;
+
+@APICommand(name = PrepareForShutdownCmd.APINAME,
+            description = "Prepares CloudStack for a safe manual shutdown by preventing new jobs from being accepted",
+            since = "4.19.0",
+            responseObject = ReadyForShutdownResponse.class,
+            requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+            authorized = {RoleType.Admin})
+public class PrepareForShutdownCmd extends BaseShutdownActionCmd {
+    public static final Logger LOG = Logger.getLogger(PrepareForShutdownCmd.class);
+    public static final String APINAME = "prepareForShutdown";
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        final ReadyForShutdownResponse response = shutdownManager.prepareForShutdown(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName("prepareforshutdown");
+        setResponseObject(response);
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/ReadyForShutdownCmd.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/ReadyForShutdownCmd.java
new file mode 100644
index 0000000..d7ab6a2
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/ReadyForShutdownCmd.java
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ManagementServerResponse;
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+import org.apache.cloudstack.shutdown.ShutdownManager;
+import org.apache.log4j.Logger;
+import com.cloud.user.Account;
+
+@APICommand(name = ReadyForShutdownCmd.APINAME,
+            description = "Returs the status of CloudStack, whether a shutdown has been triggered and if ready to shutdown",
+            since = "4.19.0",
+            responseObject = ReadyForShutdownResponse.class,
+            requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class ReadyForShutdownCmd extends BaseCmd {
+    public static final Logger LOG = Logger.getLogger(ReadyForShutdownCmd.class);
+    public static final String APINAME = "readyForShutdown";
+
+    @Inject
+    private ShutdownManager shutdownManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "the uuid of the management server")
+    private Long managementServerId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getManagementServerId() {
+        return managementServerId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        final ReadyForShutdownResponse response = shutdownManager.readyForShutdown(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName("readyforshutdown");
+        setResponseObject(response);
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/TriggerShutdownCmd.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/TriggerShutdownCmd.java
new file mode 100644
index 0000000..3abde0b
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/command/TriggerShutdownCmd.java
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.api.command;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.log4j.Logger;
+
+import com.cloud.user.Account;
+
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+import org.apache.cloudstack.acl.RoleType;
+
+@APICommand(name = TriggerShutdownCmd.APINAME,
+            description = "Triggers an automatic safe shutdown of CloudStack by not accepting new jobs and shutting down when all pending jobbs have been completed. Triggers an immediate shutdown if forced",
+            since = "4.19.0",
+            responseObject = ReadyForShutdownResponse.class,
+            requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+            authorized = {RoleType.Admin})
+public class TriggerShutdownCmd extends BaseShutdownActionCmd {
+    public static final Logger LOG = Logger.getLogger(TriggerShutdownCmd.class);
+    public static final String APINAME = "triggerShutdown";
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() {
+        final ReadyForShutdownResponse response = shutdownManager.triggerShutdown(this);
+        response.setResponseName(getCommandName());
+        response.setObjectName("triggershutdown");
+        setResponseObject(response);
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/api/response/ReadyForShutdownResponse.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/response/ReadyForShutdownResponse.java
new file mode 100644
index 0000000..d1b2353
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/api/response/ReadyForShutdownResponse.java
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.response;
+
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class ReadyForShutdownResponse extends BaseResponse {
+    @SerializedName(ApiConstants.READY_FOR_SHUTDOWN)
+    @Param(description = "Indicates whether CloudStack is ready to shutdown")
+    private Boolean readyForShutdown;
+
+    @SerializedName(ApiConstants.SHUTDOWN_TRIGGERED)
+    @Param(description = "Indicates whether a shutdown has been triggered")
+    private Boolean shutdownTriggered;
+
+    @SerializedName(ApiConstants.PENDING_JOBS_COUNT)
+    @Param(description = "The number of jobs in progress")
+    private Long pendingJobsCount;
+
+    @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID)
+    @Param(description = "The id of the management server")
+    private Long msId;
+
+    public ReadyForShutdownResponse(Long msId, Boolean shutdownTriggered, Boolean readyForShutdown, long pendingJobsCount) {
+        this.msId = msId;
+        this.shutdownTriggered = shutdownTriggered;
+        this.readyForShutdown = readyForShutdown;
+        this.pendingJobsCount = pendingJobsCount;
+    }
+
+    public Boolean getShutdownTriggered() {
+        return this.shutdownTriggered;
+    }
+
+    public void setShutdownTriggered(Boolean shutdownTriggered) {
+        this.shutdownTriggered = shutdownTriggered;
+    }
+
+    public Boolean getReadyForShutdown() {
+        return this.readyForShutdown;
+    }
+
+    public void setReadyForShutdown(Boolean readyForShutdown) {
+        this.readyForShutdown = readyForShutdown;
+    }
+
+    public Long getPendingJobsCount() {
+        return this.pendingJobsCount;
+    }
+
+    public void setPendingJobsCount(Long pendingJobsCount) {
+        this.pendingJobsCount = pendingJobsCount;
+    }
+
+    public Long getMsId() {
+        return msId;
+    }
+
+    public void setMsId(Long msId) {
+        this.msId = msId;
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManager.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManager.java
new file mode 100644
index 0000000..22f43cb
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManager.java
@@ -0,0 +1,60 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.shutdown;
+
+import org.apache.cloudstack.api.command.CancelShutdownCmd;
+import org.apache.cloudstack.api.command.PrepareForShutdownCmd;
+import org.apache.cloudstack.api.command.ReadyForShutdownCmd;
+import org.apache.cloudstack.api.command.TriggerShutdownCmd;
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+
+public interface ShutdownManager {
+    // Returns the number of pending jobs for the given Management server msids.
+    // NOTE: This is the msid and NOT the id
+    long countPendingJobs(Long... msIds);
+
+    // Indicates whether a shutdown has been triggered on the current management server
+    boolean isShutdownTriggered();
+
+    // Indicates whether the current management server is preparing to shutdown
+    boolean isPreparingForShutdown();
+
+    // Triggers a shutdown on the current management server by not accepting any more async jobs and shutting down when there are no pending jobs
+    void triggerShutdown();
+
+    // Prepares the current management server to shutdown by not accepting any more async jobs
+    void prepareForShutdown();
+
+    // Cancels the shutdown on the current management server
+    void cancelShutdown();
+
+    // Returns whether the given ms can be shut down
+    ReadyForShutdownResponse readyForShutdown(Long managementserverid);
+
+    // Returns whether the any of the ms can be shut down and if a shutdown has been triggered on any running ms
+    ReadyForShutdownResponse readyForShutdown(ReadyForShutdownCmd cmd);
+
+    // Prepares the specified management server to shutdown by not accepting any more async jobs
+    ReadyForShutdownResponse prepareForShutdown(PrepareForShutdownCmd cmd);
+
+    // Cancels the shutdown on the specified management server
+    ReadyForShutdownResponse cancelShutdown(CancelShutdownCmd cmd);
+
+    // Triggers a shutdown on the specified management server by not accepting any more async jobs and shutting down when there are no pending jobs
+    ReadyForShutdownResponse triggerShutdown(TriggerShutdownCmd cmd);
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManagerImpl.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManagerImpl.java
new file mode 100644
index 0000000..b8f5fb5
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/ShutdownManagerImpl.java
@@ -0,0 +1,265 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.shutdown;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.command.CancelShutdownCmd;
+import org.apache.cloudstack.api.command.PrepareForShutdownCmd;
+import org.apache.cloudstack.api.command.ReadyForShutdownCmd;
+import org.apache.cloudstack.api.command.TriggerShutdownCmd;
+import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.management.ManagementServerHost.State;
+import org.apache.cloudstack.shutdown.command.CancelShutdownManagementServerHostCommand;
+import org.apache.cloudstack.shutdown.command.PrepareForShutdownManagementServerHostCommand;
+import org.apache.cloudstack.shutdown.command.TriggerShutdownManagementServerHostCommand;
+import org.apache.cloudstack.utils.identity.ManagementServerNode;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Command;
+import com.cloud.cluster.ClusterManager;
+import com.cloud.cluster.ManagementServerHostVO;
+import com.cloud.cluster.dao.ManagementServerHostDao;
+import com.cloud.serializer.GsonHelper;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.google.gson.Gson;
+
+public class ShutdownManagerImpl extends ManagerBase implements ShutdownManager, PluggableService{
+
+    private static Logger logger = Logger.getLogger(ShutdownManagerImpl.class);
+    Gson gson;
+
+    @Inject
+    private AsyncJobManager jobManager;
+    @Inject
+    private ManagementServerHostDao msHostDao;
+    @Inject
+    private ClusterManager clusterManager;
+
+    private boolean shutdownTriggered = false;
+    private boolean preparingForShutdown = false;
+
+    private Timer timer = new Timer();
+    private TimerTask shutdownTask;
+
+    protected ShutdownManagerImpl() {
+        super();
+        gson = GsonHelper.getGson();
+    }
+
+    @Override
+    public boolean isShutdownTriggered() {
+        return shutdownTriggered;
+    }
+
+    @Override
+    public boolean isPreparingForShutdown() {
+        return preparingForShutdown;
+    }
+
+    @Override
+    public long countPendingJobs(Long... msIds) {
+        return jobManager.countPendingNonPseudoJobs(msIds);
+    }
+
+    @Override
+    public void triggerShutdown() {
+        if (this.shutdownTriggered) {
+            throw new CloudRuntimeException("A shutdown has already been triggered");
+        }
+        this.shutdownTriggered = true;
+        prepareForShutdown(true);
+    }
+
+    private void prepareForShutdown(boolean postTrigger) {
+        // Ensure we don't throw an error if triggering a shutdown after just preparing for it
+        if (!postTrigger && this.preparingForShutdown) {
+            throw new CloudRuntimeException("A shutdown has already been triggered");
+        }
+        this.preparingForShutdown = true;
+        jobManager.disableAsyncJobs();
+        if (this.shutdownTask != null) {
+            this.shutdownTask.cancel();
+            this.shutdownTask = null;
+        }
+        this.shutdownTask = new ShutdownTask(this);
+        timer.scheduleAtFixedRate(shutdownTask, 0, 30L * 1000);
+    }
+
+    @Override
+    public void prepareForShutdown() {
+        prepareForShutdown(false);
+    }
+
+    @Override
+    public void cancelShutdown() {
+        if (!this.preparingForShutdown) {
+            throw new CloudRuntimeException("A shutdown has not been triggered");
+        }
+
+        this.preparingForShutdown = false;
+        this.shutdownTriggered = false;
+        jobManager.enableAsyncJobs();
+        if (shutdownTask != null) {
+            shutdownTask.cancel();
+        }
+        shutdownTask = null;
+    }
+
+    @Override
+    public ReadyForShutdownResponse readyForShutdown(Long managementserverid) {
+        Long[] msIds = null;
+        boolean shutdownTriggeredAnywhere = false;
+        State[] shutdownTriggeredStates = {State.ShuttingDown, State.PreparingToShutDown, State.ReadyToShutDown};
+        if (managementserverid == null) {
+            List<ManagementServerHostVO> msHosts = msHostDao.listBy(shutdownTriggeredStates);
+            if (msHosts != null && !msHosts.isEmpty()) {
+                msIds = new Long[msHosts.size()];
+                for (int i = 0; i < msHosts.size(); i++) {
+                    msIds[i] = msHosts.get(i).getMsid();
+                }
+                shutdownTriggeredAnywhere = !msHosts.isEmpty();
+            }
+        } else {
+            ManagementServerHostVO msHost = msHostDao.findById(managementserverid);
+            msIds = new Long[]{msHost.getMsid()};
+            shutdownTriggeredAnywhere = Arrays.asList(shutdownTriggeredStates).contains(msHost.getState());
+        }
+        long pendingJobCount = countPendingJobs(msIds);
+        return new ReadyForShutdownResponse(managementserverid, shutdownTriggeredAnywhere, pendingJobCount == 0, pendingJobCount);
+    }
+
+    @Override
+    public ReadyForShutdownResponse readyForShutdown(ReadyForShutdownCmd cmd) {
+        return readyForShutdown(cmd.getManagementServerId());
+    }
+
+    @Override
+    public ReadyForShutdownResponse prepareForShutdown(PrepareForShutdownCmd cmd) {
+        ManagementServerHostVO msHost = msHostDao.findById(cmd.getManagementServerId());
+        final Command[] cmds = new Command[1];
+        cmds[0] = new PrepareForShutdownManagementServerHostCommand(msHost.getMsid());
+        String result = clusterManager.execute(String.valueOf(msHost.getMsid()), 0, gson.toJson(cmds), true);
+        logger.info("PrepareForShutdownCmd result : " + result);
+        if (!result.contains("Success")) {
+            throw new CloudRuntimeException(result);
+        }
+
+        msHost.setState(State.PreparingToShutDown);
+        msHostDao.persist(msHost);
+
+        return readyForShutdown(cmd.getManagementServerId());
+    }
+
+    @Override
+    public ReadyForShutdownResponse triggerShutdown(TriggerShutdownCmd cmd) {
+        ManagementServerHostVO msHost = msHostDao.findById(cmd.getManagementServerId());
+        final Command[] cmds = new Command[1];
+        cmds[0] = new TriggerShutdownManagementServerHostCommand(msHost.getMsid());
+        String result = clusterManager.execute(String.valueOf(msHost.getMsid()), 0, gson.toJson(cmds), true);
+        logger.info("TriggerShutdownCmd result : " + result);
+        if (!result.contains("Success")) {
+            throw new CloudRuntimeException(result);
+        }
+
+        msHost.setState(State.ShuttingDown);
+        msHostDao.persist(msHost);
+
+        return readyForShutdown(cmd.getManagementServerId());
+    }
+
+    @Override
+    public ReadyForShutdownResponse cancelShutdown(CancelShutdownCmd cmd) {
+        ManagementServerHostVO msHost = msHostDao.findById(cmd.getManagementServerId());
+        final Command[] cmds = new Command[1];
+        cmds[0] = new CancelShutdownManagementServerHostCommand(msHost.getMsid());
+        String result = clusterManager.execute(String.valueOf(msHost.getMsid()), 0, gson.toJson(cmds), true);
+        logger.info("CancelShutdownCmd result : " + result);
+        if (!result.contains("Success")) {
+            throw new CloudRuntimeException(result);
+        }
+
+        msHost.setState(State.Up);
+        msHostDao.persist(msHost);
+
+        return readyForShutdown(cmd.getManagementServerId());
+    }
+
+    @Override
+    public List<Class<?>> getCommands() {
+        final List<Class<?>> cmdList = new ArrayList<>();
+        cmdList.add(CancelShutdownCmd.class);
+        cmdList.add(PrepareForShutdownCmd.class);
+        cmdList.add(ReadyForShutdownCmd.class);
+        cmdList.add(TriggerShutdownCmd.class);
+        return cmdList;
+    }
+
+    private final class ShutdownTask extends TimerTask {
+
+        private ShutdownManager shutdownManager;
+
+        public ShutdownTask(ShutdownManager shutdownManager) {
+            this.shutdownManager = shutdownManager;
+        }
+
+        @Override
+        public void run() {
+            try {
+                Long totalPendingJobs = shutdownManager.countPendingJobs(ManagementServerNode.getManagementServerId());
+                String msg = String.format("Checking for triggered shutdown... shutdownTriggered [%b] AllowAsyncJobs [%b] PendingJobCount [%d]",
+                    shutdownManager.isShutdownTriggered(), shutdownManager.isPreparingForShutdown(), totalPendingJobs);
+                logger.info(msg);
+
+                // If the shutdown has been cancelled
+                if (!shutdownManager.isPreparingForShutdown()) {
+                    logger.info("Shutdown cancelled. Terminating the shutdown timer task");
+                    this.cancel();
+                    return;
+                }
+
+                // No more pending jobs. Good to terminate
+                if (totalPendingJobs == 0) {
+                    if (shutdownManager.isShutdownTriggered()) {
+                        logger.info("Shutting down now");
+                        System.exit(0);
+                    }
+                    if (shutdownManager.isPreparingForShutdown()) {
+                        logger.info("Ready to shutdown");
+                        ManagementServerHostVO msHost = msHostDao.findByMsid(ManagementServerNode.getManagementServerId());
+                        msHost.setState(State.ReadyToShutDown);
+                        msHostDao.persist(msHost);
+                    }
+                }
+
+                logger.info("Pending jobs. Trying again later");
+            } catch (final Exception e) {
+                logger.error("Error trying to run shutdown task", e);
+            }
+        }
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/BaseShutdownManagementServerHostCommand.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/BaseShutdownManagementServerHostCommand.java
new file mode 100644
index 0000000..8fe3331
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/BaseShutdownManagementServerHostCommand.java
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+package org.apache.cloudstack.shutdown.command;
+
+import com.cloud.agent.api.Command;
+
+public class BaseShutdownManagementServerHostCommand extends Command {
+    long msId;
+
+    public BaseShutdownManagementServerHostCommand(long msId) {
+        this.msId = msId;
+    }
+
+    public long getMsId() {
+        return msId;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/CancelShutdownManagementServerHostCommand.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/CancelShutdownManagementServerHostCommand.java
new file mode 100644
index 0000000..eef4444
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/CancelShutdownManagementServerHostCommand.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+package org.apache.cloudstack.shutdown.command;
+
+public class CancelShutdownManagementServerHostCommand extends BaseShutdownManagementServerHostCommand {
+
+    public CancelShutdownManagementServerHostCommand(long msId) {
+        super(msId);
+    }
+
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/PrepareForShutdownManagementServerHostCommand.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/PrepareForShutdownManagementServerHostCommand.java
new file mode 100644
index 0000000..32a9201d
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/PrepareForShutdownManagementServerHostCommand.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+package org.apache.cloudstack.shutdown.command;
+
+public class PrepareForShutdownManagementServerHostCommand extends BaseShutdownManagementServerHostCommand {
+
+    public PrepareForShutdownManagementServerHostCommand(long msId) {
+        super(msId);
+    }
+}
diff --git a/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/TriggerShutdownManagementServerHostCommand.java b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/TriggerShutdownManagementServerHostCommand.java
new file mode 100644
index 0000000..e0d1879
--- /dev/null
+++ b/plugins/shutdown/src/main/java/org/apache/cloudstack/shutdown/command/TriggerShutdownManagementServerHostCommand.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+
+package org.apache.cloudstack.shutdown.command;
+
+public class TriggerShutdownManagementServerHostCommand extends BaseShutdownManagementServerHostCommand {
+
+    public TriggerShutdownManagementServerHostCommand(long msId) {
+        super(msId);
+    }
+}
diff --git a/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/module.properties b/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/module.properties
new file mode 100644
index 0000000..fd85c30
--- /dev/null
+++ b/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/module.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+name=shutdown
+parent=api
diff --git a/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/spring-shutdown-context.xml b/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/spring-shutdown-context.xml
new file mode 100644
index 0000000..5318b3b
--- /dev/null
+++ b/plugins/shutdown/src/main/resources/META-INF/cloudstack/shutdown/spring-shutdown-context.xml
@@ -0,0 +1,29 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans.xsd"
+>
+
+    <bean id="shutdownManager" class="org.apache.cloudstack.shutdown.ShutdownManagerImpl" >
+      <property name="name" value="shutdownManager" />
+    </bean>
+
+</beans>
diff --git a/plugins/shutdown/src/test/java/org/apache/cloudstack/shutdown/ShutdownManagerImplTest.java b/plugins/shutdown/src/test/java/org/apache/cloudstack/shutdown/ShutdownManagerImplTest.java
new file mode 100644
index 0000000..19ded78
--- /dev/null
+++ b/plugins/shutdown/src/test/java/org/apache/cloudstack/shutdown/ShutdownManagerImplTest.java
@@ -0,0 +1,78 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.cloudstack.shutdown;
+
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+
+
+@RunWith(MockitoJUnitRunner.class)
+public class ShutdownManagerImplTest {
+
+    @Spy
+    @InjectMocks
+    ShutdownManagerImpl spy;
+
+    @Mock
+    AsyncJobManager jobManagerMock;
+
+    private long prepareCountPendingJobs() {
+        long expectedCount = 1L;
+        Mockito.doReturn(expectedCount).when(jobManagerMock).countPendingNonPseudoJobs(1L);
+        return expectedCount;
+    }
+
+    @Test
+    public void countPendingJobs() {
+        long expectedCount = prepareCountPendingJobs();
+        long count = spy.countPendingJobs(1L);
+        Assert.assertEquals(expectedCount, count);
+    }
+
+    @Test
+    public void cancelShutdown() {
+        Assert.assertThrows(CloudRuntimeException.class, () -> {
+            spy.cancelShutdown();
+        });
+    }
+
+    @Test
+    public void prepareForShutdown() {
+        Mockito.doNothing().when(jobManagerMock).disableAsyncJobs();
+        spy.prepareForShutdown();
+        Mockito.verify(jobManagerMock).disableAsyncJobs();
+
+        Assert.assertThrows(CloudRuntimeException.class, () -> {
+            spy.prepareForShutdown();
+        });
+
+
+        Mockito.doNothing().when(jobManagerMock).enableAsyncJobs();
+        spy.cancelShutdown();
+        Mockito.verify(jobManagerMock).enableAsyncJobs();
+    }
+}
diff --git a/plugins/storage-allocators/random/pom.xml b/plugins/storage-allocators/random/pom.xml
index e68b0cb..a745f47 100644
--- a/plugins/storage-allocators/random/pom.xml
+++ b/plugins/storage-allocators/random/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/storage/image/default/pom.xml b/plugins/storage/image/default/pom.xml
index bd75e1c..4f2b1ab 100644
--- a/plugins/storage/image/default/pom.xml
+++ b/plugins/storage/image/default/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -53,9 +53,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/image/s3/pom.xml b/plugins/storage/image/s3/pom.xml
index dc01305..de4457f 100644
--- a/plugins/storage/image/s3/pom.xml
+++ b/plugins/storage/image/s3/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/storage/image/sample/pom.xml b/plugins/storage/image/sample/pom.xml
index db0f377..29bb514 100644
--- a/plugins/storage/image/sample/pom.xml
+++ b/plugins/storage/image/sample/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -53,9 +53,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/image/swift/pom.xml b/plugins/storage/image/swift/pom.xml
index 473b6ab..5f2563b 100644
--- a/plugins/storage/image/swift/pom.xml
+++ b/plugins/storage/image/swift/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -53,9 +53,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/cloudbyte/pom.xml b/plugins/storage/volume/cloudbyte/pom.xml
index 2c9b0ba..2313bfd 100644
--- a/plugins/storage/volume/cloudbyte/pom.xml
+++ b/plugins/storage/volume/cloudbyte/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -57,9 +57,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/datera/pom.xml b/plugins/storage/volume/datera/pom.xml
index 5c734bb..e3a53e7 100644
--- a/plugins/storage/volume/datera/pom.xml
+++ b/plugins/storage/volume/datera/pom.xml
@@ -16,7 +16,7 @@
   <parent>
     <groupId>org.apache.cloudstack</groupId>
     <artifactId>cloudstack-plugins</artifactId>
-    <version>4.18.1.0-SNAPSHOT</version>
+    <version>4.19.0.0-SNAPSHOT</version>
     <relativePath>../../../pom.xml</relativePath>
   </parent>
   <dependencies>
@@ -49,9 +49,6 @@
     <plugins>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
-        <configuration>
-          <skipTests>true</skipTests>
-        </configuration>
         <executions>
           <execution>
             <phase>integration-test</phase>
diff --git a/plugins/storage/volume/default/pom.xml b/plugins/storage/volume/default/pom.xml
index 6572c09..66f9f6b 100644
--- a/plugins/storage/volume/default/pom.xml
+++ b/plugins/storage/volume/default/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -38,9 +38,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java b/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
index ffdc514..dbd13d8 100644
--- a/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
+++ b/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
@@ -23,6 +23,7 @@
 import com.cloud.agent.api.ModifyStoragePoolAnswer;
 import com.cloud.agent.api.ModifyStoragePoolCommand;
 import com.cloud.agent.api.StoragePoolInfo;
+import com.cloud.exception.StorageConflictException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
@@ -32,7 +33,6 @@
 import com.cloud.storage.Storage;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StorageManagerImpl;
-import com.cloud.storage.StoragePoolHostVO;
 import com.cloud.storage.dao.StoragePoolHostDao;
 import junit.framework.TestCase;
 import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
@@ -45,8 +45,9 @@
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
-import org.apache.cloudstack.storage.datastore.provider.DefaultHostListener;
 import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
+import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,8 +55,8 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -64,8 +65,6 @@
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
@@ -77,7 +76,6 @@
     @InjectMocks
     PrimaryDataStoreLifeCycle _cloudStackPrimaryDataStoreLifeCycle = new CloudStackPrimaryDataStoreLifeCycleImpl();
 
-    @Spy
     @InjectMocks
     StorageManager storageMgr = new StorageManagerImpl();
 
@@ -93,9 +91,8 @@
     @Mock
     DataStoreProviderManager _dataStoreProviderMgr;
 
-    @Spy
-    @InjectMocks
-    HypervisorHostListener hostListener = new DefaultHostListener();
+    @Mock
+    HypervisorHostListener hostListener;
 
     @Mock
     StoragePoolHostDao storagePoolHostDao;
@@ -121,10 +118,16 @@
     @Mock
     PrimaryDataStoreHelper primaryDataStoreHelper;
 
-    @Before
-    public void initMocks() {
+    AutoCloseable closeable;
 
-        MockitoAnnotations.initMocks(this);
+    @Before
+    public void initMocks() throws StorageConflictException {
+        closeable = MockitoAnnotations.openMocks(this);
+
+        ReflectionTestUtils.setField(storageMgr, "_storagePoolDao", primaryStoreDao);
+        ReflectionTestUtils.setField(storageMgr, "_dataStoreProviderMgr", _dataStoreProviderMgr);
+        ReflectionTestUtils.setField(storageMgr, "_dataStoreMgr", _dataStoreMgr);
+        ReflectionTestUtils.setField(_cloudStackPrimaryDataStoreLifeCycle, "storageMgr", storageMgr);
 
         List<HostVO> hostList = new ArrayList<HostVO>();
         HostVO host1 = new HostVO(1L, "aa01", Host.Type.Routing, "192.168.1.1", "255.255.255.0", null, null, null, null, null, null, null, null, null, null,
@@ -141,30 +144,31 @@
         when(store.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
         when(store.isShared()).thenReturn(true);
         when(store.getName()).thenReturn("newPool");
+        when(store.getStorageProviderName()).thenReturn("default");
+
 
         when(_dataStoreProviderMgr.getDataStoreProvider(anyString())).thenReturn(dataStoreProvider);
         when(dataStoreProvider.getName()).thenReturn("default");
-        ((StorageManagerImpl)storageMgr).registerHostListener("default", hostListener);
+
+        when(hostListener.hostConnect(Mockito.anyLong(), Mockito.anyLong())).thenReturn(true);
+        storageMgr.registerHostListener("default", hostListener);
+
 
         when(_resourceMgr.listAllUpHosts(eq(Host.Type.Routing), anyLong(), anyLong(), anyLong())).thenReturn(hostList);
         when(agentMgr.easySend(anyLong(), Mockito.any(ModifyStoragePoolCommand.class))).thenReturn(answer);
         when(answer.getResult()).thenReturn(true);
-        when(answer.getPoolInfo()).thenReturn(info);
 
-        when(info.getLocalPath()).thenReturn("/mnt/1");
-        when(info.getCapacityBytes()).thenReturn(0L);
-        when(info.getAvailableBytes()).thenReturn(0L);
-
-        when(storagePoolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(null);
         when(primaryStoreDao.findById(anyLong())).thenReturn(storagePool);
-        when(primaryStoreDao.update(anyLong(), Mockito.any(StoragePoolVO.class))).thenReturn(true);
         when(primaryDataStoreHelper.attachCluster(Mockito.any(DataStore.class))).thenReturn(null);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        closeable.close();
+    }
+
     @Test
     public void testAttachCluster() throws Exception {
-        _cloudStackPrimaryDataStoreLifeCycle.attachCluster(store, new ClusterScope(1L, 1L, 1L));
-        verify(storagePoolHostDao,times(2)).persist(Mockito.any(StoragePoolHostVO.class));
-
+        Assert.assertTrue(_cloudStackPrimaryDataStoreLifeCycle.attachCluster(store, new ClusterScope(1L, 1L, 1L)));
     }
 }
diff --git a/plugins/storage/volume/linstor/pom.xml b/plugins/storage/volume/linstor/pom.xml
index 3efda9d..69de4ff 100644
--- a/plugins/storage/volume/linstor/pom.xml
+++ b/plugins/storage/volume/linstor/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -48,9 +48,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/nexenta/pom.xml b/plugins/storage/volume/nexenta/pom.xml
index e6ff46f..33c0ffe 100644
--- a/plugins/storage/volume/nexenta/pom.xml
+++ b/plugins/storage/volume/nexenta/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -38,9 +38,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/nexenta/src/test/java/org/apache/cloudstack/storage/datastore/util/NexentaStorApplianceTest.java b/plugins/storage/volume/nexenta/src/test/java/org/apache/cloudstack/storage/datastore/util/NexentaStorApplianceTest.java
index 6dc59eb..749c04b 100644
--- a/plugins/storage/volume/nexenta/src/test/java/org/apache/cloudstack/storage/datastore/util/NexentaStorApplianceTest.java
+++ b/plugins/storage/volume/nexenta/src/test/java/org/apache/cloudstack/storage/datastore/util/NexentaStorApplianceTest.java
@@ -119,7 +119,6 @@
         when(client.execute(ListOfStringsNmsResponse.class, "stmf", "list_targetgroups")).thenReturn(null);
         assertFalse(appliance.isIscsiTargetGroupExists(targetGroup));
 
-        when(client.execute(ListOfIscsiTargetsNmsResponse.class, "stmf", "list_targetgroups")).thenReturn(new ListOfIscsiTargetsNmsResponse());
         assertFalse(appliance.isIscsiTargetGroupExists(targetGroup));
 
         LinkedList<String> result = new LinkedList<String>();
diff --git a/plugins/storage/volume/sample/pom.xml b/plugins/storage/volume/sample/pom.xml
index 79013c8..bb96b60 100644
--- a/plugins/storage/volume/sample/pom.xml
+++ b/plugins/storage/volume/sample/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -38,9 +38,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/scaleio/pom.xml b/plugins/storage/volume/scaleio/pom.xml
index cff8048..390d11b 100644
--- a/plugins/storage/volume/scaleio/pom.xml
+++ b/plugins/storage/volume/scaleio/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -44,9 +44,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
index 577b918..cf624c2 100644
--- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
+++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImplTest.java
@@ -48,7 +48,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class ScaleIOGatewayClientImplTest {
-    private final int port = 443;
+    private final int port = 8443;
     private final int timeout = 30;
     private final int maxConnections = 50;
     private final String username = "admin";
@@ -70,7 +70,7 @@
                         .withHeader("content-type", "application/json;charset=UTF-8")
                         .withBody(sessionKey)));
 
-        client = new ScaleIOGatewayClientImpl("https://localhost/api", username, password, false, timeout, maxConnections);
+        client = new ScaleIOGatewayClientImpl(String.format("https://localhost:%d/api", port), username, password, false, timeout, maxConnections);
 
         wireMockRule.stubFor(post("/api/types/Volume/instances")
                 .willReturn(aResponse()
diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriverTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriverTest.java
index d147582..1852ec1 100644
--- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriverTest.java
+++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriverTest.java
@@ -49,28 +49,29 @@
 import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import java.util.Optional;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.when;
 
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(RemoteHostEndPoint.class)
+@RunWith(MockitoJUnitRunner.class)
 public class ScaleIOPrimaryDataStoreDriverTest {
 
     @Spy
@@ -93,6 +94,18 @@
     @Mock
     ConfigurationDao configDao;
 
+    static MockedStatic<RemoteHostEndPoint> remoteHostEndPointMock;
+
+    @BeforeClass
+    public static void init() {
+        remoteHostEndPointMock = mockStatic(RemoteHostEndPoint.class);
+    }
+
+    @AfterClass
+    public static void close() {
+        remoteHostEndPointMock.close();
+    }
+
     @Before
     public void initMocks() {
         MockitoAnnotations.initMocks(this);
@@ -180,9 +193,8 @@
         VolumeObjectTO destVolTO = Mockito.mock(VolumeObjectTO.class);
         when(destData.getTO()).thenReturn(destVolTO);
         Host host = prepareEndpointForVolumeOperation(srcData);
-        PowerMockito.mockStatic(RemoteHostEndPoint.class);
         RemoteHostEndPoint ep = Mockito.mock(RemoteHostEndPoint.class);
-        when(RemoteHostEndPoint.getHypervisorHostEndPoint(host)).thenReturn(ep);
+        remoteHostEndPointMock.when(() -> RemoteHostEndPoint.getHypervisorHostEndPoint(host)).thenReturn(ep);
 
         DataTO dataTO = Mockito.mock(DataTO.class);
         CreateObjectAnswer createAnswer = new CreateObjectAnswer(dataTO);
@@ -225,9 +237,8 @@
         VolumeObjectTO destVolTO = Mockito.mock(VolumeObjectTO.class);
         when(destData.getTO()).thenReturn(destVolTO);
         Host host = prepareEndpointForVolumeOperation(srcData);
-        PowerMockito.mockStatic(RemoteHostEndPoint.class);
         RemoteHostEndPoint ep = Mockito.mock(RemoteHostEndPoint.class);
-        when(RemoteHostEndPoint.getHypervisorHostEndPoint(host)).thenReturn(ep);
+        remoteHostEndPointMock.when(() -> RemoteHostEndPoint.getHypervisorHostEndPoint(host)).thenReturn(ep);
 
         DataTO dataTO = Mockito.mock(DataTO.class);
         CreateObjectAnswer createAnswer = new CreateObjectAnswer(dataTO);
@@ -493,9 +504,8 @@
         Host destHost = Mockito.mock(Host.class);
 
         doReturn(false).when(scaleIOPrimaryDataStoreDriver).anyVolumeRequiresEncryption(srcData, destData);
-        PowerMockito.mockStatic(RemoteHostEndPoint.class);
         RemoteHostEndPoint ep = Mockito.mock(RemoteHostEndPoint.class);
-        when(RemoteHostEndPoint.getHypervisorHostEndPoint(destHost)).thenReturn(ep);
+        remoteHostEndPointMock.when(() -> RemoteHostEndPoint.getHypervisorHostEndPoint(destHost)).thenReturn(ep);
         Answer answer = Mockito.mock(Answer.class);
         when(ep.sendMessage(any())).thenReturn(answer);
 
@@ -517,8 +527,7 @@
         Host destHost = Mockito.mock(Host.class);
 
         doReturn(false).when(scaleIOPrimaryDataStoreDriver).anyVolumeRequiresEncryption(srcData, destData);
-        PowerMockito.mockStatic(RemoteHostEndPoint.class);
-        when(RemoteHostEndPoint.getHypervisorHostEndPoint(destHost)).thenReturn(null);
+        remoteHostEndPointMock.when(() -> RemoteHostEndPoint.getHypervisorHostEndPoint(destHost)).thenReturn(null);
 
         Answer answer = scaleIOPrimaryDataStoreDriver.copyOfflineVolume(srcData, destData, destHost);
 
diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
index 6cc7b87..4a6e73a 100644
--- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
+++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java
@@ -24,8 +24,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
@@ -40,13 +39,11 @@
 import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
-import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient;
 import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool;
 import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientImpl;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
-import org.apache.cloudstack.storage.datastore.provider.ScaleIOHostListener;
 import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil;
 import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
 import org.junit.Before;
@@ -54,19 +51,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.mockito.Spy;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
-import com.cloud.agent.AgentManager;
-import com.cloud.agent.api.ModifyStoragePoolAnswer;
-import com.cloud.agent.api.ModifyStoragePoolCommand;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
-import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor;
 import com.cloud.resource.ResourceManager;
 import com.cloud.resource.ResourceState;
@@ -80,9 +71,9 @@
 import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.template.TemplateManager;
 import com.cloud.utils.exception.CloudRuntimeException;
+import org.springframework.test.util.ReflectionTestUtils;
 
-@PrepareForTest(ScaleIOGatewayClient.class)
-@RunWith(PowerMockRunner.class)
+@RunWith(MockitoJUnitRunner.class)
 public class ScaleIOPrimaryDataStoreLifeCycleTest {
 
     @Mock
@@ -96,8 +87,6 @@
     @Mock
     private StoragePoolAutomation storagePoolAutomation;
     @Mock
-    private HostDao hostDao;
-    @Mock
     private StoragePoolHostDao storagePoolHostDao;
     @Mock
     private DataStoreProviderManager dataStoreProviderMgr;
@@ -109,18 +98,12 @@
     private PrimaryDataStore store;
     @Mock
     private TemplateManager templateMgr;
-    @Mock
-    private AgentManager agentMgr;
-    @Mock
-    ModifyStoragePoolAnswer answer;
 
-    @Spy
     @InjectMocks
     private StorageManager storageMgr = new StorageManagerImpl();
 
-    @Spy
-    @InjectMocks
-    private HypervisorHostListener hostListener = new ScaleIOHostListener();
+    @Mock
+    private HypervisorHostListener hostListener;
 
     @InjectMocks
     private ScaleIOPrimaryDataStoreLifeCycle scaleIOPrimaryDataStoreLifeCycleTest;
@@ -128,6 +111,7 @@
     @Before
     public void setUp() {
         initMocks(this);
+        ReflectionTestUtils.setField(scaleIOPrimaryDataStoreLifeCycleTest, "storageMgr", storageMgr);
     }
 
     @Test
@@ -135,9 +119,11 @@
         final DataStore dataStore = mock(DataStore.class);
         when(dataStore.getId()).thenReturn(1L);
 
-        PowerMockito.mockStatic(ScaleIOGatewayClient.class);
+        MockedStatic<ScaleIOGatewayClientConnectionPool> scaleIOGatewayClientConnectionPoolMocked = mockStatic(ScaleIOGatewayClientConnectionPool.class);
         ScaleIOGatewayClientImpl client = mock(ScaleIOGatewayClientImpl.class);
-        when(ScaleIOGatewayClientConnectionPool.getInstance().getClient(1L, storagePoolDetailsDao)).thenReturn(client);
+        ScaleIOGatewayClientConnectionPool pool = mock(ScaleIOGatewayClientConnectionPool.class);
+        scaleIOGatewayClientConnectionPoolMocked.when(() -> ScaleIOGatewayClientConnectionPool.getInstance()).thenReturn(pool);
+        when(pool.getClient(1L, storagePoolDetailsDao)).thenReturn(client);
 
         when(client.haveConnectedSdcs()).thenReturn(true);
 
@@ -157,28 +143,19 @@
 
         when(dataStoreMgr.getDataStore(anyLong(), eq(DataStoreRole.Primary))).thenReturn(store);
         when(store.getId()).thenReturn(1L);
-        when(store.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex);
         when(store.isShared()).thenReturn(true);
         when(store.getName()).thenReturn("ScaleIOPool");
         when(store.getStorageProviderName()).thenReturn(ScaleIOUtil.PROVIDER_NAME);
 
         when(dataStoreProviderMgr.getDataStoreProvider(ScaleIOUtil.PROVIDER_NAME)).thenReturn(dataStoreProvider);
         when(dataStoreProvider.getName()).thenReturn(ScaleIOUtil.PROVIDER_NAME);
+        when(hostListener.hostConnect(Mockito.anyLong(), Mockito.anyLong())).thenReturn(true);
         storageMgr.registerHostListener(ScaleIOUtil.PROVIDER_NAME, hostListener);
 
-        when(agentMgr.easySend(anyLong(), Mockito.any(ModifyStoragePoolCommand.class))).thenReturn(answer);
-        when(answer.getResult()).thenReturn(true);
-
-        when(storagePoolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(null);
-
-        when(hostDao.findById(1L)).thenReturn(host1);
-        when(hostDao.findById(2L)).thenReturn(host2);
-
         when(dataStoreHelper.attachZone(Mockito.any(DataStore.class))).thenReturn(null);
 
-        scaleIOPrimaryDataStoreLifeCycleTest.attachZone(dataStore, scope, Hypervisor.HypervisorType.KVM);
-        verify(storageMgr,times(2)).connectHostToSharedPool(Mockito.any(Long.class), Mockito.any(Long.class));
-        verify(storagePoolHostDao,times(2)).persist(Mockito.any(StoragePoolHostVO.class));
+        boolean result = scaleIOPrimaryDataStoreLifeCycleTest.attachZone(dataStore, scope, Hypervisor.HypervisorType.KVM);
+        assertThat(result).isTrue();
     }
 
     @Test(expected = CloudRuntimeException.class)
@@ -224,7 +201,6 @@
     public void testDeleteDataStoreWithStoragePoolNull() {
         final PrimaryDataStore store = mock(PrimaryDataStore.class);
         when(primaryDataStoreDao.findById(anyLong())).thenReturn(null);
-        when(dataStoreHelper.deletePrimaryDataStore(any(DataStore.class))).thenReturn(true);
         final boolean result = scaleIOPrimaryDataStoreLifeCycleTest.deleteDataStore(store);
         assertThat(result).isFalse();
     }
@@ -233,6 +209,7 @@
     public void testDeleteDataStore() {
         final PrimaryDataStore store = mock(PrimaryDataStore.class);
         final StoragePoolVO storagePoolVO = mock(StoragePoolVO.class);
+        when(store.getId()).thenReturn(1L);
         when(primaryDataStoreDao.findById(anyLong())).thenReturn(storagePoolVO);
         List<VMTemplateStoragePoolVO> unusedTemplates = new ArrayList<>();
         when(templateMgr.getUnusedTemplatesInPool(storagePoolVO)).thenReturn(unusedTemplates);
diff --git a/plugins/storage/volume/scaleio/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/plugins/storage/volume/scaleio/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d4
--- /dev/null
+++ b/plugins/storage/volume/scaleio/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml
index 1d1db53..1c54522 100644
--- a/plugins/storage/volume/solidfire/pom.xml
+++ b/plugins/storage/volume/solidfire/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml
index 4186430..de257d0 100644
--- a/plugins/storage/volume/storpool/pom.xml
+++ b/plugins/storage/volume/storpool/pom.xml
@@ -17,7 +17,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -66,9 +66,6 @@
         <plugins>
             <plugin>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <skipTests>true</skipTests>
-                </configuration>
                 <executions>
                     <execution>
                         <phase>integration-test</phase>
diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
index a7ff626..3a428ef 100644
--- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
+++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
@@ -54,6 +54,8 @@
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -67,7 +69,10 @@
 public class StorPoolUtil {
     private static final Logger log = Logger.getLogger(StorPoolUtil.class);
 
-    private static final File spLogFile = new File("/var/log/cloudstack/management/storpool-plugin.log");
+    private static final File spLogFile = new File(
+            Files.exists(Paths.get("/var/log/cloudstack/management/")) ?
+                    "/var/log/cloudstack/management/storpool-plugin.log" :
+                    "/tmp/storpool-plugin.log");
     private static PrintWriter spLogPrinterWriter = spLogFileInitialize();
 
     private static PrintWriter spLogFileInitialize() {
diff --git a/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java b/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
index 356cac9..b862695 100644
--- a/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
+++ b/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
@@ -47,18 +47,16 @@
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.powermock.core.classloader.annotations.PrepareForTest;
 
 import java.util.UUID;
 
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
-import static org.powermock.api.mockito.PowerMockito.mock;
 
 
 @RunWith(MockitoJUnitRunner.class)
-@PrepareForTest(StorPoolUtil.class)
 public class StorPoolPrimaryDataStoreDriverTest {
 
     @Mock
diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml
index deed063..6ccbfa8 100644
--- a/plugins/user-authenticators/ldap/pom.xml
+++ b/plugins/user-authenticators/ldap/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 
diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java
index 6bc81b7..b96a007 100644
--- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java
+++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapCreateAccountCmdTest.java
@@ -32,8 +32,8 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.nullable;
-import static org.powermock.api.mockito.PowerMockito.spy;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class LdapCreateAccountCmdTest implements LdapConfigurationChanger {
diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java
index 55310f9..594c23f 100644
--- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java
+++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapImportUsersCmdTest.java
@@ -40,8 +40,8 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.powermock.api.mockito.PowerMockito.spy;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class LdapImportUsersCmdTest implements LdapConfigurationChanger {
diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java
index 6203c53..11d99f5 100644
--- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java
+++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java
@@ -31,14 +31,14 @@
 import org.apache.cloudstack.ldap.LdapUser;
 import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
 import org.apache.cloudstack.query.QueryService;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,16 +50,13 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.powermock.api.mockito.PowerMockito.doReturn;
-import static org.powermock.api.mockito.PowerMockito.doThrow;
-import static org.powermock.api.mockito.PowerMockito.spy;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.when;
 
-@RunWith(PowerMockRunner.class)
-@PrepareForTest(CallContext.class)
-@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*", "org.xml.*"})
+@RunWith(MockitoJUnitRunner.class)
 public class LdapListUsersCmdTest implements LdapConfigurationChanger {
 
     public static final String LOCAL_DOMAIN_ID = "12345678-90ab-cdef-fedc-ba0987654321";
@@ -76,23 +73,30 @@
 
     Domain localDomain;
 
+    MockedStatic<CallContext> callContextMocked;
+
     @Before
     public void setUp() throws NoSuchFieldException, IllegalAccessException {
         ldapListUsersCmd = new LdapListUsersCmd(ldapManager, queryService);
         cmdSpy = spy(ldapListUsersCmd);
 
-        PowerMockito.mockStatic(CallContext.class);
-        CallContext callContextMock = PowerMockito.mock(CallContext.class);
-        PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
-        Account accountMock = PowerMockito.mock(Account.class);
-        PowerMockito.when(accountMock.getDomainId()).thenReturn(1l);
-        PowerMockito.when(callContextMock.getCallingAccount()).thenReturn(accountMock);
+        callContextMocked = Mockito.mockStatic(CallContext.class);
+        CallContext callContextMock = Mockito.mock(CallContext.class);
+        callContextMocked.when(CallContext::current).thenReturn(callContextMock);
+        Account accountMock = Mockito.mock(Account.class);
+        when(accountMock.getDomainId()).thenReturn(1l);
+        when(callContextMock.getCallingAccount()).thenReturn(accountMock);
 
         ldapListUsersCmd._domainService = domainService;
 
 // no need to        setHiddenField(ldapListUsersCmd, .... );
     }
 
+    @After
+    public void tearDown() throws Exception {
+        callContextMocked.close();
+    }
+
     /**
      * given: "We have an LdapManager, QueryService and LdapListUsersCmd"
      *  when: "Get entity owner id is called"
@@ -114,7 +118,7 @@
      */
     @Test
     public void successfulEmptyResponseFromExecute() throws NoLdapUserMatchingQueryException {
-        doThrow(new NoLdapUserMatchingQueryException("")).when(ldapManager).getUsers(null);
+        Mockito.doThrow(new NoLdapUserMatchingQueryException("")).when(ldapManager).getUsers(null);
         ldapListUsersCmd.execute();
         assertEquals(0, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
     }
diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java
index 1a00a05..e355d77 100644
--- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java
+++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java
@@ -36,7 +36,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class LinkAccountToLdapCmdTest implements LdapConfigurationChanger {
diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java
index 04594e2..204e985 100644
--- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java
+++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java
@@ -35,7 +35,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
-import static org.powermock.api.mockito.PowerMockito.when;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class LinkDomainToLdapCmdTest implements LdapConfigurationChanger
diff --git a/plugins/user-authenticators/ldap/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/plugins/user-authenticators/ldap/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d4
--- /dev/null
+++ b/plugins/user-authenticators/ldap/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/plugins/user-authenticators/md5/pom.xml b/plugins/user-authenticators/md5/pom.xml
index 718dd31..084471b 100644
--- a/plugins/user-authenticators/md5/pom.xml
+++ b/plugins/user-authenticators/md5/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml
index 8dcf9c9..154e811 100644
--- a/plugins/user-authenticators/pbkdf2/pom.xml
+++ b/plugins/user-authenticators/pbkdf2/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml
index 153ad94..4529e0c 100644
--- a/plugins/user-authenticators/plain-text/pom.xml
+++ b/plugins/user-authenticators/plain-text/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml
index 2d47e67..cbcd511 100644
--- a/plugins/user-authenticators/saml2/pom.xml
+++ b/plugins/user-authenticators/saml2/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml
index 32342d7..f202855 100644
--- a/plugins/user-authenticators/sha256salted/pom.xml
+++ b/plugins/user-authenticators/sha256salted/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/plugins/user-two-factor-authenticators/static-pin/pom.xml b/plugins/user-two-factor-authenticators/static-pin/pom.xml
index b740bfc..60cf3d2 100644
--- a/plugins/user-two-factor-authenticators/static-pin/pom.xml
+++ b/plugins/user-two-factor-authenticators/static-pin/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
\ No newline at end of file
diff --git a/plugins/user-two-factor-authenticators/totp/pom.xml b/plugins/user-two-factor-authenticators/totp/pom.xml
index 0eb1b80..b9455ee 100644
--- a/plugins/user-two-factor-authenticators/totp/pom.xml
+++ b/plugins/user-two-factor-authenticators/totp/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-plugins</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
 </project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 824ab36..204c74c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
 
     <groupId>org.apache.cloudstack</groupId>
     <artifactId>cloudstack</artifactId>
-    <version>4.18.1.0-SNAPSHOT</version>
+    <version>4.19.0.0-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>Apache CloudStack</name>
     <description>Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform.</description>
@@ -113,7 +113,7 @@
         <cs.junit.dataprovider.version>1.13.1</cs.junit.dataprovider.version>
         <cs.junit.jupiter.version>5.9.1</cs.junit.jupiter.version>
         <cs.guava-testlib.version>18.0</cs.guava-testlib.version>
-        <cs.mockito.version>3.2.4</cs.mockito.version>
+        <cs.mockito.version>3.12.4</cs.mockito.version>
         <cs.powermock.version>2.0.5</cs.powermock.version>
         <cs.selenium.server.version>1.0-20081010.060147</cs.selenium.server.version>
         <cs.selenium-java-client-driver.version>1.0.1</cs.selenium-java-client-driver.version>
@@ -133,6 +133,7 @@
         <cs.bcprov.version>1.70</cs.bcprov.version>
         <cs.cglib.version>3.3.0</cs.cglib.version>
         <cs.checkstyle-lib.version>8.18</cs.checkstyle-lib.version>
+        <cs.cron-utils.version>9.2.0</cs.cron-utils.version>
         <cs.cxf.version>3.2.14</cs.cxf.version>
         <cs.ehcache.version>2.6.11</cs.ehcache.version>
         <cs.globodns-client.version>0.0.27</cs.globodns-client.version>
@@ -1062,6 +1063,7 @@
                             <exclude>ui/public/**</exclude>
                             <exclude>ui/legacy/**</exclude>
                             <exclude>utils/testsmallfileinactive</exclude>
+                            <exclude>**/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker</exclude>
                         </excludes>
                     </configuration>
                 </plugin>
diff --git a/quickcloud/pom.xml b/quickcloud/pom.xml
index 42a294b..e3b77a8 100644
--- a/quickcloud/pom.xml
+++ b/quickcloud/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 </project>
diff --git a/scripts/util/keystore-cert-import b/scripts/util/keystore-cert-import
index c4ec3be..a7523ca 100755
--- a/scripts/util/keystore-cert-import
+++ b/scripts/util/keystore-cert-import
@@ -49,7 +49,7 @@
 KS_PASS=$(sed -n '/keystore.passphrase/p' "$PROPS_FILE" 2>/dev/null  | sed 's/keystore.passphrase=//g' 2>/dev/null)
 
 if [ -z "${KS_PASS// }" ]; then
-    echo "Failed to find keystore passphrase from file: $PROPS_FILE, quiting!"
+    echo "Failed to find keystore passphrase from file: $PROPS_FILE, quitting!"
     exit 1
 fi
 
diff --git a/scripts/vm/network/tungsten/create_tap_device.sh b/scripts/vm/network/tungsten/create_tap_device.sh
index c10a0e7..90c24d6 100755
--- a/scripts/vm/network/tungsten/create_tap_device.sh
+++ b/scripts/vm/network/tungsten/create_tap_device.sh
@@ -16,5 +16,5 @@
 # specific language governing permissions and limitations
 # under the License.
 
-ip tuntap add dev $1 mode tap
-ip link set $1 up
\ No newline at end of file
+#ip tuntap add dev $1 mode tap multi_queue
+#ip link set $1 up
diff --git a/scripts/vm/network/tungsten/delete_tap_device.sh b/scripts/vm/network/tungsten/delete_tap_device.sh
index 37d668a..8a36772 100755
--- a/scripts/vm/network/tungsten/delete_tap_device.sh
+++ b/scripts/vm/network/tungsten/delete_tap_device.sh
@@ -16,4 +16,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-ip tuntap del dev $1 mode tap
\ No newline at end of file
+ip tuntap del dev $1 mode tap multi_queue 2>/dev/null
+if [ $? -ne 0 ];then
+  ip tuntap del dev $1 mode tap
+fi
diff --git a/server/pom.xml b/server/pom.xml
index c1a7e02..a6c33dd 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <repositories>
         <repository>
diff --git a/server/src/main/java/com/cloud/api/ApiDispatcher.java b/server/src/main/java/com/cloud/api/ApiDispatcher.java
index 3880f2a..09a7a92 100644
--- a/server/src/main/java/com/cloud/api/ApiDispatcher.java
+++ b/server/src/main/java/com/cloud/api/ApiDispatcher.java
@@ -35,6 +35,7 @@
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.framework.jobs.AsyncJob;
 import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl;
 import org.apache.log4j.Logger;
 
 import com.cloud.api.dispatch.DispatchChain;
@@ -44,6 +45,7 @@
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.exception.CloudRuntimeException;
 
 public class ApiDispatcher {
     private static final Logger s_logger = Logger.getLogger(ApiDispatcher.class.getName());
@@ -63,6 +65,9 @@
     @Inject()
     protected DispatchChainFactory dispatchChainFactory;
 
+    @Inject
+    AsyncJobManagerImpl asyncJobManager;
+
     protected DispatchChain standardDispatchChain;
 
     protected DispatchChain asyncCreationDispatchChain;
@@ -85,7 +90,11 @@
     }
 
     public void dispatchCreateCmd(final BaseAsyncCreateCmd cmd, final Map<String, String> params) throws Exception {
-        asyncCreationDispatchChain.dispatch(new DispatchTask(cmd, params));
+        if (asyncJobManager.isAsyncJobsEnabled()) {
+            asyncCreationDispatchChain.dispatch(new DispatchTask(cmd, params));
+        } else {
+            throw new CloudRuntimeException("A shutdown has been triggered. Can not accept new jobs");
+        }
     }
 
     private void doAccessChecks(BaseCmd cmd, Map<Object, AccessType> entitiesToAccess) {
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 8fffebb..517ee8d 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -43,6 +43,7 @@
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
+import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiConstants.DomainDetails;
 import org.apache.cloudstack.api.ApiConstants.HostDetails;
 import org.apache.cloudstack.api.ApiConstants.VMDetails;
@@ -91,6 +92,8 @@
 import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.cloudstack.api.response.HostResponse;
 import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
+import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse;
+import org.apache.cloudstack.api.response.HypervisorGuestOsResponse;
 import org.apache.cloudstack.api.response.IPAddressResponse;
 import org.apache.cloudstack.api.response.ImageStoreResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
@@ -199,6 +202,7 @@
 import org.apache.cloudstack.usage.UsageService;
 import org.apache.cloudstack.usage.UsageTypes;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
@@ -291,6 +295,7 @@
 import com.cloud.network.as.Condition;
 import com.cloud.network.as.ConditionVO;
 import com.cloud.network.as.Counter;
+import com.cloud.network.dao.FirewallRulesDao;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.LoadBalancerVO;
@@ -455,6 +460,8 @@
     UserVmJoinDao userVmJoinDao;
     @Inject
     NetworkServiceMapDao ntwkSrvcDao;
+    @Inject
+    FirewallRulesDao firewallRulesDao;
 
     @Override
     public UserResponse createUserResponse(User user) {
@@ -935,6 +942,42 @@
         return userIpAddresVO != null ? userIpAddresVO.isForSystemVms() : false;
     }
 
+    private void addVmDetailsInIpResponse(IPAddressResponse response, IpAddress ipAddress) {
+        if (ipAddress.getAllocatedToAccountId() != null && ipAddress.getAllocatedToAccountId() == Account.ACCOUNT_ID_SYSTEM) {
+            NicVO nic = ApiDBUtils.findByIp4AddressAndNetworkId(ipAddress.getAddress().toString(), ipAddress.getNetworkId());
+            if (nic != null) {
+                addSystemVmInfoToIpResponse(nic, response);
+            }
+        }
+        if (ipAddress.getAssociatedWithVmId() != null) {
+            addUserVmDetailsInIpResponse(response, ipAddress);
+        }
+    }
+
+    private void addSystemVmInfoToIpResponse(NicVO nic, IPAddressResponse ipResponse) {
+        final boolean isAdmin = Account.Type.ADMIN.equals(CallContext.current().getCallingAccount().getType());
+        if (!isAdmin) {
+            return;
+        }
+        VirtualMachine vm = ApiDBUtils.findVMInstanceById(nic.getInstanceId());
+        if (vm == null) {
+            return;
+        }
+        ipResponse.setVirtualMachineId(vm.getUuid());
+        ipResponse.setVirtualMachineName(vm.getHostName());
+        ipResponse.setVirtualMachineType(vm.getType().toString());
+    }
+
+    private void addUserVmDetailsInIpResponse(IPAddressResponse response, IpAddress ipAddress) {
+        UserVm userVm = ApiDBUtils.findUserVmById(ipAddress.getAssociatedWithVmId());
+        if (userVm != null) {
+            response.setVirtualMachineId(userVm.getUuid());
+            response.setVirtualMachineName(userVm.getHostName());
+            response.setVirtualMachineType(userVm.getType().toString());
+            response.setVirtualMachineDisplayName(ObjectUtils.firstNonNull(userVm.getDisplayName(), userVm.getHostName()));
+        }
+    }
+
     @Override
     public IPAddressResponse createIPAddressResponse(ResponseView view, IpAddress ipAddr) {
         VlanVO vlan = ApiDBUtils.findVlanById(ipAddr.getVlanId());
@@ -963,18 +1006,7 @@
         ipResponse.setForVirtualNetwork(forVirtualNetworks);
         ipResponse.setStaticNat(ipAddr.isOneToOneNat());
 
-        if (ipAddr.getAssociatedWithVmId() != null) {
-            UserVm vm = ApiDBUtils.findUserVmById(ipAddr.getAssociatedWithVmId());
-            if (vm != null) {
-                ipResponse.setVirtualMachineId(vm.getUuid());
-                ipResponse.setVirtualMachineName(vm.getHostName());
-                if (vm.getDisplayName() != null) {
-                    ipResponse.setVirtualMachineDisplayName(vm.getDisplayName());
-                } else {
-                    ipResponse.setVirtualMachineDisplayName(vm.getHostName());
-                }
-            }
-        }
+        addVmDetailsInIpResponse(ipResponse, ipAddr);
         if (ipAddr.getVmIp() != null) {
             ipResponse.setVirtualMachineIp(ipAddr.getVmIp());
         }
@@ -1058,6 +1090,7 @@
         ipResponse.setHasAnnotation(annotationDao.hasAnnotations(ipAddr.getUuid(), AnnotationService.EntityType.PUBLIC_IP_ADDRESS.name(),
                 _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));
 
+        ipResponse.setHasRules(firewallRulesDao.countRulesByIpId(ipAddr.getId()) > 0);
         ipResponse.setObjectName("ipaddress");
         return ipResponse;
     }
@@ -1092,8 +1125,9 @@
                         ipResponse.setVirtualMachineDisplayName(vm.getHostName());
                     }
                 }
-            } else if (nic.getVmType() == VirtualMachine.Type.DomainRouter) {
+            } else if (nic.getVmType().isUsedBySystem()) {
                 ipResponse.setIsSystem(true);
+                addSystemVmInfoToIpResponse(nic, ipResponse);
             }
         }
     }
@@ -3629,12 +3663,14 @@
     @Override
     public GuestOSResponse createGuestOSResponse(GuestOS guestOS) {
         GuestOSResponse response = new GuestOSResponse();
+        response.setName(guestOS.getDisplayName());
         response.setDescription(guestOS.getDisplayName());
         response.setId(guestOS.getUuid());
-        response.setIsUserDefined(guestOS.getIsUserDefined());
+        response.setIsUserDefined(String.valueOf(guestOS.getIsUserDefined()));
         GuestOSCategoryVO category = ApiDBUtils.findGuestOsCategoryById(guestOS.getCategoryId());
         if (category != null) {
             response.setOsCategoryId(category.getUuid());
+            response.setOsCategoryName(category.getName());
         }
 
         response.setObjectName("ostype");
@@ -3660,6 +3696,28 @@
     }
 
     @Override
+    public HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse(List<Pair<String, String>> hypervisorGuestOsNames) {
+        HypervisorGuestOsNamesResponse response = new HypervisorGuestOsNamesResponse();
+        List<HypervisorGuestOsResponse> hypervisorGuestOsResponses = new ArrayList<>();
+        for (Pair<String, String> hypervisorGuestOsName : hypervisorGuestOsNames) {
+            HypervisorGuestOsResponse hypervisorGuestOsResponse = createHypervisorGuestOsResponse(hypervisorGuestOsName);
+            hypervisorGuestOsResponses.add(hypervisorGuestOsResponse);
+        }
+        response.setGuestOSList(hypervisorGuestOsResponses);
+        response.setGuestOSCount(hypervisorGuestOsResponses.size());
+        response.setObjectName("hypervisorguestosnames");
+        return response;
+    }
+
+    private HypervisorGuestOsResponse createHypervisorGuestOsResponse(Pair<String, String> hypervisorGuestOsName) {
+        HypervisorGuestOsResponse hypervisorGuestOsResponse = new HypervisorGuestOsResponse();
+        hypervisorGuestOsResponse.setOsStdName(hypervisorGuestOsName.first());
+        hypervisorGuestOsResponse.setOsNameForHypervisor(hypervisorGuestOsName.second());
+        hypervisorGuestOsResponse.setObjectName(ApiConstants.GUEST_OS_LIST);
+        return hypervisorGuestOsResponse;
+    }
+
+    @Override
     public SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule snapshotSchedule) {
         SnapshotScheduleResponse response = new SnapshotScheduleResponse();
         response.setId(snapshotSchedule.getUuid());
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index 0968af0..63927f2 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -184,6 +184,8 @@
 import com.cloud.api.query.vo.UserAccountJoinVO;
 import com.cloud.api.query.vo.UserVmJoinVO;
 import com.cloud.api.query.vo.VolumeJoinVO;
+import com.cloud.cluster.ManagementServerHostVO;
+import com.cloud.cluster.dao.ManagementServerHostDao;
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.DedicatedResourceVO;
 import com.cloud.dc.dao.DedicatedResourceDao;
@@ -457,6 +459,10 @@
     private ResourceIconDao resourceIconDao;
 
     @Inject
+    private ManagementServerHostDao msHostDao;
+
+
+    @Inject
     EntityManager entityManager;
 
     private SearchCriteria<ServiceOfferingJoinVO> getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
@@ -791,7 +797,9 @@
             sc.setParameters("resourceType", resourceType.toString());
         }
 
-        sc.setParameters("archived", false);
+        if (id == null) {
+            sc.setParameters("archived", cmd.getArchived());
+        }
 
         Pair<List<EventJoinVO>, Integer> eventPair = null;
         // event_view will not have duplicate rows for each event, so
@@ -965,7 +973,13 @@
     @Override
     public ListResponse<UserVmResponse> searchForUserVMs(ListVMsCmd cmd) {
         Pair<List<UserVmJoinVO>, Integer> result = searchForUserVMsInternal(cmd);
-        ListResponse<UserVmResponse> response = new ListResponse<UserVmResponse>();
+        ListResponse<UserVmResponse> response = new ListResponse<>();
+
+        if (cmd.getRetrieveOnlyResourceCount()) {
+            response.setResponses(new ArrayList<>(), result.second());
+            return response;
+        }
+
         ResponseView respView = ResponseView.Restricted;
         Account caller = CallContext.current().getCallingAccount();
         if (_accountMgr.isRootAdmin(caller.getId())) {
@@ -2054,7 +2068,12 @@
     @Override
     public ListResponse<VolumeResponse> searchForVolumes(ListVolumesCmd cmd) {
         Pair<List<VolumeJoinVO>, Integer> result = searchForVolumesInternal(cmd);
-        ListResponse<VolumeResponse> response = new ListResponse<VolumeResponse>();
+        ListResponse<VolumeResponse> response = new ListResponse<>();
+
+        if (cmd.getRetrieveOnlyResourceCount()) {
+            response.setResponses(new ArrayList<>(), result.second());
+            return response;
+        }
 
         ResponseView respView = cmd.getResponseView();
         Account account = CallContext.current().getCallingAccount();
@@ -2543,6 +2562,10 @@
             }
         }
 
+        if (cmd.getManagementServerId() != null) {
+            sb.and("executingMsid", sb.entity().getExecutingMsid(), SearchCriteria.Op.EQ);
+        }
+
         Object keyword = cmd.getKeyword();
         Object startDate = cmd.getStartDate();
 
@@ -2571,6 +2594,11 @@
             sc.addAnd("created", SearchCriteria.Op.GTEQ, startDate);
         }
 
+        if (cmd.getManagementServerId() != null) {
+            ManagementServerHostVO msHost = msHostDao.findById(cmd.getManagementServerId());
+            sc.setParameters("executingMsid", msHost.getMsid());
+        }
+
         return _jobJoinDao.searchAndCount(sc, searchFilter);
     }
 
@@ -4036,6 +4064,8 @@
             options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList());
             options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific"));
             options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled"));
+            options.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, Collections.emptyList());
+            options.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, Arrays.asList("true", "false"));
         }
 
         if (HypervisorType.VMware.equals(hypervisorType)) {
diff --git a/server/src/main/java/com/cloud/api/query/dao/AsyncJobJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/AsyncJobJoinDaoImpl.java
index bd11015..32cd1c2 100644
--- a/server/src/main/java/com/cloud/api/query/dao/AsyncJobJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/AsyncJobJoinDaoImpl.java
@@ -53,6 +53,11 @@
     public AsyncJobResponse newAsyncJobResponse(final AsyncJobJoinVO job) {
         final AsyncJobResponse jobResponse = new AsyncJobResponse();
         jobResponse.setAccountId(job.getAccountUuid());
+        jobResponse.setAccount(job.getAccountName());
+        jobResponse.setDomainId(job.getDomainUuid());
+        StringBuilder domainPath = new StringBuilder("ROOT");
+        (domainPath.append(job.getDomainPath())).deleteCharAt(domainPath.length() - 1);
+        jobResponse.setDomainPath(domainPath.toString());
         jobResponse.setUserId(job.getUserUuid());
         jobResponse.setCmd(job.getCmd());
         jobResponse.setCreated(job.getCreated());
@@ -60,6 +65,7 @@
         jobResponse.setJobId(job.getUuid());
         jobResponse.setJobStatus(job.getStatus());
         jobResponse.setJobProcStatus(job.getProcessStatus());
+        jobResponse.setMsid(job.getExecutingMsid());
 
         if (job.getInstanceType() != null && job.getInstanceId() != null) {
             jobResponse.setJobInstanceType(job.getInstanceType().toString());
diff --git a/server/src/main/java/com/cloud/api/query/vo/AsyncJobJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/AsyncJobJoinVO.java
index a4db864..cee5526 100644
--- a/server/src/main/java/com/cloud/api/query/vo/AsyncJobJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/AsyncJobJoinVO.java
@@ -75,6 +75,9 @@
     @Column(name = "job_cmd")
     private String cmd;
 
+    @Column(name = "job_executing_msid")
+    private Long executingMsid;
+
     @Column(name = "job_status")
     private int status;
 
@@ -214,6 +217,10 @@
         return null;
     }
 
+    public Long getExecutingMsid() {
+        return executingMsid;
+    }
+
     @Override
     public String getProjectUuid() {
         // TODO Auto-generated method stub
diff --git a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
index 9c5f3e0..6926f67 100644
--- a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
+++ b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
@@ -978,16 +978,12 @@
         allocateVmCapacity(vm, fromLastHost);
       }
 
-      if (newState == State.Stopped) {
-        if (vm.getType() == VirtualMachine.Type.User) {
-
+      if (newState == State.Stopped && event != Event.RestoringFailed && event != Event.RestoringSuccess && vm.getType() == VirtualMachine.Type.User) {
           UserVmVO userVM = _userVMDao.findById(vm.getId());
           _userVMDao.loadDetails(userVM);
           // free the message sent flag if it exists
           userVM.setDetail(VmDetailConstants.MESSAGE_RESERVED_CAPACITY_FREED_FLAG, "false");
           _userVMDao.saveDetails(userVM);
-
-        }
       }
 
       return true;
diff --git a/server/src/main/java/com/cloud/configuration/Config.java b/server/src/main/java/com/cloud/configuration/Config.java
index aeefdb5..f1b5836 100644
--- a/server/src/main/java/com/cloud/configuration/Config.java
+++ b/server/src/main/java/com/cloud/configuration/Config.java
@@ -844,7 +844,7 @@
             "The interval (in milliseconds) when vm stats are retrieved from agents.",
             null),
     VmDiskStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.disk.stats.interval", "0", "Interval (in seconds) to report vm disk statistics.", null),
-    VolumeStatsInterval("Advanced", ManagementServer.class, Integer.class, "volume.stats.interval", "60000", "Interval (in miliseconds) to report volume statistics.", null),
+    VolumeStatsInterval("Advanced", ManagementServer.class, Integer.class, "volume.stats.interval", "60000", "Interval (in milliseconds) to report volume statistics.", null),
     VmTransitionWaitInterval(
             "Advanced",
             ManagementServer.class,
@@ -1621,7 +1621,7 @@
             String.class,
             "implicit.host.tags",
             "GPU",
-            "Tag hosts at the time of host disovery based on the host properties/capabilities",
+            "Tag hosts at the time of host discovery based on the host properties/capabilities",
             null),
     VpcCleanupInterval(
             "Advanced",
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 737cdc7..890fb11 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -295,6 +295,8 @@
 
 public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
     public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class);
+    public static final String PERACCOUNT = "peraccount";
+    public static final String PERZONE = "perzone";
 
     @Inject
     EntityManager _entityMgr;
@@ -458,7 +460,6 @@
     protected Set<String> configValuesForValidation;
     private Set<String> weightBasedParametersForValidation;
     private Set<String> overprovisioningFactorsForValidation;
-    public static final String VM_USERDATA_MAX_LENGTH_STRING = "vm.userdata.max.length";
 
     public static final ConfigKey<Boolean> SystemVMUseLocalStorage = new ConfigKey<Boolean>(Boolean.class, "system.vm.use.local.storage", "Advanced", "false",
             "Indicates whether to use local storage pools or shared storage pools for system VMs.", false, ConfigKey.Scope.Zone, null);
@@ -489,8 +490,6 @@
     public static ConfigKey<Integer> VM_SERVICE_OFFERING_MAX_RAM_SIZE = new ConfigKey<Integer>("Advanced", Integer.class, "vm.serviceoffering.ram.size.max", "0", "Maximum RAM size in "
       + "MB for vm service offering. If 0 - no limitation", true);
 
-    public static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<Integer>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
-            "Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
     public static final ConfigKey<Boolean> MIGRATE_VM_ACROSS_CLUSTERS = new ConfigKey<Boolean>(Boolean.class, "migrate.vm.across.clusters", "Advanced", "false",
             "Indicates whether the VM can be migrated to different cluster if no host is found in same cluster",true, ConfigKey.Scope.Zone, null);
 
@@ -6235,34 +6234,32 @@
     }
 
     void validateSourceNatServiceCapablities(final Map<Capability, String> sourceNatServiceCapabilityMap) {
-        if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) {
-            if (sourceNatServiceCapabilityMap.keySet().size() > 2) {
-                throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter
-                        + " capabilities can be sepcified for source nat service");
-            }
+        if (MapUtils.isNotEmpty(sourceNatServiceCapabilityMap) && (sourceNatServiceCapabilityMap.size() > 2 || ! sourceNatCapabilitiesContainValidValues(sourceNatServiceCapabilityMap))) {
+            throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName()
+                    + ", " + Capability.RedundantRouter
+                    + " capabilities can be specified for source nat service");
+        }
+    }
 
-            for (final Map.Entry<Capability ,String> srcNatPair : sourceNatServiceCapabilityMap.entrySet()) {
-                final Capability capability = srcNatPair.getKey();
-                final String value = srcNatPair.getValue();
-                if (capability == Capability.SupportedSourceNatTypes) {
-                    final boolean perAccount = value.contains("peraccount");
-                    final boolean perZone = value.contains("perzone");
-                    if (perAccount && perZone || !perAccount && !perZone) {
-                        throw new InvalidParameterValueException("Either peraccount or perzone source NAT type can be specified for "
-                                + Capability.SupportedSourceNatTypes.getName());
-                    }
-                } else if (capability == Capability.RedundantRouter) {
-                    final boolean enabled = value.contains("true");
-                    final boolean disabled = value.contains("false");
-                    if (!enabled && !disabled) {
-                        throw new InvalidParameterValueException("Unknown specified value for " + Capability.RedundantRouter.getName());
-                    }
-                } else {
-                    throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter
-                            + " capabilities can be sepcified for source nat service");
+    boolean sourceNatCapabilitiesContainValidValues(Map<Capability, String> sourceNatServiceCapabilityMap) {
+        for (final Entry<Capability ,String> srcNatPair : sourceNatServiceCapabilityMap.entrySet()) {
+            final Capability capability = srcNatPair.getKey();
+            final String value = srcNatPair.getValue();
+            if (Capability.SupportedSourceNatTypes.equals(capability)) {
+                List<String> snatTypes = Arrays.asList(PERACCOUNT, PERZONE);
+                if (! snatTypes.contains(value) || ( value.contains(PERACCOUNT) && value.contains(PERZONE))) {
+                    throw new InvalidParameterValueException("Either peraccount or perzone source NAT type can be specified for "
+                            + Capability.SupportedSourceNatTypes.getName());
                 }
+            } else if (Capability.RedundantRouter.equals(capability)) {
+                if (! Arrays.asList("true", "false").contains(value.toLowerCase())) {
+                    throw new InvalidParameterValueException("Unknown specified value for " + capability.getName());
+                }
+            } else {
+                return false;
             }
         }
+        return true;
     }
 
     void validateStaticNatServiceCapablities(final Map<Capability, String> staticNatServiceCapabilityMap) {
@@ -6439,17 +6436,8 @@
 
             final Map<Capability, String> sourceNatServiceCapabilityMap = serviceCapabilityMap.get(Service.SourceNat);
             if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) {
-                final String sourceNatType = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes);
-                if (sourceNatType != null) {
-                    _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, sourceNatType);
-                    sharedSourceNat = sourceNatType.contains("perzone");
-                }
-
-                final String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter);
-                if (param != null) {
-                    _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param);
-                    redundantRouter = param.contains("true");
-                }
+                sharedSourceNat = isSharedSourceNat(serviceProviderMap, sourceNatServiceCapabilityMap);
+                redundantRouter = isRedundantRouter(serviceProviderMap, sourceNatServiceCapabilityMap);
             }
 
             final Map<Capability, String> staticNatServiceCapabilityMap = serviceCapabilityMap.get(Service.StaticNat);
@@ -6599,6 +6587,26 @@
         });
     }
 
+    boolean isRedundantRouter(Map<Service, Set<Provider>> serviceProviderMap, Map<Capability, String> sourceNatServiceCapabilityMap) {
+        boolean redundantRouter = false;
+        String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter);
+        if (param != null) {
+            _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param);
+            redundantRouter = param.contains("true");
+        }
+        return redundantRouter;
+    }
+
+    boolean isSharedSourceNat(Map<Service, Set<Provider>> serviceProviderMap, Map<Capability, String> sourceNatServiceCapabilityMap) {
+        boolean sharedSourceNat = false;
+        String param = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes);
+        if (param != null) {
+            _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, param);
+            sharedSourceNat = param.contains(PERZONE);
+        }
+        return sharedSourceNat;
+    }
+
     protected void validateNtwkOffDetails(final Map<Detail, String> details, final Map<Service, Set<Provider>> serviceProviderMap) {
         for (final Detail detail : details.keySet()) {
 
diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index cf4e7fd..f71f46f 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -16,6 +16,8 @@
 // under the License.
 package com.cloud.deploy;
 
+import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -23,28 +25,17 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.user.AccountVO;
-import com.cloud.user.dao.AccountDao;
-import com.cloud.exception.StorageUnavailableException;
-import com.cloud.utils.db.Filter;
-import com.cloud.utils.fsm.StateMachine2;
-
-import org.apache.cloudstack.framework.config.ConfigKey;
-import org.apache.cloudstack.framework.config.Configurable;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.log4j.Logger;
 import org.apache.cloudstack.affinity.AffinityGroupProcessor;
 import org.apache.cloudstack.affinity.AffinityGroupService;
 import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
@@ -57,6 +48,8 @@
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.framework.messagebus.MessageBus;
 import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
@@ -64,6 +57,9 @@
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.Listener;
@@ -95,6 +91,7 @@
 import com.cloud.exception.AffinityConflictException;
 import com.cloud.exception.ConnectionException;
 import com.cloud.exception.InsufficientServerCapacityException;
+import com.cloud.exception.StorageUnavailableException;
 import com.cloud.gpu.GPU;
 import com.cloud.host.DetailVO;
 import com.cloud.host.Host;
@@ -115,26 +112,32 @@
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.StoragePoolHostVO;
+import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSCategoryDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.StoragePoolHostDao;
+import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.Pair;
 import com.cloud.utils.component.Manager;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.db.DB;
+import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.db.TransactionCallback;
 import com.cloud.utils.db.TransactionStatus;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.fsm.StateListener;
+import com.cloud.utils.fsm.StateMachine2;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
@@ -144,8 +147,6 @@
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
 
-import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
-
 public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager, Listener,
 StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
 
@@ -266,6 +267,35 @@
         _affinityProcessors = affinityProcessors;
     }
 
+    protected void avoidOtherClustersForDeploymentIfMigrationDisabled(VirtualMachine vm, Host lastHost, ExcludeList avoids) {
+        if (lastHost == null || lastHost.getClusterId() == null ||
+                ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS.valueIn(vm.getDataCenterId())) {
+            return;
+        }
+        List<VolumeVO> volumes = _volsDao.findUsableVolumesForInstance(vm.getId());
+        if (CollectionUtils.isEmpty(volumes)) {
+            return;
+        }
+        boolean storageMigrationNeededDuringClusterMigration = false;
+        for (Volume volume : volumes) {
+            StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId());
+            if (List.of(ScopeType.HOST, ScopeType.CLUSTER).contains(pool.getScope())) {
+                storageMigrationNeededDuringClusterMigration = true;
+                break;
+            }
+        }
+        if (!storageMigrationNeededDuringClusterMigration) {
+            return;
+        }
+        final Long lastHostClusterId = lastHost.getClusterId();
+        s_logger.warn(String.format("VM last host ID: %d belongs to zone ID: %s for which config - %s is false and storage migration would be needed for inter-cluster migration, therefore, adding all other clusters except ID: %d from this zone to avoid list",
+                lastHost.getId(), vm.getDataCenterId(), ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS.key(), lastHostClusterId));
+        List<Long> clusterIds = _clusterDao.listAllClusters(lastHost.getDataCenterId());
+        Set<Long> existingAvoidedClusters = avoids.getClustersToAvoid();
+        clusterIds = clusterIds.stream().filter(x -> !Objects.equals(x, lastHostClusterId) && (existingAvoidedClusters == null || !existingAvoidedClusters.contains(x))).collect(Collectors.toList());
+        avoids.addClusterList(clusterIds);
+    }
+
     @Override
     public DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner)
             throws InsufficientServerCapacityException, AffinityConflictException {
@@ -408,6 +438,8 @@
             planner = getDeploymentPlannerByName(plannerName);
         }
 
+        Host lastHost = null;
+
         String considerLastHostStr = (String)vmProfile.getParameter(VirtualMachineProfile.Param.ConsiderLastHost);
         boolean considerLastHost = vm.getLastHostId() != null && haVmTag == null &&
                 (considerLastHostStr == null || Boolean.TRUE.toString().equalsIgnoreCase(considerLastHostStr));
@@ -415,6 +447,7 @@
             s_logger.debug("This VM has last host_id specified, trying to choose the same host: " + vm.getLastHostId());
 
             HostVO host = _hostDao.findById(vm.getLastHostId());
+            lastHost = host;
             _hostDao.loadHostTags(host);
             _hostDao.loadDetails(host);
             ServiceOfferingDetailsVO offeringDetails = null;
@@ -519,6 +552,8 @@
             s_logger.debug("Cannot choose the last host to deploy this VM ");
         }
 
+        avoidOtherClustersForDeploymentIfMigrationDisabled(vm, lastHost, avoids);
+
         DeployDestination dest = null;
         List<Long> clusterList = null;
 
diff --git a/server/src/main/java/com/cloud/event/dao/EventJoinDaoImpl.java b/server/src/main/java/com/cloud/event/dao/EventJoinDaoImpl.java
index c73f575..24c699a 100644
--- a/server/src/main/java/com/cloud/event/dao/EventJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/event/dao/EventJoinDaoImpl.java
@@ -110,6 +110,9 @@
         responseEvent.setParentId(event.getStartUuid());
         responseEvent.setState(event.getState());
         responseEvent.setUsername(event.getUserName());
+        if (event.getArchived()) {
+            responseEvent.setArchived(true);
+        }
         Long resourceId = event.getResourceId();
         responseEvent.setResourceType(event.getResourceType());
         ApiCommandResourceType resourceType = ApiCommandResourceType.fromString(event.getResourceType());
diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
index b690acd..8fae26e 100644
--- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
@@ -2358,4 +2358,19 @@
     public static ConfigKey<Boolean> getSystemvmpublicipreservationmodestrictness() {
         return SystemVmPublicIpReservationModeStrictness;
     }
+
+    @Override
+    public void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception{
+        Transaction.execute((TransactionCallbackWithException<IpAddress, Exception>) status -> {
+            // update all other IPs to not be sourcenat, should be at most one
+            for(IPAddressVO oldIpAddress :userIps) {
+                oldIpAddress.setSourceNat(false);
+                _ipAddressDao.update(oldIpAddress.getId(), oldIpAddress);
+            }
+            requestedIp.setSourceNat(true);
+            _ipAddressDao.update(requestedIp.getId(),requestedIp);
+            return requestedIp;
+        });
+    }
+
 }
diff --git a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
index e1f4fe7..0cfd6e6 100644
--- a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
@@ -297,6 +297,7 @@
         Vpc copyOfVpc;
         long copyOfVpcId;
         try {
+
             copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(),
                     vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
                     vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu());
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 896292d..ac698f6 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -80,6 +80,8 @@
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 
@@ -264,9 +266,13 @@
     private static final long MIN_GRE_KEY = 0L;
     private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1
     private static final long MIN_VXLAN_VNI = 0L;
+    /**
+     // MAX_VXLAN_VNI should be 16777215L (2^24-1), but Linux vxlan interface doesn't accept VNI:2^24-1 now.
+     // It seems a bug.
+     // Is this still valid (per 2023?)
+     */
     private static final long MAX_VXLAN_VNI = 16777214L; // 2^24 -2
-    // MAX_VXLAN_VNI should be 16777215L (2^24-1), but Linux vxlan interface doesn't accept VNI:2^24-1 now.
-    // It seems a bug.
+    private static final String NETWORK_OFFERING_ID = "networkOfferingId";
 
     @Inject
     DataCenterDao _dcDao = null;
@@ -1289,15 +1295,20 @@
         }
     }
 
-    private void validateRouterIps(String routerIp, String routerIpv6, String startIp, String endIp, String gateway,
-                                   String netmask, String startIpv6, String endIpv6, String ip6Cidr) {
+    void validateSharedNetworkRouterIPs(String gateway, String startIP, String endIP, String netmask, String routerIPv4, String routerIPv6, String startIPv6, String endIPv6, String ip6Cidr, NetworkOffering ntwkOff) {
+        if (ntwkOff.getGuestType() == GuestType.Shared) {
+            validateSharedNetworkRouterIPv4(routerIPv4, startIP, endIP, gateway, netmask);
+            validateSharedNetworkRouterIPv6(routerIPv6, startIPv6, endIPv6, ip6Cidr);
+
+        }
+    }
+
+    private void validateSharedNetworkRouterIPv4(String routerIp, String startIp, String endIp, String gateway, String netmask) {
         if (StringUtils.isNotBlank(routerIp)) {
             if (startIp != null && endIp == null) {
                 endIp = startIp;
             }
-            if (!NetUtils.isValidIp4(routerIp)) {
-                throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
-            }
+            isIPv4AddressValid(routerIp);
             if (StringUtils.isNoneBlank(startIp, endIp)) {
                 if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) {
                     throw new CloudRuntimeException("Router IPv4 IP provided is not within the specified range: " + startIp + " - " + endIp);
@@ -1309,26 +1320,39 @@
                 }
             }
         }
-        if (StringUtils.isNotBlank(routerIpv6)) {
-            if (startIpv6 != null && endIpv6 == null) {
-                endIpv6 = startIpv6;
+    }
+
+    private void validateSharedNetworkRouterIPv6(String routerIPv6, String startIPv6, String endIPv6, String cidrIPv6) {
+        if (StringUtils.isNotBlank(routerIPv6)) {
+            if (startIPv6 != null && endIPv6 == null) {
+                endIPv6 = startIPv6;
             }
-            if (!NetUtils.isValidIp6(routerIpv6)) {
-                throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
-            }
-            if (StringUtils.isNoneBlank(startIpv6, endIpv6)) {
-                String ipv6Range = startIpv6 + "-" + endIpv6;
-                if (!NetUtils.isIp6InRange(routerIpv6, ipv6Range)) {
-                    throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIpv6 + " - " + endIpv6);
+            isIPv6AddressValid(routerIPv6);
+            if (StringUtils.isNoneBlank(startIPv6, endIPv6)) {
+                String ipv6Range = startIPv6 + "-" + endIPv6;
+                if (!NetUtils.isIp6InRange(routerIPv6, ipv6Range)) {
+                    throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIPv6 + " - " + endIPv6);
                 }
             } else {
-                if (!NetUtils.isIp6InNetwork(routerIpv6, ip6Cidr)) {
+                if (!NetUtils.isIp6InNetwork(routerIPv6, cidrIPv6)) {
                     throw new CloudRuntimeException("Router IPv6 address provided is not with the network range");
                 }
             }
         }
     }
 
+    private void isIPv4AddressValid(String routerIp) {
+        if (!NetUtils.isValidIp4(routerIp)) {
+            throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
+        }
+    }
+
+    private void isIPv6AddressValid(String routerIPv6) {
+        if (!NetUtils.isValidIp6(routerIPv6)) {
+            throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
+        }
+    }
+
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network")
@@ -1339,34 +1363,26 @@
         String endIP = cmd.getEndIp();
         String netmask = cmd.getNetmask();
         String networkDomain = cmd.getNetworkDomain();
-        String vlanId = null;
-        boolean bypassVlanOverlapCheck = false;
-        boolean hideIpAddressUsage = false;
-        String routerIp = null;
-        String routerIpv6 = null;
-        if (cmd instanceof CreateNetworkCmdByAdmin) {
-            vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan();
-            bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
-            hideIpAddressUsage = ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
-            routerIp = ((CreateNetworkCmdByAdmin)cmd).getRouterIp();
-            routerIpv6 = ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6();
-        }
+
+        boolean adminCalledUs = cmd instanceof CreateNetworkCmdByAdmin;
+        String vlanId = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getVlan() : null;
+        boolean bypassVlanOverlapCheck = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
+        boolean hideIpAddressUsage = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
+        String routerIPv4 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIp() : null;
+        String routerIPv6 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6() : null;
 
         String name = cmd.getNetworkName();
         String displayText = cmd.getDisplayText();
         Account caller = CallContext.current().getCallingAccount();
         Long physicalNetworkId = cmd.getPhysicalNetworkId();
-        Long zoneId = cmd.getZoneId();
-        String aclTypeStr = cmd.getAclType();
         Long domainId = cmd.getDomainId();
-        boolean isDomainSpecific = false;
         Boolean subdomainAccess = cmd.getSubdomainAccess();
         Long vpcId = cmd.getVpcId();
         String startIPv6 = cmd.getStartIpv6();
         String endIPv6 = cmd.getEndIpv6();
         String ip6Gateway = cmd.getIp6Gateway();
         String ip6Cidr = cmd.getIp6Cidr();
-        Boolean displayNetwork = cmd.getDisplayNetwork();
+        boolean displayNetwork = ! Boolean.FALSE.equals(cmd.getDisplayNetwork());
         Long aclId = cmd.getAclId();
         String isolatedPvlan = cmd.getIsolatedPvlan();
         String externalId = cmd.getExternalId();
@@ -1379,127 +1395,31 @@
         String ip6Dns1 = cmd.getIp6Dns1();
         String ip6Dns2 = cmd.getIp6Dns2();
 
-        // Validate network offering
-        NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
-        if (ntwkOff == null || ntwkOff.isSystemOnly()) {
-            InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id");
-            if (ntwkOff != null) {
-                ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId");
-            }
-            throw ex;
-        }
+        // Validate network offering id
+        NetworkOffering ntwkOff = getAndValidateNetworkOffering(networkOfferingId);
 
-        Account owner = null;
-        if ((cmd.getAccountName() != null && domainId != null) || cmd.getProjectId() != null) {
-            owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), domainId, cmd.getProjectId());
-        } else {
-            s_logger.info(String.format("Assigning the network to caller:%s because either projectId or accountname and domainId are not provided", caller.getAccountName()));
-            owner = caller;
-        }
+        Account owner = getOwningAccount(cmd, caller);
 
-        // validate physical network and zone
-        // Check if physical network exists
-        PhysicalNetwork pNtwk = null;
-        if (physicalNetworkId != null) {
-            pNtwk = _physicalNetworkDao.findById(physicalNetworkId);
-            if (pNtwk == null) {
-                throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
-            }
-        }
+        PhysicalNetwork pNtwk = getAndValidatePhysicalNetwork(physicalNetworkId);
 
-        if (zoneId == null) {
-            zoneId = pNtwk.getDataCenterId();
-        }
-
-        if (displayNetwork == null) {
-            displayNetwork = true;
-        }
-
-        DataCenter zone = _dcDao.findById(zoneId);
-        if (zone == null) {
-            throw new InvalidParameterValueException("Specified zone id was not found");
-        }
+        DataCenter zone = getAndValidateZone(cmd, pNtwk);
 
         _accountMgr.checkAccess(owner, ntwkOff, zone);
 
-        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
-            // See DataCenterVO.java
-            PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
-            ex.addProxyObject(zone.getUuid(), "zoneId");
-            throw ex;
-        }
+        validateZoneAvailability(caller, zone);
 
-        // Only domain and account ACL types are supported in Acton.
-        ACLType aclType = null;
-        if (aclTypeStr != null) {
-            if (aclTypeStr.equalsIgnoreCase(ACLType.Account.toString())) {
-                aclType = ACLType.Account;
-            } else if (aclTypeStr.equalsIgnoreCase(ACLType.Domain.toString())) {
-                aclType = ACLType.Domain;
-            } else {
-                throw new InvalidParameterValueException("Incorrect aclType specified. Check the API documentation for supported types");
-            }
-            // In 3.0 all Shared networks should have aclType == Domain, all Isolated networks aclType==Account
-            if (ntwkOff.getGuestType() == GuestType.Isolated) {
-                if (aclType != ACLType.Account) {
-                    throw new InvalidParameterValueException("AclType should be " + ACLType.Account + " for network of type " + Network.GuestType.Isolated);
-                }
-            } else if (ntwkOff.getGuestType() == GuestType.Shared) {
-                if (!(aclType == ACLType.Domain || aclType == ACLType.Account)) {
-                    throw new InvalidParameterValueException("AclType should be " + ACLType.Domain + " or " + ACLType.Account + " for network of type " + Network.GuestType.Shared);
-                }
-            }
-        } else {
-            if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2) {
-                aclType = ACLType.Account;
-            } else if (ntwkOff.getGuestType() == GuestType.Shared) {
-                if (_accountMgr.isRootAdmin(caller.getId())) {
-                    aclType = ACLType.Domain;
-                } else if (_accountMgr.isNormalUser(caller.getId())) {
-                    aclType = ACLType.Account;
-                } else {
-                    throw new InvalidParameterValueException("AclType must be specified for shared network created by domain admin");
-                }
-            }
-        }
+        ACLType aclType = getAclType(caller, cmd.getAclType(), ntwkOff);
 
-        if (ntwkOff.getGuestType() != GuestType.Shared && (!StringUtils.isAllBlank(routerIp, routerIpv6))) {
+        if (ntwkOff.getGuestType() != GuestType.Shared && (!StringUtils.isAllBlank(routerIPv4, routerIPv6))) {
             throw new InvalidParameterValueException("Router IP can be specified only for Shared networks");
         }
 
         if (ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.isProviderForNetworkOffering(Provider.VirtualRouter, networkOfferingId)
-                && (!StringUtils.isAllBlank(routerIp, routerIpv6))) {
+                && (!StringUtils.isAllBlank(routerIPv4, routerIPv6))) {
             throw new InvalidParameterValueException("Virtual Router is not a supported provider for the Shared network, hence router ip should not be provided");
         }
 
-        // Check if the network is domain specific
-        if (aclType == ACLType.Domain) {
-            // only Admin can create domain with aclType=Domain
-            if (!_accountMgr.isAdmin(caller.getId())) {
-                throw new PermissionDeniedException("Only admin can create networks with aclType=Domain");
-            }
-
-            // only shared networks can be Domain specific
-            if (ntwkOff.getGuestType() != GuestType.Shared) {
-                throw new InvalidParameterValueException("Only " + GuestType.Shared + " networks can have aclType=" + ACLType.Domain);
-            }
-
-            if (domainId != null) {
-                if (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Shared) {
-                    throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + Network.GuestType.Shared);
-                }
-
-                DomainVO domain = _domainDao.findById(domainId);
-                if (domain == null) {
-                    throw new InvalidParameterValueException("Unable to find domain by specified id");
-                }
-                _accountMgr.checkAccess(caller, domain);
-            }
-            isDomainSpecific = true;
-
-        } else if (subdomainAccess != null) {
-            throw new InvalidParameterValueException("Parameter subDomainAccess can be specified only with aclType=Domain");
-        }
+        boolean isDomainSpecific = isDomainSpecificNetworkRequested(caller, domainId, subdomainAccess, ntwkOff, aclType);
 
         if (aclType == ACLType.Domain) {
             owner = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
@@ -1601,7 +1521,8 @@
             }
         }
 
-        validateRouterIps(routerIp, routerIpv6, startIP, endIP, gateway, netmask, startIPv6, endIPv6, ip6Cidr);
+        validateSharedNetworkRouterIPs(gateway, startIP, endIP, netmask, routerIPv4, routerIPv6, startIPv6, endIPv6, ip6Cidr, ntwkOff);
+
         Pair<String, String> ip6GatewayCidr = null;
         if (zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getGuestType() == GuestType.Isolated) {
             ipv6 = _networkOfferingDao.isIpv6Supported(ntwkOff.getId());
@@ -1673,7 +1594,7 @@
         if (cidr != null && providersConfiguredForExternalNetworking(ntwkProviders)) {
             if (ntwkOff.getGuestType() == GuestType.Shared && (zone.getNetworkType() == NetworkType.Advanced) && isSharedNetworkOfferingWithServices(networkOfferingId)) {
                 // validate if CIDR specified overlaps with any of the CIDR's allocated for isolated networks and shared networks in the zone
-                checkSharedNetworkCidrOverlap(zoneId, pNtwk.getId(), cidr);
+                checkSharedNetworkCidrOverlap(zone.getId(), pNtwk.getId(), cidr);
             } else {
                 // if the guest network is for the VPC, if any External Provider are supported in VPC
                 // cidr will not be null as it is generated from the super cidr of vpc.
@@ -1698,10 +1619,10 @@
 
         // Can add vlan range only to the network which allows it
         if (createVlan && !ntwkOff.isSpecifyIpRanges()) {
-            throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), "networkOfferingId");
+            throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), NETWORK_OFFERING_ID);
         }
 
-        Pair<Integer, Integer> interfaceMTUs = validateMtuConfig(publicMtu, privateMtu, zoneId);
+        Pair<Integer, Integer> interfaceMTUs = validateMtuConfig(publicMtu, privateMtu, zone.getId());
         mtuCheckForVpcNetwork(vpcId, interfaceMTUs, publicMtu, privateMtu);
 
         Network associatedNetwork = null;
@@ -1720,9 +1641,12 @@
 
         checkNetworkDns(ipv6, ntwkOff, vpcId, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
 
-        Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId,
+        Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zone.getId(),
                 domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan,
-                externalId, routerIp, routerIpv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs);
+                externalId, routerIPv4, routerIPv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs);
+
+        // retrieve, acquire and associate the correct ip adresses
+        checkAndSetRouterSourceNatIp(owner, cmd, network);
 
         if (hideIpAddressUsage) {
             _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false));
@@ -1739,6 +1663,213 @@
         return network;
     }
 
+    void checkAndSetRouterSourceNatIp(Account owner, CreateNetworkCmd cmd, Network network) throws InsufficientAddressCapacityException, ResourceAllocationException {
+        String sourceNatIp = cmd.getSourceNatIP();
+        if (sourceNatIp == null) {
+            s_logger.debug(String.format("no source nat ip given for create network %s command, using something arbitrary.", cmd.getNetworkName()));
+            return; // nothing to try
+        }
+        IpAddress ip = allocateIP(owner, cmd.getZoneId(), network.getId(), null, sourceNatIp);
+        try {
+            associateIPToNetwork(ip.getId(), network.getId());
+        } catch (ResourceUnavailableException e) {
+            String msg = String.format("can´t use %s as sourcenat IP address for network %s/%s as it is un available", sourceNatIp, network.getName(), network.getUuid());
+            s_logger.error(msg);
+            throw new CloudRuntimeException(msg,e);
+        }
+    }
+
+    /**
+     * @param cmd
+     * @param network
+     * @return whether the sourceNat is changed, and consequently restart is needed
+     * @throws InsufficientAddressCapacityException
+     * @throws ResourceAllocationException
+     */
+    private boolean checkAndUpdateRouterSourceNatIp(UpdateNetworkCmd cmd, Network network) {
+        IPAddressVO requestedIp = checkSourceNatIpAddressForUpdate(cmd, network);
+        if (requestedIp == null) return false; // ip not associated with this network
+
+        List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), true);
+        if (! userIps.isEmpty()) {
+            try {
+                _ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
+            } catch (Exception e) { // pokemon execption from transaction
+                String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s",
+                        requestedIp.getAddress().addr(), network.getName(), network.getUuid(), e.getLocalizedMessage());
+                s_logger.error(msg);
+                throw new CloudRuntimeException(msg, e);
+            }
+        }
+        return true;
+    }
+
+    @Nullable
+    private IPAddressVO checkSourceNatIpAddressForUpdate(UpdateNetworkCmd cmd, Network network) {
+        String sourceNatIp = cmd.getSourceNatIP();
+        if (sourceNatIp == null) {
+            s_logger.trace(String.format("no source NAT ip given to update network %s with.", cmd.getNetworkName()));
+            return null;
+        } else {
+            s_logger.info(String.format("updating network %s to have source NAT ip %s", cmd.getNetworkName(), sourceNatIp));
+        }
+        // check if the address is already aqcuired for this network
+        IPAddressVO requestedIp = _ipAddressDao.findByIp(sourceNatIp);
+        if (requestedIp == null || requestedIp.getAssociatedWithNetworkId() == null || ! requestedIp.getAssociatedWithNetworkId().equals(network.getId())) {
+            s_logger.warn(String.format("Source NAT IP %s is not associated with network %s/%s. It cannot be used as source NAT IP.",
+                    sourceNatIp, network.getName(), network.getUuid()));
+            return null;
+        }
+        // check if it is the current source NAT address
+        if (requestedIp.isSourceNat()) {
+            s_logger.info(String.format("IP address %s is allready the source Nat address. Not updating!", sourceNatIp));
+            return null;
+        }
+        return requestedIp;
+    }
+
+    @Nullable
+    private ACLType getAclType(Account caller, String aclTypeStr, NetworkOffering ntwkOff) {
+        // Only domain and account ACL types are supported in Acton.
+        ACLType aclType = null;
+        if (aclTypeStr != null) {
+            aclType = getAclType(aclTypeStr, ntwkOff);
+        } else {
+            aclType = getAclType(caller, ntwkOff, aclType);
+        }
+        return aclType;
+    }
+
+    @NotNull
+    private static ACLType getAclType(String aclTypeStr, NetworkOffering ntwkOff) {
+        ACLType aclType;
+        if (aclTypeStr.equalsIgnoreCase(ACLType.Account.toString())) {
+            aclType = ACLType.Account;
+        } else if (aclTypeStr.equalsIgnoreCase(ACLType.Domain.toString())) {
+            aclType = ACLType.Domain;
+        } else {
+            throw new InvalidParameterValueException("Incorrect aclType specified. Check the API documentation for supported types");
+        }
+        // In 3.0 all Shared networks should have aclType == Domain, all Isolated networks aclType==Account
+        if (ntwkOff.getGuestType() == GuestType.Isolated && aclType != ACLType.Account) {
+            throw new InvalidParameterValueException("AclType should be " + ACLType.Account + " for network of type " + GuestType.Isolated);
+        }
+        return aclType;
+    }
+
+    private ACLType getAclType(Account caller, NetworkOffering ntwkOff, ACLType aclType) {
+        if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2) {
+            aclType = ACLType.Account;
+        } else if (ntwkOff.getGuestType() == GuestType.Shared) {
+            if (_accountMgr.isRootAdmin(caller.getId())) {
+                aclType = ACLType.Domain;
+            } else if (_accountMgr.isNormalUser(caller.getId())) {
+                aclType = ACLType.Account;
+            } else {
+                throw new InvalidParameterValueException("AclType must be specified for shared network created by domain admin");
+            }
+        }
+        return aclType;
+    }
+
+    private void validateZoneAvailability(Account caller, DataCenter zone) {
+        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
+            // See DataCenterVO.java
+            PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
+            ex.addProxyObject(zone.getUuid(), "zoneId");
+            throw ex;
+        }
+    }
+
+    private boolean isDomainSpecificNetworkRequested(Account caller, Long domainId, Boolean subdomainAccess, NetworkOffering ntwkOff, ACLType aclType) {
+        boolean isDomainSpecific = false;
+        // Check if the network is domain specific
+        if (aclType == ACLType.Domain) {
+            // only Admin can create domain with aclType=Domain
+            if (!_accountMgr.isAdmin(caller.getId())) {
+                throw new PermissionDeniedException("Only admin can create networks with aclType=Domain");
+            }
+
+            // only shared networks can be Domain specific
+            if (ntwkOff.getGuestType() != GuestType.Shared) {
+                throw new InvalidParameterValueException("Only " + GuestType.Shared + " networks can have aclType=" + ACLType.Domain);
+            }
+
+            if (domainId != null) {
+                if (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != GuestType.Shared) {
+                    throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + GuestType.Shared);
+                }
+
+                DomainVO domain = _domainDao.findById(domainId);
+                if (domain == null) {
+                    throw new InvalidParameterValueException("Unable to find domain by specified id");
+                }
+                _accountMgr.checkAccess(caller, domain);
+            }
+            isDomainSpecific = true;
+
+        } else if (subdomainAccess != null) {
+            throw new InvalidParameterValueException("Parameter subDomainAccess can be specified only with aclType=Domain");
+        }
+        return isDomainSpecific;
+    }
+
+    @NotNull
+    private DataCenter getAndValidateZone(CreateNetworkCmd cmd, PhysicalNetwork pNtwk) {
+        Long zoneId = (cmd.getZoneId() == null) ? pNtwk.getDataCenterId() : cmd.getZoneId();
+        DataCenter zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new InvalidParameterValueException("Specified zone id was not found");
+        }
+        return zone;
+    }
+
+    /**
+     // validate physical network and zone
+     // Check if physical network exists
+     *
+     * @param physicalNetworkId the id of the required physical network
+     * @return the data object for the physical network
+     */
+    @NotNull
+    private PhysicalNetwork getAndValidatePhysicalNetwork(Long physicalNetworkId) {
+        PhysicalNetwork pNtwk = null;
+        if (physicalNetworkId != null) {
+            pNtwk = getPhysicalNetwork(physicalNetworkId);
+            if (pNtwk == null) {
+                throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
+            }
+        } else {
+            throw new CloudRuntimeException("cannot create Guestnetwork without physical network.");
+        }
+        return pNtwk;
+    }
+
+    private Account getOwningAccount(CreateNetworkCmd cmd, Account caller) {
+        Account owner = null;
+        Long domainId = cmd.getDomainId();
+        if ((cmd.getAccountName() != null && domainId != null) || cmd.getProjectId() != null) {
+            owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), domainId, cmd.getProjectId());
+        } else {
+            s_logger.info(String.format("Assigning the network to caller:%s because either projectId or accountname and domainId are not provided", caller.getAccountName()));
+            owner = caller;
+        }
+        return owner;
+    }
+
+    @NotNull
+    private NetworkOffering getAndValidateNetworkOffering(Long networkOfferingId) {
+        NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
+        if (ntwkOff == null || ntwkOff.isSystemOnly()) {
+            InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id");
+            if (ntwkOff != null) {
+                ex.addProxyObject(ntwkOff.getUuid(), NETWORK_OFFERING_ID);
+            }
+            throw ex;
+        }
+        return ntwkOff;
+    }
+
     protected void mtuCheckForVpcNetwork(Long vpcId, Pair<Integer, Integer> interfaceMTUs, Integer publicMtu, Integer privateMtu) {
         if (vpcId != null && publicMtu != null) {
             VpcVO vpc = _vpcDao.findById(vpcId);
@@ -1853,7 +1984,7 @@
         }
     }
 
-    private void validateNetworkOfferingForNonRootAdminUser(NetworkOfferingVO ntwkOff) {
+    private void validateNetworkOfferingForNonRootAdminUser(NetworkOffering ntwkOff) {
         if (ntwkOff.getTrafficType() != TrafficType.Guest) {
             throw new InvalidParameterValueException("This user can only create a Guest network");
         }
@@ -1915,7 +2046,7 @@
     private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String vlanIdFinal,
                                   final Boolean bypassVlanOverlapCheck, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId,
                                   final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr,
-                                  final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
+                                  final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOffering ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
                                   final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6,
                                   final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs) throws InsufficientCapacityException, ResourceAllocationException {
         try {
@@ -2364,7 +2495,7 @@
         }
 
         if (networkOfferingId != null) {
-            sc.addAnd("networkOfferingId", SearchCriteria.Op.EQ, networkOfferingId);
+            sc.addAnd(NETWORK_OFFERING_ID, SearchCriteria.Op.EQ, networkOfferingId);
         }
 
         if (associatedNetworkId != null) {
@@ -2787,6 +2918,8 @@
         _accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network);
         _accountMgr.checkAccess(_accountMgr.getActiveAccountById(network.getAccountId()), offering, _dcDao.findById(network.getDataCenterId()));
 
+        restartNetwork |= checkAndUpdateRouterSourceNatIp(cmd, network);
+
         if (cmd instanceof UpdateNetworkCmdByAdmin) {
             final Boolean hideIpAddressUsage = ((UpdateNetworkCmdByAdmin) cmd).getHideIpAddressUsage();
             if (hideIpAddressUsage != null) {
@@ -2835,13 +2968,13 @@
         NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId);
         if (networkOfferingId != null) {
             if (networkOffering == null || networkOffering.isSystemOnly()) {
-                throwInvalidIdException("Unable to find network offering with specified id", networkOfferingId.toString(), "networkOfferingId");
+                throwInvalidIdException("Unable to find network offering with specified id", networkOfferingId.toString(), NETWORK_OFFERING_ID);
             }
 
             // network offering should be in Enabled state
             if (networkOffering.getState() != NetworkOffering.State.Enabled) {
                 throwInvalidIdException("Network offering with specified id is not in " + NetworkOffering.State.Enabled + " state, can't upgrade to it", networkOffering.getUuid(),
-                        "networkOfferingId");
+                        NETWORK_OFFERING_ID);
             }
             //can't update from vpc to non-vpc network offering
             boolean forVpcNew = _configMgr.isOfferingForVpc(networkOffering);
@@ -2884,10 +3017,8 @@
             }
         }
 
-        if (checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2,
-                ip6Dns1, ip6Dns2)) {
-            restartNetwork = true;
-        }
+        restartNetwork |= checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2,
+                ip6Dns1, ip6Dns2);
 
         final Map<String, String> newSvcProviders = networkOfferingChanged
                 ? _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())
@@ -3084,7 +3215,7 @@
             if (servicesNotInNewOffering != null && !servicesNotInNewOffering.isEmpty()) {
                 _networkMgr.cleanupConfigForServicesInNetwork(servicesNotInNewOffering, network);
             }
-        } catch (Throwable e) {
+        } catch (Exception e) { // old pokemon catch that used to catch throwable
             s_logger.debug("failed to cleanup config related to unused services error:" + e.getMessage());
         }
 
@@ -3638,7 +3769,7 @@
         if (newNtwkOff == null || newNtwkOff.isSystemOnly()) {
             InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering.");
             if (newNtwkOff != null) {
-                ex.addProxyObject(String.valueOf(newNtwkOff.getId()), "networkOfferingId");
+                ex.addProxyObject(String.valueOf(newNtwkOff.getId()), NETWORK_OFFERING_ID);
             }
             throw ex;
         }
@@ -4078,7 +4209,7 @@
         for (String vlanRange : listOfRanges) {
             String[] VnetRange = vlanRange.split("-");
 
-            // Init with [min,max] of VLAN. Actually 0x000 and 0xFFF are reserved by IEEE, shoudn't be used.
+            // Init with [min,max] of VLAN. Actually 0x000 and 0xFFF are reserved by IEEE, shouldn't be used.
             long minVnet = MIN_VLAN_ID;
             long maxVnet = MAX_VLAN_ID;
 
diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index 9343360..99d3e7d 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -542,6 +542,7 @@
 
                 router.setDynamicallyScalable(template.isDynamicallyScalable());
                 router.setRole(Role.VIRTUAL_ROUTER);
+                router.setLimitCpuUse(routerOffering.getLimitCpuUse());
                 router = _routerDao.persist(router);
 
                 reallocateRouterNetworks(routerDeploymentDefinition, router, template, null);
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index f568266..5de05a1 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -53,7 +53,9 @@
 import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
 import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd;
 import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd;
+import org.apache.cloudstack.api.command.user.vpc.ListVPCsCmd;
 import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd;
+import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -62,6 +64,7 @@
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.log4j.Logger;
+import org.jetbrains.annotations.Nullable;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 
@@ -1017,7 +1020,7 @@
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true)
     public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwnerId, final String vpcName, final String displayText, final String cidr, String networkDomain,
-            final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer publicMtu) throws ResourceAllocationException {
+                         final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer publicMtu) throws ResourceAllocationException {
         final Account caller = CallContext.current().getCallingAccount();
         final Account owner = _accountMgr.getAccount(vpcOwnerId);
 
@@ -1091,6 +1094,7 @@
         final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff,
                 vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
             vpc.setPublicMtu(publicMtu);
+            vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
 
         return createVpc(displayVpc, vpc);
     }
@@ -1098,9 +1102,24 @@
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true)
     public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException {
-        return createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
+        Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
             cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(),
             cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu());
+        // associate cmd.getSourceNatIP() with this vpc
+        allocateSourceNatIp(vpc, cmd.getSourceNatIP());
+        return vpc;
+    }
+
+    private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) {
+        Account account = _accountMgr.getAccount(vpc.getAccountId());
+        DataCenter zone = _dcDao.findById(vpc.getZoneId());
+        // reserve this ip and then
+        try {
+            IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId(), zone, null, sourceNatIP);
+            this.associateIPToVpc(ip.getId(), vpc.getId());
+        } catch (ResourceAllocationException | ResourceUnavailableException | InsufficientAddressCapacityException e){
+            throw new CloudRuntimeException("new source NAT address cannot be acquired", e);
+        }
     }
 
     @DB
@@ -1126,10 +1145,6 @@
         return Transaction.execute(new TransactionCallback<VpcVO>() {
             @Override
             public VpcVO doInTransaction(final TransactionStatus status) {
-                if (displayVpc != null) {
-                    vpc.setDisplay(displayVpc);
-                }
-
                 final VpcVO persistedVpc = vpcDao.persist(vpc, finalizeServicesAndProvidersForVpc(vpc.getZoneId(), vpc.getVpcOfferingId()));
                 _resourceLimitMgr.incrementResourceCount(vpc.getAccountId(), ResourceType.vpc);
                 s_logger.debug("Created VPC " + persistedVpc);
@@ -1243,8 +1258,13 @@
     }
 
     @Override
+    public Vpc updateVpc(UpdateVPCCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException {
+        return updateVpc(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(), cmd.getCustomId(), cmd.isDisplayVpc(), cmd.getPublicMtu(), cmd.getSourceNatIP());
+    }
+
+    @Override
     @ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription = "updating vpc")
-    public Vpc updateVpc(final long vpcId, final String vpcName, final String displayText, final String customId, final Boolean displayVpc, Integer mtu) {
+    public Vpc updateVpc(final long vpcId, final String vpcName, final String displayText, final String customId, final Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException {
         CallContext.current().setEventDetails(" Id: " + vpcId);
         final Account caller = CallContext.current().getCallingAccount();
 
@@ -1279,14 +1299,80 @@
             updateMtuOfVpcNetwork(vpcToUpdate, vpc, mtu);
         }
 
-        if (vpcDao.update(vpcId, vpc)) {
+        boolean restartRequired = checkAndUpdateRouterSourceNatIp(vpcToUpdate, sourceNatIp);
+
+        if (vpcDao.update(vpcId, vpc) || restartRequired) { // Note that the update may fail because nothing has changed, other than the sourcenat ip
             s_logger.debug("Updated VPC id=" + vpcId);
+            if (restartRequired) {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug(String.format("restarting vpc %s/%s, due to changing sourcenat in Update VPC call", vpc.getName(), vpc.getUuid()));
+                }
+                final User callingUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
+                restartVpc(vpcId, true, false, false, callingUser);
+            } else {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("no restart needed.");
+                }
+            }
             return vpcDao.findById(vpcId);
         } else {
+            s_logger.error(String.format("failed to update vpc %s/%s",vpc.getName(), vpc.getUuid()));
             return null;
         }
     }
 
+    private boolean checkAndUpdateRouterSourceNatIp(Vpc vpc, String sourceNatIp) {
+        IPAddressVO requestedIp = validateSourceNatip(vpc, sourceNatIp);
+        if (requestedIp == null) return false; // ip not associated with this network
+
+        List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedVpc(vpc.getId(), true);
+        if (! userIps.isEmpty()) {
+            try {
+                _ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
+            } catch (Exception e) { // pokemon exception from transaction
+                String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s",
+                        requestedIp.getAddress().addr(), vpc.getName(), vpc.getUuid(), e.getLocalizedMessage());
+                s_logger.error(msg);
+                throw new CloudRuntimeException(msg, e);
+            }
+        }
+        return true;
+    }
+
+    @Nullable
+    protected IPAddressVO validateSourceNatip(Vpc vpc, String sourceNatIp) {
+        if (sourceNatIp == null) {
+            s_logger.trace(String.format("no source NAT ip given to update vpc %s with.", vpc.getName()));
+            return null;
+        } else {
+            s_logger.info(String.format("updating VPC %s to have source NAT ip %s", vpc.getName(), sourceNatIp));
+        }
+        IPAddressVO requestedIp = getIpAddressVO(vpc, sourceNatIp);
+        if (requestedIp == null) return null;
+        // check if it is the current source NAT address
+        if (requestedIp.isSourceNat()) {
+            s_logger.info(String.format("IP address %s is already the source Nat address. Not updating!", sourceNatIp));
+            return null;
+        }
+        if (_firewallDao.countRulesByIpId(requestedIp.getId()) > 0) {
+            s_logger.info(String.format("IP address %s has firewall/portforwarding rules. Not updating!", sourceNatIp));
+            return null;
+        }
+        return requestedIp;
+    }
+
+    @Nullable
+    private IPAddressVO getIpAddressVO(Vpc vpc, String sourceNatIp) {
+        // check if the address is already aqcuired for this network
+        IPAddressVO requestedIp = _ipAddressDao.findByIp(sourceNatIp);
+        if (requestedIp == null || requestedIp.getVpcId() == null || ! requestedIp.getVpcId().equals(vpc.getId())) {
+            s_logger.warn(String.format("Source NAT IP %s is not associated with network %s/%s. It cannot be used as source NAT IP.",
+                    sourceNatIp, vpc.getName(), vpc.getUuid()));
+            return null;
+        }
+        return requestedIp;
+    }
+
     protected Integer validateMtu(VpcVO vpcToUpdate, Integer mtu) {
         Long zoneId = vpcToUpdate.getZoneId();
         if (mtu == null || NetworkService.AllowUsersToSpecifyVRMtu.valueIn(zoneId)) {
@@ -1374,6 +1460,13 @@
     }
 
     @Override
+    public Pair<List<? extends Vpc>, Integer> listVpcs(ListVPCsCmd cmd) {
+        return listVpcs(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(), cmd.getSupportedServices(), cmd.getCidr(), cmd.getVpcOffId(),
+                cmd.getState(), cmd.getAccountName(), cmd.getDomainId(), cmd.getKeyword(), cmd.getStartIndex(), cmd.getPageSizeVal(),
+                cmd.getZoneId(), cmd.isRecursive(), cmd.listAll(), cmd.getRestartRequired(), cmd.getTags(), cmd.getProjectId(),
+                cmd.getDisplay());
+    }
+    @Override
     public Pair<List<? extends Vpc>, Integer> listVpcs(final Long id, final String vpcName, final String displayText, final List<String> supportedServicesStr, final String cidr,
                                                        final Long vpcOffId, final String state, final String accountName, Long domainId, final String keyword, final Long startIndex, final Long pageSizeVal,
                                                        final Long zoneId, Boolean isRecursive, final Boolean listAll, final Boolean restartRequired, final Map<String, String> tags, final Long projectId,
@@ -1476,7 +1569,7 @@
         final boolean listBySupportedServices = supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty();
 
         if (listBySupportedServices) {
-            final List<VpcVO> supportedVpcs = new ArrayList<VpcVO>();
+            final List<Vpc> supportedVpcs = new ArrayList<>();
             Service[] supportedServices = null;
 
             if (listBySupportedServices) {
@@ -1501,22 +1594,20 @@
 
             final List<? extends Vpc> wPagination = StringUtils.applyPagination(supportedVpcs, startIndex, pageSizeVal);
             if (wPagination != null) {
-                final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, supportedVpcs.size());
-                return listWPagination;
+                return new Pair<>(wPagination, supportedVpcs.size());
             }
-            return new Pair<List<? extends Vpc>, Integer>(supportedVpcs, supportedVpcs.size());
+            return new Pair<>(supportedVpcs, supportedVpcs.size());
         } else {
             final List<? extends Vpc> wPagination = StringUtils.applyPagination(vpcs, startIndex, pageSizeVal);
             if (wPagination != null) {
-                final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, vpcs.size());
-                return listWPagination;
+                return new Pair<>(wPagination, vpcs.size());
             }
-            return new Pair<List<? extends Vpc>, Integer>(vpcs, vpcs.size());
+            return new Pair<>(vpcs, vpcs.size());
         }
     }
 
     protected List<Service> getSupportedServices() {
-        final List<Service> services = new ArrayList<Service>();
+        final List<Service> services = new ArrayList<>();
         services.add(Network.Service.Dhcp);
         services.add(Network.Service.Dns);
         services.add(Network.Service.UserData);
diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java
index f87ab4a..19776d4 100644
--- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java
+++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java
@@ -18,9 +18,11 @@
 
 import java.io.UnsupportedEncodingException;
 import java.security.SecureRandom;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.TimeZone;
 import java.util.UUID;
 import java.util.concurrent.Executors;
@@ -33,26 +35,20 @@
 import javax.mail.MessagingException;
 import javax.naming.ConfigurationException;
 
-import com.cloud.network.dao.NetworkDao;
-import com.cloud.network.dao.NetworkVO;
-import com.cloud.network.vpc.Vpc;
-import com.cloud.network.vpc.VpcManager;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.dao.UserVmDao;
-import com.cloud.vm.snapshot.VMSnapshotVO;
-import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 import org.apache.cloudstack.acl.ProjectRole;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.acl.dao.ProjectRoleDao;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.framework.messagebus.MessageBus;
 import org.apache.cloudstack.framework.messagebus.PublishScope;
 import org.apache.cloudstack.managed.context.ManagedContextRunnable;
+import org.apache.cloudstack.utils.mailing.MailAddress;
+import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
+import org.apache.cloudstack.utils.mailing.SMTPMailSender;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
@@ -72,11 +68,19 @@
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcManager;
 import com.cloud.projects.Project.State;
 import com.cloud.projects.ProjectAccount.Role;
 import com.cloud.projects.dao.ProjectAccountDao;
 import com.cloud.projects.dao.ProjectDao;
 import com.cloud.projects.dao.ProjectInvitationDao;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VolumeDao;
 import com.cloud.tags.dao.ResourceTagDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
@@ -95,14 +99,10 @@
 import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
 import com.cloud.utils.db.TransactionStatus;
 import com.cloud.utils.exception.CloudRuntimeException;
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.cloudstack.framework.config.ConfigKey;
-import org.apache.cloudstack.framework.config.Configurable;
-import org.apache.cloudstack.utils.mailing.MailAddress;
-import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
-import org.apache.cloudstack.utils.mailing.SMTPMailSender;
-import org.apache.commons.lang3.BooleanUtils;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 
 @Component
 public class ProjectManagerImpl extends ManagerBase implements ProjectManager, Configurable {
@@ -650,7 +650,7 @@
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_UPDATE, eventDescription = "updating project", async = true)
-    public Project updateProject(final long projectId, final String displayText, final String newOwnerName) throws ResourceAllocationException {
+    public Project updateProject(final long projectId, String name, final String displayText, final String newOwnerName) throws ResourceAllocationException {
         Account caller = CallContext.current().getCallingAccount();
 
         //check that the project exists
@@ -666,10 +666,7 @@
         Transaction.execute(new TransactionCallbackWithExceptionNoReturn<ResourceAllocationException>() {
             @Override
             public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException {
-                if (displayText != null) {
-                    project.setDisplayText(displayText);
-                    _projectDao.update(projectId, project);
-                }
+                updateProjectNameAndDisplayText(project, name, displayText);
 
                 if (newOwnerName != null) {
                     //check that the new owner exists
@@ -717,7 +714,7 @@
     @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_PROJECT_UPDATE, eventDescription = "updating project", async = true)
-    public Project updateProject(final long projectId, final String displayText, final String newOwnerName, Long userId,
+    public Project updateProject(final long projectId, String name, final String displayText, final String newOwnerName, Long userId,
                                  Role newRole) throws ResourceAllocationException {
         Account caller = CallContext.current().getCallingAccount();
 
@@ -737,10 +734,8 @@
         Transaction.execute(new TransactionCallbackWithExceptionNoReturn<ResourceAllocationException>() {
             @Override
             public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException {
-                if (displayText != null) {
-                    project.setDisplayText(displayText);
-                    _projectDao.update(projectId, project);
-                }
+                updateProjectNameAndDisplayText(project, name, displayText);
+
                 if (newOwnerName != null) {
                     //check that the new owner exists
                     Account updatedAcc = _accountMgr.getActiveAccountByName(newOwnerName, project.getDomainId());
@@ -1443,4 +1438,17 @@
     public ConfigKey<?>[] getConfigKeys() {
         return new ConfigKey<?>[] {ProjectSmtpEnabledSecurityProtocols, ProjectSmtpUseStartTLS};
     }
+
+    protected void updateProjectNameAndDisplayText(final ProjectVO project, String name, String displayText) {
+        if (name == null && displayText == null){
+            return;
+        }
+        if (name != null) {
+            project.setName(name);
+        }
+        if (displayText != null) {
+            project.setDisplayText(displayText);
+        }
+        _projectDao.update(project.getId(), project);
+    }
 }
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 1909063..150aed6 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -36,11 +36,13 @@
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.alert.AlertManager;
 import com.cloud.exception.StorageConflictException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.VolumeDao;
+import org.apache.cloudstack.alert.AlertService;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiConstants;
@@ -298,6 +300,10 @@
     @Inject
     private AnnotationDao annotationDao;
     @Inject
+    private AlertManager alertManager;
+    @Inject
+    private AnnotationService annotationService;
+    @Inject
     private VolumeDao volumeDao;
 
     private final long _nodeId = ManagementServerNode.getManagementServerId();
@@ -1801,73 +1807,149 @@
         return hostInMaintenance;
     }
 
+    private ResourceState.Event getResourceEventFromAllocationStateString(String allocationState) {
+        final ResourceState.Event resourceEvent = ResourceState.Event.toEvent(allocationState);
+        if (resourceEvent != ResourceState.Event.Enable && resourceEvent != ResourceState.Event.Disable) {
+            throw new InvalidParameterValueException(String.format("Invalid allocation state: %s, " +
+                    "only Enable/Disable are allowed", allocationState));
+        }
+        return resourceEvent;
+    }
+
+    private void handleAutoEnableDisableKVMHost(boolean autoEnableDisableKVMSetting,
+                                                boolean isUpdateFromHostHealthCheck,
+                                                HostVO host, DetailVO hostDetail,
+                                                ResourceState.Event resourceEvent) {
+        if (autoEnableDisableKVMSetting) {
+            if (!isUpdateFromHostHealthCheck && hostDetail != null &&
+                    !Boolean.parseBoolean(hostDetail.getValue()) && resourceEvent == ResourceState.Event.Enable) {
+                hostDetail.setValue(Boolean.TRUE.toString());
+                _hostDetailsDao.update(hostDetail.getId(), hostDetail);
+            } else if (!isUpdateFromHostHealthCheck && hostDetail != null &&
+                    Boolean.parseBoolean(hostDetail.getValue()) && resourceEvent == ResourceState.Event.Disable) {
+                s_logger.info(String.format("The setting %s is enabled but the host %s is manually set into %s state," +
+                                "ignoring future auto enabling of the host based on health check results",
+                        AgentManager.EnableKVMAutoEnableDisable.key(), host.getName(), resourceEvent));
+                hostDetail.setValue(Boolean.FALSE.toString());
+                _hostDetailsDao.update(hostDetail.getId(), hostDetail);
+            } else if (hostDetail == null) {
+                String autoEnableValue = !isUpdateFromHostHealthCheck ? Boolean.FALSE.toString() : Boolean.TRUE.toString();
+                hostDetail = new DetailVO(host.getId(), ApiConstants.AUTO_ENABLE_KVM_HOST, autoEnableValue);
+                _hostDetailsDao.persist(hostDetail);
+            }
+        }
+    }
+    private boolean updateHostAllocationState(HostVO host, String allocationState,
+                                           boolean isUpdateFromHostHealthCheck) throws NoTransitionException {
+        boolean autoEnableDisableKVMSetting = AgentManager.EnableKVMAutoEnableDisable.valueIn(host.getClusterId()) &&
+                host.getHypervisorType() == HypervisorType.KVM;
+        ResourceState.Event resourceEvent = getResourceEventFromAllocationStateString(allocationState);
+        DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), ApiConstants.AUTO_ENABLE_KVM_HOST);
+
+        if ((host.getResourceState() == ResourceState.Enabled && resourceEvent == ResourceState.Event.Enable) ||
+                (host.getResourceState() == ResourceState.Disabled && resourceEvent == ResourceState.Event.Disable)) {
+            s_logger.info(String.format("The host %s is already on the allocated state", host.getName()));
+            return false;
+        }
+
+        if (isAutoEnableAttemptForADisabledHost(autoEnableDisableKVMSetting, isUpdateFromHostHealthCheck, hostDetail, resourceEvent)) {
+            s_logger.debug(String.format("The setting '%s' is enabled and the health check succeeds on the host, " +
+                            "but the host has been manually disabled previously, ignoring auto enabling",
+                    AgentManager.EnableKVMAutoEnableDisable.key()));
+            return false;
+        }
+
+        handleAutoEnableDisableKVMHost(autoEnableDisableKVMSetting, isUpdateFromHostHealthCheck, host,
+                hostDetail, resourceEvent);
+
+        resourceStateTransitTo(host, resourceEvent, _nodeId);
+        return true;
+    }
+
+    private boolean isAutoEnableAttemptForADisabledHost(boolean autoEnableDisableKVMSetting,
+                                                        boolean isUpdateFromHostHealthCheck,
+                                                        DetailVO hostDetail, ResourceState.Event resourceEvent) {
+        return autoEnableDisableKVMSetting && isUpdateFromHostHealthCheck && hostDetail != null &&
+                !Boolean.parseBoolean(hostDetail.getValue()) && resourceEvent == ResourceState.Event.Enable;
+    }
+
+    private void updateHostName(HostVO host, String name) {
+        s_logger.debug("Updating Host name to: " + name);
+        host.setName(name);
+        _hostDao.update(host.getId(), host);
+    }
+
+    private void updateHostGuestOSCategory(Long hostId, Long guestOSCategoryId) {
+        // Verify that the guest OS Category exists
+        if (!(guestOSCategoryId > 0) || _guestOSCategoryDao.findById(guestOSCategoryId) == null) {
+            throw new InvalidParameterValueException("Please specify a valid guest OS category.");
+        }
+
+        final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId);
+        final DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
+
+        if (guestOSCategory != null && !GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) {
+            // Create/Update an entry for guest.os.category.id
+            if (guestOSDetail != null) {
+                guestOSDetail.setValue(String.valueOf(guestOSCategory.getId()));
+                _hostDetailsDao.update(guestOSDetail.getId(), guestOSDetail);
+            } else {
+                final Map<String, String> detail = new HashMap<String, String>();
+                detail.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
+                _hostDetailsDao.persist(hostId, detail);
+            }
+        } else {
+            // Delete any existing entry for guest.os.category.id
+            if (guestOSDetail != null) {
+                _hostDetailsDao.remove(guestOSDetail.getId());
+            }
+        }
+    }
+
+    private void updateHostTags(HostVO host, Long hostId, List<String> hostTags) {
+        List<VMInstanceVO> activeVMs =  _vmDao.listByHostId(hostId);
+        s_logger.warn(String.format("The following active VMs [%s] are using the host [%s]. " +
+                "Updating the host tags will not affect them.", activeVMs, host));
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Updating Host Tags to :" + hostTags);
+        }
+        _hostTagsDao.persist(hostId, new ArrayList<>(new HashSet<>(hostTags)));
+    }
+
     @Override
     public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException {
-        Long hostId = cmd.getId();
-        String name = cmd.getName();
-        Long guestOSCategoryId = cmd.getOsCategoryId();
+        return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(),
+                cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getAnnotation(), false);
+    }
 
+    private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String allocationState,
+                            String url, List<String> hostTags, String annotation, boolean isUpdateFromHostHealthCheck) throws NoTransitionException {
         // Verify that the host exists
         final HostVO host = _hostDao.findById(hostId);
         if (host == null) {
             throw new InvalidParameterValueException("Host with id " + hostId + " doesn't exist");
         }
 
-        if (cmd.getAllocationState() != null) {
-            final ResourceState.Event resourceEvent = ResourceState.Event.toEvent(cmd.getAllocationState());
-            if (resourceEvent != ResourceState.Event.Enable && resourceEvent != ResourceState.Event.Disable) {
-                throw new CloudRuntimeException("Invalid allocation state:" + cmd.getAllocationState() + ", only Enable/Disable are allowed");
-            }
-
-            resourceStateTransitTo(host, resourceEvent, _nodeId);
+        boolean isUpdateHostAllocation = false;
+        if (StringUtils.isNotBlank(allocationState)) {
+            isUpdateHostAllocation = updateHostAllocationState(host, allocationState, isUpdateFromHostHealthCheck);
         }
 
         if (StringUtils.isNotBlank(name)) {
-            s_logger.debug("Updating Host name to: " + name);
-            host.setName(name);
-            _hostDao.update(host.getId(), host);
+            updateHostName(host, name);
         }
 
         if (guestOSCategoryId != null) {
-            // Verify that the guest OS Category exists
-            if (!(guestOSCategoryId > 0) || _guestOSCategoryDao.findById(guestOSCategoryId) == null) {
-                throw new InvalidParameterValueException("Please specify a valid guest OS category.");
-            }
-
-            final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId);
-            final DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
-
-            if (guestOSCategory != null && !GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) {
-                // Create/Update an entry for guest.os.category.id
-                if (guestOSDetail != null) {
-                    guestOSDetail.setValue(String.valueOf(guestOSCategory.getId()));
-                    _hostDetailsDao.update(guestOSDetail.getId(), guestOSDetail);
-                } else {
-                    final Map<String, String> detail = new HashMap<String, String>();
-                    detail.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
-                    _hostDetailsDao.persist(hostId, detail);
-                }
-            } else {
-                // Delete any existing entry for guest.os.category.id
-                if (guestOSDetail != null) {
-                    _hostDetailsDao.remove(guestOSDetail.getId());
-                }
-            }
+            updateHostGuestOSCategory(hostId, guestOSCategoryId);
         }
-        final List<String> hostTags = cmd.getHostTags();
+
         if (hostTags != null) {
-            List<VMInstanceVO> activeVMs =  _vmDao.listByHostId(hostId);
-            s_logger.warn(String.format("The following active VMs [%s] are using the host [%s]. Updating the host tags will not affect them.", activeVMs, host));
-
-            if (s_logger.isDebugEnabled()) {
-                s_logger.debug("Updating Host Tags to :" + hostTags);
-            }
-            _hostTagsDao.persist(hostId, new ArrayList(new HashSet<String>(hostTags)));
+            updateHostTags(host, hostId, hostTags);
         }
 
-        final String url = cmd.getUrl();
         if (url != null) {
-            _storageMgr.updateSecondaryStorage(cmd.getId(), cmd.getUrl());
+            _storageMgr.updateSecondaryStorage(hostId, url);
         }
         try {
             _storageMgr.enableHost(hostId);
@@ -1876,9 +1958,55 @@
         }
 
         final HostVO updatedHost = _hostDao.findById(hostId);
+
+        sendAlertAndAnnotationForAutoEnableDisableKVMHostFeature(host, allocationState,
+                isUpdateFromHostHealthCheck, isUpdateHostAllocation, annotation);
+
         return updatedHost;
     }
 
+    private void sendAlertAndAnnotationForAutoEnableDisableKVMHostFeature(HostVO host, String allocationState,
+                                                                          boolean isUpdateFromHostHealthCheck,
+                                                                          boolean isUpdateHostAllocation, String annotation) {
+        boolean isAutoEnableDisableKVMSettingEnabled = host.getHypervisorType() == HypervisorType.KVM &&
+                AgentManager.EnableKVMAutoEnableDisable.valueIn(host.getClusterId());
+        if (!isAutoEnableDisableKVMSettingEnabled) {
+            if (StringUtils.isNotBlank(annotation)) {
+                annotationService.addAnnotation(annotation, AnnotationService.EntityType.HOST, host.getUuid(), true);
+            }
+            return;
+        }
+
+        if (!isUpdateHostAllocation) {
+            return;
+        }
+
+        String msg = String.format("The host %s (%s) ", host.getName(), host.getUuid());
+        ResourceState.Event resourceEvent = getResourceEventFromAllocationStateString(allocationState);
+        boolean isEventEnable = resourceEvent == ResourceState.Event.Enable;
+
+        if (isUpdateFromHostHealthCheck) {
+            msg += String.format("is auto-%s after %s health check results",
+                    isEventEnable ? "enabled" : "disabled",
+                    isEventEnable ? "successful" : "failed");
+            alertManager.sendAlert(AlertService.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(),
+                    host.getPodId(), msg, msg);
+        } else {
+            msg += String.format("is %s despite the setting '%s' is enabled for the cluster %s",
+                    isEventEnable ? "enabled" : "disabled", AgentManager.EnableKVMAutoEnableDisable.key(),
+                    host.getClusterId());
+            if (StringUtils.isNotBlank(annotation)) {
+                msg += String.format(", reason: %s", annotation);
+            }
+        }
+        annotationService.addAnnotation(msg, AnnotationService.EntityType.HOST, host.getUuid(), true);
+    }
+
+    @Override
+    public Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException {
+        return updateHost(hostId, null, null, resourceEvent.toString(), null, null, null, true);
+    }
+
     @Override
     public Cluster getCluster(final Long clusterId) {
         return _clusterDao.findById(clusterId);
diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
index 288315d..51b1635 100644
--- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
+++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
@@ -1169,12 +1169,28 @@
         @Override
         protected void runInContext() {
             s_logger.info("Started resource counters recalculation periodic task.");
-            List<DomainVO> domains = _domainDao.findImmediateChildrenForParent(Domain.ROOT_DOMAIN);
-            List<AccountVO> accounts = _accountDao.findActiveAccountsForDomain(Domain.ROOT_DOMAIN);
+            List<DomainVO> domains;
+            List<AccountVO> accounts;
+            // try/catch task, otherwise it won't be rescheduled in case of exception
+            try {
+                domains = _domainDao.findImmediateChildrenForParent(Domain.ROOT_DOMAIN);
+            } catch (Exception e) {
+                s_logger.warn("Resource counters recalculation periodic task failed, unable to fetch immediate children for the domain " + Domain.ROOT_DOMAIN, e);
+                // initialize domains as empty list to do best effort recalculation
+                domains = new ArrayList<>();
+            }
+            // try/catch task, otherwise it won't be rescheduled in case of exception
+            try {
+                accounts = _accountDao.findActiveAccountsForDomain(Domain.ROOT_DOMAIN);
+            } catch (Exception e) {
+                s_logger.warn("Resource counters recalculation periodic task failed, unable to fetch active accounts for domain " + Domain.ROOT_DOMAIN, e);
+                // initialize accounts as empty list to do best effort recalculation
+                accounts = new ArrayList<>();
+            }
 
             for (ResourceType type : ResourceType.values()) {
                 if (type.supportsOwner(ResourceOwnerType.Domain)) {
-                    recalculateDomainResourceCount(Domain.ROOT_DOMAIN, type);
+                    recalculateDomainResourceCountInContext(Domain.ROOT_DOMAIN, type);
                     for (Domain domain : domains) {
                         recalculateDomainResourceCount(domain.getId(), type);
                     }
@@ -1183,10 +1199,25 @@
                 if (type.supportsOwner(ResourceOwnerType.Account)) {
                     // run through the accounts in the root domain
                     for (AccountVO account : accounts) {
-                        recalculateAccountResourceCount(account.getId(), type);
+                        recalculateAccountResourceCountInContext(account.getId(), type);
                     }
                 }
             }
         }
+
+        private void recalculateDomainResourceCountInContext(long domainId, ResourceType type) {
+            try {
+                recalculateDomainResourceCount(domainId, type);
+            } catch (Exception e) {
+                s_logger.warn("Resource counters recalculation periodic task failed for the domain " + domainId + " and the resource type " + type + " .", e);
+            }
+        }
+        private void recalculateAccountResourceCountInContext(long accountId, ResourceType type) {
+            try {
+                recalculateAccountResourceCount(accountId, type);
+            } catch (Exception e) {
+                s_logger.warn("Resource counters recalculation periodic task failed for the account " + accountId + " and the resource type " + type + " .", e);
+            }
+        }
     }
 }
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 85e86b5..c16dc4e 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -16,12 +16,7 @@
 // under the License.
 package com.cloud.server;
 
-import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH;
-import static com.cloud.vm.UserVmManager.MAX_USER_DATA_LENGTH_BYTES;
-
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Field;
-import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -56,7 +51,6 @@
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
 import org.apache.cloudstack.api.command.admin.account.DeleteAccountCmd;
 import org.apache.cloudstack.api.command.admin.account.DisableAccountCmd;
@@ -95,6 +89,7 @@
 import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
 import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
 import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd;
+import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd;
 import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd;
 import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd;
 import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd;
@@ -610,6 +605,7 @@
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
+import org.apache.cloudstack.userdata.UserDataManager;
 import org.apache.cloudstack.utils.CloudStackVersion;
 import org.apache.cloudstack.utils.identity.ManagementServerNode;
 import org.apache.commons.codec.binary.Base64;
@@ -619,7 +615,11 @@
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CheckGuestOsMappingAnswer;
+import com.cloud.agent.api.CheckGuestOsMappingCommand;
 import com.cloud.agent.api.Command;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
+import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
 import com.cloud.agent.api.GetVncPortAnswer;
 import com.cloud.agent.api.GetVncPortCommand;
 import com.cloud.agent.api.PatchSystemVmAnswer;
@@ -691,7 +691,6 @@
 import com.cloud.host.dao.HostDao;
 import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.host.dao.HostTagsDao;
-import com.cloud.hypervisor.Hypervisor;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorCapabilities;
 import com.cloud.hypervisor.HypervisorCapabilitiesVO;
@@ -774,6 +773,7 @@
 import com.cloud.utils.crypt.DBEncryptionUtil;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GenericSearchBuilder;
 import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.db.JoinBuilder;
 import com.cloud.utils.db.JoinBuilder.JoinType;
@@ -821,10 +821,6 @@
     static final ConfigKey<Boolean> humanReadableSizes = new ConfigKey<Boolean>("Advanced", Boolean.class, "display.human.readable.sizes", "true", "Enables outputting human readable byte sizes to logs and usage records.", false, ConfigKey.Scope.Global);
     public static final ConfigKey<String> customCsIdentifier = new ConfigKey<String>("Advanced", String.class, "custom.cs.identifier", UUID.randomUUID().toString().split("-")[0].substring(4), "Custom identifier for the cloudstack installation", true, ConfigKey.Scope.Global);
     private static final VirtualMachine.Type []systemVmTypes = { VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.ConsoleProxy};
-
-    private static final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES;
-    private static final int NUM_OF_2K_BLOCKS = 512;
-    private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES;
     private static final List<HypervisorType> LIVE_MIGRATION_SUPPORTING_HYPERVISORS = List.of(HypervisorType.Hyperv, HypervisorType.KVM,
             HypervisorType.LXC, HypervisorType.Ovm, HypervisorType.Ovm3, HypervisorType.Simulator, HypervisorType.VMware, HypervisorType.XenServer);
 
@@ -976,6 +972,8 @@
     protected VMTemplateDao templateDao;
     @Inject
     protected AnnotationDao annotationDao;
+    @Inject
+    UserDataManager userDataManager;
 
     private LockControllerListener _lockControllerListener;
     private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -993,7 +991,7 @@
 
     protected List<DeploymentPlanner> _planners;
 
-    private final List<HypervisorType> supportedHypervisors = new ArrayList<Hypervisor.HypervisorType>();
+    private final List<HypervisorType> supportedHypervisors = new ArrayList<HypervisorType>();
 
     public List<DeploymentPlanner> getPlanners() {
         return _planners;
@@ -2635,6 +2633,17 @@
 
         if (id != null) {
             sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        } else {
+            GenericSearchBuilder<GuestOSVO, Long> sb = _guestOSDao.createSearchBuilder(Long.class);
+            sb.select(null, SearchCriteria.Func.MAX, sb.entity().getId());
+            sb.groupBy(sb.entity().getCategoryId(), sb.entity().getDisplayName());
+            sb.done();
+
+            final SearchCriteria<Long> scGuestOs = sb.create();
+            final List<Long> guestOSVOList = _guestOSDao.customSearch(scGuestOs, null);
+            if (CollectionUtils.isNotEmpty(guestOSVOList)) {
+                sc.addAnd("id", SearchCriteria.Op.IN, guestOSVOList.toArray());
+            }
         }
 
         if (osCategoryId != null) {
@@ -2680,9 +2689,15 @@
 
     @Override
     public Pair<List<? extends GuestOSHypervisor>, Integer> listGuestOSMappingByCriteria(final ListGuestOsMappingCmd cmd) {
+        final String guestOsId = "guestOsId";
         final Filter searchFilter = new Filter(GuestOSHypervisorVO.class, "hypervisorType", true, cmd.getStartIndex(), cmd.getPageSizeVal());
+        searchFilter.addOrderBy(GuestOSHypervisorVO.class, "hypervisorVersion", false);
+        searchFilter.addOrderBy(GuestOSHypervisorVO.class, guestOsId, true);
+        searchFilter.addOrderBy(GuestOSHypervisorVO.class, "created", false);
         final Long id = cmd.getId();
         final Long osTypeId = cmd.getOsTypeId();
+        final String osDisplayName = cmd.getOsDisplayName();
+        final String osNameForHypervisor = cmd.getOsNameForHypervisor();
         final String hypervisor = cmd.getHypervisor();
         final String hypervisorVersion = cmd.getHypervisorVersion();
 
@@ -2698,15 +2713,27 @@
         }
 
         if (osTypeId != null) {
-            sc.addAnd("guestOsId", SearchCriteria.Op.EQ, osTypeId);
+            sc.addAnd(guestOsId, SearchCriteria.Op.EQ, osTypeId);
+        }
+
+        if (osNameForHypervisor != null) {
+            sc.addAnd("guestOsName", SearchCriteria.Op.LIKE, "%" + osNameForHypervisor + "%");
         }
 
         if (hypervisor != null) {
-            sc.addAnd("hypervisorType", SearchCriteria.Op.EQ, hypervisor);
+            sc.addAnd("hypervisorType", SearchCriteria.Op.LIKE, "%" + hypervisor + "%");
         }
 
         if (hypervisorVersion != null) {
-            sc.addAnd("hypervisorVersion", SearchCriteria.Op.EQ, hypervisorVersion);
+            sc.addAnd("hypervisorVersion", SearchCriteria.Op.LIKE, "%" + hypervisorVersion + "%");
+        }
+
+        if (osDisplayName != null) {
+            List<GuestOSVO> guestOSVOS = _guestOSDao.listLikeDisplayName(osDisplayName);
+            if (CollectionUtils.isNotEmpty(guestOSVOS)) {
+                List<Long> guestOSids = guestOSVOS.stream().map(mo -> mo.getId()).collect(Collectors.toList());
+                sc.addAnd(guestOsId, SearchCriteria.Op.IN, guestOSids.toArray());
+            }
         }
 
         final Pair<List<GuestOSHypervisorVO>, Integer> result = _guestOSHypervisorDao.searchAndCount(sc, searchFilter);
@@ -2724,7 +2751,7 @@
         final String osNameForHypervisor = cmd.getOsNameForHypervisor();
         GuestOS guestOs = null;
 
-        if (osTypeId == null && (osStdName == null || osStdName.isEmpty())) {
+        if (osTypeId == null && StringUtils.isEmpty(osStdName)) {
             throw new InvalidParameterValueException("Please specify either a guest OS name or UUID");
         }
 
@@ -2753,9 +2780,28 @@
         final GuestOSHypervisorVO duplicate = _guestOSHypervisorDao.findByOsIdAndHypervisorAndUserDefined(guestOs.getId(), hypervisorType.toString(), hypervisorVersion, true);
 
         if (duplicate != null) {
-            throw new InvalidParameterValueException(
-                    "Mapping from hypervisor : " + hypervisorType.toString() + ", version : " + hypervisorVersion + " and guest OS : " + guestOs.getDisplayName() + " already exists!");
+            if (!cmd.isForced()) {
+                throw new InvalidParameterValueException(
+                        "Mapping from hypervisor : " + hypervisorType.toString() + ", version : " + hypervisorVersion + " and guest OS : " + guestOs.getDisplayName() + " already exists!");
+            }
+
+            if (Boolean.TRUE.equals(cmd.getOsMappingCheckEnabled())) {
+                checkGuestOSHypervisorMapping(hypervisorType, hypervisorVersion, guestOs.getDisplayName(), osNameForHypervisor);
+            }
+
+            final long guestOsId = duplicate.getId();
+            final GuestOSHypervisorVO guestOsHypervisor = _guestOSHypervisorDao.createForUpdate(guestOsId);
+            guestOsHypervisor.setGuestOsName(osNameForHypervisor);
+            if (_guestOSHypervisorDao.update(guestOsId, guestOsHypervisor)) {
+                return _guestOSHypervisorDao.findById(guestOsId);
+            }
+            return null;
         }
+
+        if (Boolean.TRUE.equals(cmd.getOsMappingCheckEnabled())) {
+            checkGuestOSHypervisorMapping(hypervisorType, hypervisorVersion, guestOs.getDisplayName(), osNameForHypervisor);
+        }
+
         final GuestOSHypervisorVO guestOsMapping = new GuestOSHypervisorVO();
         guestOsMapping.setGuestOsId(guestOs.getId());
         guestOsMapping.setGuestOsName(osNameForHypervisor);
@@ -2763,7 +2809,24 @@
         guestOsMapping.setHypervisorVersion(hypervisorVersion);
         guestOsMapping.setIsUserDefined(true);
         return _guestOSHypervisorDao.persist(guestOsMapping);
+    }
 
+    private void checkGuestOSHypervisorMapping(HypervisorType hypervisorType, String hypervisorVersion, String guestOsName, String guestOsNameForHypervisor) {
+        if (!canCheckGuestOsNameInHypervisor(hypervisorType)) {
+            throw new InvalidParameterValueException(String.format("Guest OS mapping check is not supported for hypervisor: %s, please specify a valid hypervisor : VMware, XenServer", hypervisorType.toString()));
+        }
+        final HostVO host = _hostDao.findHostByHypervisorTypeAndVersion(hypervisorType, hypervisorVersion);
+        if (host == null) {
+            throw new CloudRuntimeException(String.format("No %s hypervisor with version: %s exists, please specify available hypervisor and version", hypervisorType.toString(), hypervisorVersion));
+        }
+        CheckGuestOsMappingAnswer answer = (CheckGuestOsMappingAnswer) _agentMgr.easySend(host.getId(), new CheckGuestOsMappingCommand(guestOsName, guestOsNameForHypervisor, hypervisorVersion));
+        if (answer == null || !answer.getResult()) {
+            throw new CloudRuntimeException(String.format("Invalid hypervisor os mapping: %s for guest os: %s, hypervisor: %s and version: %s", guestOsNameForHypervisor, guestOsName, hypervisorType.toString(), hypervisorVersion));
+        }
+    }
+
+    private boolean canCheckGuestOsNameInHypervisor(HypervisorType hypervisorType) {
+        return (hypervisorType == HypervisorType.VMware || hypervisorType == HypervisorType.XenServer);
     }
 
     @Override
@@ -2773,6 +2836,25 @@
     }
 
     @Override
+    @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_HYPERVISOR_NAME_FETCH, eventDescription = "Getting guest OS names from hypervisor", async = true)
+    public List<Pair<String, String>> getHypervisorGuestOsNames(GetHypervisorGuestOsNamesCmd getHypervisorGuestOsNamesCmd) {
+        final HypervisorType hypervisorType = HypervisorType.getType(getHypervisorGuestOsNamesCmd.getHypervisor());
+        if (!canCheckGuestOsNameInHypervisor(hypervisorType)) {
+            throw new InvalidParameterValueException(String.format("Guest OS names cannot be fetched for hypervisor: %s, please specify a valid hypervisor : VMware, XenServer", hypervisorType.toString()));
+        }
+
+        final HostVO host = _hostDao.findHostByHypervisorTypeAndVersion(hypervisorType, getHypervisorGuestOsNamesCmd.getHypervisorVersion());
+        if (host == null) {
+            throw new CloudRuntimeException(String.format("No %s hypervisor with version: %s exists, please specify available hypervisor and version", hypervisorType.toString(), getHypervisorGuestOsNamesCmd.getHypervisorVersion()));
+        }
+        GetHypervisorGuestOsNamesAnswer answer = (GetHypervisorGuestOsNamesAnswer) _agentMgr.easySend(host.getId(), new GetHypervisorGuestOsNamesCommand(getHypervisorGuestOsNamesCmd.getKeyword()));
+        if (answer == null || !answer.getResult()) {
+            throw new CloudRuntimeException(String.format("Unable to get guest os names for hypervisor: %s, version: %s", hypervisorType.toString(), getHypervisorGuestOsNamesCmd.getHypervisorVersion()));
+        }
+        return answer.getHypervisorGuestOsNames();
+    }
+
+    @Override
     @DB
     @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_ADD, eventDescription = "Adding new guest OS type", create = true)
     public GuestOS addGuestOs(final AddGuestOsCmd cmd) {
@@ -2893,6 +2975,14 @@
             throw new InvalidParameterValueException("Unable to modify system defined Guest OS mapping");
         }
 
+        if (Boolean.TRUE.equals(cmd.getOsMappingCheckEnabled())) {
+            GuestOS guestOs = ApiDBUtils.findGuestOSById(guestOsHypervisorHandle.getGuestOsId());
+            if (guestOs == null) {
+                throw new InvalidParameterValueException("Unable to find the guest OS for the mapping");
+            }
+            checkGuestOSHypervisorMapping(HypervisorType.getType(guestOsHypervisorHandle.getHypervisorType()), guestOsHypervisorHandle.getHypervisorVersion(), guestOs.getDisplayName(), osNameForHypervisor);
+        }
+
         final GuestOSHypervisorVO guestOsHypervisor = _guestOSHypervisorDao.createForUpdate(id);
         guestOsHypervisor.setGuestOsName(osNameForHypervisor);
         if (_guestOSHypervisorDao.update(id, guestOsHypervisor)) {
@@ -3469,6 +3559,7 @@
         cmdList.add(UpdateGuestOsMappingCmd.class);
         cmdList.add(RemoveGuestOsCmd.class);
         cmdList.add(RemoveGuestOsMappingCmd.class);
+        cmdList.add(GetHypervisorGuestOsNamesCmd.class);
         cmdList.add(AttachIsoCmd.class);
         cmdList.add(CopyIsoCmd.class);
         cmdList.add(DeleteIsoCmd.class);
@@ -4612,58 +4703,11 @@
         String userdata = cmd.getUserData();
         final String params = cmd.getParams();
 
-        userdata = validateUserData(userdata, cmd.getHttpMethod());
+        userdata = userDataManager.validateUserData(userdata, cmd.getHttpMethod());
 
         return createAndSaveUserData(name, userdata, params, owner);
     }
 
-    private String validateUserData(String userData, BaseCmd.HTTPMethod httpmethod) {
-        byte[] decodedUserData = null;
-        if (userData != null) {
-
-            if (userData.contains("%")) {
-                try {
-                    userData = URLDecoder.decode(userData, "UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    throw new InvalidParameterValueException("Url decoding of userdata failed.");
-                }
-            }
-
-            if (!Base64.isBase64(userData)) {
-                throw new InvalidParameterValueException("User data is not base64 encoded");
-            }
-            // If GET, use 4K. If POST, support up to 1M.
-            if (httpmethod.equals(BaseCmd.HTTPMethod.GET)) {
-                decodedUserData = validateAndDecodeByHTTPmethod(userData, MAX_HTTP_GET_LENGTH, BaseCmd.HTTPMethod.GET);
-            } else if (httpmethod.equals(BaseCmd.HTTPMethod.POST)) {
-                decodedUserData = validateAndDecodeByHTTPmethod(userData, MAX_HTTP_POST_LENGTH, BaseCmd.HTTPMethod.POST);
-            }
-
-            if (decodedUserData == null || decodedUserData.length < 1) {
-                throw new InvalidParameterValueException("User data is too short");
-            }
-            // Re-encode so that the '=' paddings are added if necessary since 'isBase64' does not require it, but python does on the VR.
-            return Base64.encodeBase64String(decodedUserData);
-        }
-        return null;
-    }
-
-    private byte[] validateAndDecodeByHTTPmethod(String userData, int maxHTTPlength, BaseCmd.HTTPMethod httpMethod) {
-        byte[] decodedUserData = null;
-
-        if (userData.length() >= maxHTTPlength) {
-            throw new InvalidParameterValueException(String.format("User data is too long for an http %s request", httpMethod.toString()));
-        }
-        if (userData.length() > VM_USERDATA_MAX_LENGTH.value()) {
-            throw new InvalidParameterValueException("User data has exceeded configurable max length : " + VM_USERDATA_MAX_LENGTH.value());
-        }
-        decodedUserData = Base64.decodeBase64(userData.getBytes());
-        if (decodedUserData.length > maxHTTPlength) {
-            throw new InvalidParameterValueException(String.format("User data is too long for http %s request", httpMethod.toString()));
-        }
-        return decodedUserData;
-    }
-
     /**
      * @param cmd
      * @param owner
diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 7bafbcc..5b1ca90 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -249,8 +249,6 @@
     @Inject
     protected ConfigurationManager _configMgr;
     @Inject
-    protected VolumeDao _volsDao;
-    @Inject
     private VolumeDataStoreDao _volumeDataStoreDao;
     @Inject
     protected HostDao _hostDao;
@@ -299,7 +297,7 @@
     @Inject
     protected HypervisorGuruManager _hvGuruMgr;
     @Inject
-    protected VolumeDao _volumeDao;
+    protected VolumeDao volumeDao;
     @Inject
     ConfigurationDao _configDao;
     @Inject
@@ -372,7 +370,7 @@
     public boolean share(VMInstanceVO vm, List<VolumeVO> vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException {
 
         // if pool is in maintenance and it is the ONLY pool available; reject
-        List<VolumeVO> rootVolForGivenVm = _volsDao.findByInstanceAndType(vm.getId(), Type.ROOT);
+        List<VolumeVO> rootVolForGivenVm = volumeDao.findByInstanceAndType(vm.getId(), Type.ROOT);
         if (rootVolForGivenVm != null && rootVolForGivenVm.size() > 0) {
             boolean isPoolAvailable = isPoolAvailable(rootVolForGivenVm.get(0).getPoolId());
             if (!isPoolAvailable) {
@@ -436,7 +434,7 @@
         for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) {
             StoragePoolVO PrimaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
             if (PrimaryDataStoreVO.getPoolType() == StoragePoolType.LVM || PrimaryDataStoreVO.getPoolType() == StoragePoolType.EXT) {
-                SearchBuilder<VolumeVO> volumeSB = _volsDao.createSearchBuilder();
+                SearchBuilder<VolumeVO> volumeSB = volumeDao.createSearchBuilder();
                 volumeSB.and("poolId", volumeSB.entity().getPoolId(), SearchCriteria.Op.EQ);
                 volumeSB.and("removed", volumeSB.entity().getRemoved(), SearchCriteria.Op.NULL);
                 volumeSB.and("state", volumeSB.entity().getState(), SearchCriteria.Op.NIN);
@@ -450,7 +448,7 @@
                 volumeSC.setParameters("state", Volume.State.Expunging, Volume.State.Destroy);
                 volumeSC.setJoinParameters("activeVmSB", "state", State.Starting, State.Running, State.Stopping, State.Migrating);
 
-                List<VolumeVO> volumes = _volsDao.search(volumeSC, null);
+                List<VolumeVO> volumes = volumeDao.search(volumeSC, null);
                 if (volumes.size() > 0) {
                     return true;
                 }
@@ -598,7 +596,7 @@
 
         StoragePoolSearch = _vmInstanceDao.createSearchBuilder();
 
-        SearchBuilder<VolumeVO> volumeSearch = _volumeDao.createSearchBuilder();
+        SearchBuilder<VolumeVO> volumeSearch = volumeDao.createSearchBuilder();
         volumeSearch.and("volumeType", volumeSearch.entity().getVolumeType(), SearchCriteria.Op.EQ);
         volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), SearchCriteria.Op.EQ);
         volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ);
@@ -1035,10 +1033,10 @@
         List<StoragePoolVO> childStoragePools = _storagePoolDao.listChildStoragePoolsInDatastoreCluster(sPool.getId());
         boolean canDelete = true;
         for (StoragePoolVO childPool : childStoragePools) {
-            Pair<Long, Long> vlms = _volsDao.getCountAndTotalByPool(childPool.getId());
+            Pair<Long, Long> vlms = volumeDao.getCountAndTotalByPool(childPool.getId());
             if (forced) {
                 if (vlms.first() > 0) {
-                    Pair<Long, Long> nonDstrdVlms = _volsDao.getNonDestroyedCountAndTotalByPool(childPool.getId());
+                    Pair<Long, Long> nonDstrdVlms = volumeDao.getNonDestroyedCountAndTotalByPool(childPool.getId());
                     if (nonDstrdVlms.first() > 0) {
                         canDelete = false;
                         break;
@@ -1055,15 +1053,15 @@
     }
 
     private boolean deleteDataStoreInternal(StoragePoolVO sPool, boolean forced) {
-        Pair<Long, Long> vlms = _volsDao.getCountAndTotalByPool(sPool.getId());
+        Pair<Long, Long> vlms = volumeDao.getCountAndTotalByPool(sPool.getId());
         if (forced) {
             if (vlms.first() > 0) {
-                Pair<Long, Long> nonDstrdVlms = _volsDao.getNonDestroyedCountAndTotalByPool(sPool.getId());
+                Pair<Long, Long> nonDstrdVlms = volumeDao.getNonDestroyedCountAndTotalByPool(sPool.getId());
                 if (nonDstrdVlms.first() > 0) {
                     throw new CloudRuntimeException("Cannot delete pool " + sPool.getName() + " as there are associated " + "non-destroyed vols for this pool");
                 }
                 // force expunge non-destroyed volumes
-                List<VolumeVO> vols = _volsDao.listVolumesToBeDestroyed();
+                List<VolumeVO> vols = volumeDao.listVolumesToBeDestroyed();
                 for (VolumeVO vol : vols) {
                     AsyncCallFuture<VolumeApiResult> future = volService.expungeVolumeAsync(volFactory.getVolume(vol.getId()));
                     try {
@@ -1326,7 +1324,7 @@
                     }
                     cleanupSecondaryStorage(recurring);
 
-                    List<VolumeVO> vols = _volsDao.listVolumesToBeDestroyed(new Date(System.currentTimeMillis() - ((long)StorageCleanupDelay.value() << 10)));
+                    List<VolumeVO> vols = volumeDao.listVolumesToBeDestroyed(new Date(System.currentTimeMillis() - ((long)StorageCleanupDelay.value() << 10)));
                     for (VolumeVO vol : vols) {
                         if (Type.ROOT.equals(vol.getVolumeType())) {
                              VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vol.getInstanceId());
@@ -1378,7 +1376,7 @@
                     // destroy uploaded volumes in abandoned/error state
                     List<VolumeDataStoreVO> volumeDataStores = _volumeDataStoreDao.listByVolumeState(Volume.State.UploadError, Volume.State.UploadAbandoned);
                     for (VolumeDataStoreVO volumeDataStore : volumeDataStores) {
-                        VolumeVO volume = _volumeDao.findById(volumeDataStore.getVolumeId());
+                        VolumeVO volume = volumeDao.findById(volumeDataStore.getVolumeId());
                         if (volume == null) {
                             s_logger.warn("Uploaded volume with id " + volumeDataStore.getVolumeId() + " not found, so cannot be destroyed");
                             continue;
@@ -1486,7 +1484,7 @@
         if (vm == null) {
             return false;
         }
-        List<VolumeVO> vmUsableVolumes = _volumeDao.findUsableVolumesForInstance(vmId);
+        List<VolumeVO> vmUsableVolumes = volumeDao.findUsableVolumesForInstance(vmId);
         for (VolumeVO vol : vmUsableVolumes) {
             if (gcVolume.getPoolId().equals(vol.getPoolId()) && gcVolume.getPath().equals(vol.getPath())) {
                 s_logger.debug(String.format("%s meant for garbage collection could a possible duplicate for %s", gcVolume, vol));
@@ -1976,7 +1974,7 @@
 
         for (String childDatastoreUUID : childDatastoreUUIDs) {
             StoragePoolVO dataStoreVO = _storagePoolDao.findPoolByUUID(childDatastoreUUID);
-            List<VolumeVO> allVolumes = _volumeDao.findByPoolId(dataStoreVO.getId());
+            List<VolumeVO> allVolumes = volumeDao.findByPoolId(dataStoreVO.getId());
             allVolumes.removeIf(volumeVO -> volumeVO.getInstanceId() == null);
             allVolumes.removeIf(volumeVO -> volumeVO.getState() != Volume.State.Ready);
             for (VolumeVO volume : allVolumes) {
@@ -2012,7 +2010,7 @@
                         volume.getUuid() + "Host=" + hostId;
 
                 // check for the changed details of volume and update database
-                VolumeVO volumeVO = _volumeDao.findById(volumeId);
+                VolumeVO volumeVO = volumeDao.findById(volumeId);
                 String datastoreName = answer.getContextParam("datastoreName");
                 if (datastoreName != null) {
                     StoragePoolVO storagePoolVO = _storagePoolDao.findByUuid(datastoreName);
@@ -2033,7 +2031,7 @@
                     volumeVO.setChainInfo(chainInfo);
                 }
 
-                _volumeDao.update(volumeVO.getId(), volumeVO);
+                volumeDao.update(volumeVO.getId(), volumeVO);
             }
             dataStoreVO.setParent(0L);
             _storagePoolDao.update(dataStoreVO.getId(), dataStoreVO);
@@ -2454,14 +2452,14 @@
             // might be clearer that this "volume" in "volumeDiskProfilesList" still might have an old value for hv_ss_reverse.
             Volume volume = volumeDiskProfilePair.first();
             DiskProfile diskProfile = volumeDiskProfilePair.second();
-            VolumeVO volumeVO = _volumeDao.findById(volume.getId());
+            VolumeVO volumeVO = volumeDao.findById(volume.getId());
 
             if (volumeVO.getHypervisorSnapshotReserve() == null) {
                 // update the volume's hv_ss_reserve (hypervisor snapshot reserve) from a disk offering (used for managed storage)
                 volService.updateHypervisorSnapshotReserveForVolume(getDiskOfferingVO(volumeVO), volumeVO.getId(), getHypervisorType(volumeVO));
 
                 // hv_ss_reserve field might have been updated; refresh from DB to make use of it in getDataObjectSizeIncludingHypervisorSnapshotReserve
-                volumeVO = _volumeDao.findById(volume.getId());
+                volumeVO = volumeDao.findById(volume.getId());
             }
 
             // this if statement should resolve to true at most once per execution of the for loop its contained within (for a root disk that is
@@ -3299,9 +3297,9 @@
             if (activeVolumeIds.contains(volumeId)) {
                 continue;
             }
-            Volume volume = _volumeDao.findById(volumeId);
+            Volume volume = volumeDao.findById(volumeId);
             if (volume != null && volume.getState() == Volume.State.Expunged) {
-                _volumeDao.remove(volumeId);
+                volumeDao.remove(volumeId);
             }
         }
 
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index e39fac1..bc9a421 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1682,7 +1682,7 @@
         }
     }
 
-    protected boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException {
+    public boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException {
         return _volStateMachine.transitTo(vol, event, null, _volsDao);
     }
 
diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java
index 39f1e5d..6dd9c27 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManager.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManager.java
@@ -58,8 +58,6 @@
             "Destroys the VM's root volume when the VM is destroyed.",
             true, ConfigKey.Scope.Domain);
 
-    static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
-
     public  static  final String CKS_NODE = "cksnode";
 
     /**
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 7ec3450..159c230 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -16,7 +16,6 @@
 // under the License.
 package com.cloud.vm;
 
-import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH;
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
 
 import java.io.IOException;
@@ -123,9 +122,10 @@
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
+import org.apache.cloudstack.userdata.UserDataManager;
 import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
 import org.apache.cloudstack.utils.security.ParserUtils;
-import org.apache.commons.codec.binary.Base64;
+import org.apache.cloudstack.vm.schedule.VMScheduleManager;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.math.NumberUtils;
@@ -582,6 +582,9 @@
     @Inject
     private AutoScaleManager autoScaleManager;
 
+    @Inject
+    VMScheduleManager vmScheduleManager;
+
     private ScheduledExecutorService _executor = null;
     private ScheduledExecutorService _vmIpFetchExecutor = null;
     private int _expungeInterval;
@@ -598,10 +601,6 @@
 
     protected static long ROOT_DEVICE_ID = 0;
 
-    private static final int MAX_HTTP_GET_LENGTH = 2 * MAX_USER_DATA_LENGTH_BYTES;
-    private static final int NUM_OF_2K_BLOCKS = 512;
-    private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES;
-
     @Inject
     private OrchestrationService _orchSrvc;
 
@@ -611,6 +610,9 @@
     @Inject
     private ManagementService _mgr;
 
+    @Inject
+    private UserDataManager userDataManager;
+
     private static final ConfigKey<Integer> VmIpFetchWaitInterval = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180",
             "Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true);
 
@@ -939,7 +941,7 @@
             userDataDetails = cmd.getUserdataDetails().toString();
         }
         userData = finalizeUserData(userData, userDataId, template);
-        userData = validateUserData(userData, cmd.getHttpMethod());
+        userData = userDataManager.validateUserData(userData, cmd.getHttpMethod());
 
         userVm.setUserDataId(userDataId);
         userVm.setUserData(userData);
@@ -2942,7 +2944,7 @@
         if (userData != null) {
             // check and replace newlines
             userData = userData.replace("\\n", "");
-            userData = validateUserData(userData, httpMethod);
+            userData = userDataManager.validateUserData(userData, httpMethod);
             // update userData on domain router.
             updateUserdata = true;
         } else {
@@ -3272,6 +3274,8 @@
 
         autoScaleManager.removeVmFromVmGroup(vmId);
 
+        vmScheduleManager.removeScheduleByVmId(vmId, expunge);
+
         deleteVolumesFromVm(volumesToBeDeleted, expunge);
 
         if (getDestroyRootVolumeOnVmDestruction(vm.getDomainId())) {
@@ -4063,7 +4067,7 @@
             _accountMgr.checkAccess(owner, AccessType.UseEntry, false, template);
 
             // check if the user data is correct
-            userData = validateUserData(userData, httpmethod);
+            userData = userDataManager.validateUserData(userData, httpmethod);
 
             // Find an SSH public key corresponding to the key pair name, if one is
             // given
@@ -4756,55 +4760,6 @@
         }
     }
 
-    protected String validateUserData(String userData, HTTPMethod httpmethod) {
-        byte[] decodedUserData = null;
-        if (userData != null) {
-
-            if (userData.contains("%")) {
-                try {
-                    userData = URLDecoder.decode(userData, "UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    throw new InvalidParameterValueException("Url decoding of userdata failed.");
-                }
-            }
-
-            if (!Base64.isBase64(userData)) {
-                throw new InvalidParameterValueException("User data is not base64 encoded");
-            }
-            // If GET, use 4K. If POST, support up to 1M.
-            if (httpmethod.equals(HTTPMethod.GET)) {
-                if (userData.length() >= MAX_HTTP_GET_LENGTH) {
-                    throw new InvalidParameterValueException("User data is too long for an http GET request");
-                }
-                if (userData.length() > VM_USERDATA_MAX_LENGTH.value()) {
-                    throw new InvalidParameterValueException("User data has exceeded configurable max length : " + VM_USERDATA_MAX_LENGTH.value());
-                }
-                decodedUserData = Base64.decodeBase64(userData.getBytes());
-                if (decodedUserData.length > MAX_HTTP_GET_LENGTH) {
-                    throw new InvalidParameterValueException("User data is too long for GET request");
-                }
-            } else if (httpmethod.equals(HTTPMethod.POST)) {
-                if (userData.length() >= MAX_HTTP_POST_LENGTH) {
-                    throw new InvalidParameterValueException("User data is too long for an http POST request");
-                }
-                if (userData.length() > VM_USERDATA_MAX_LENGTH.value()) {
-                    throw new InvalidParameterValueException("User data has exceeded configurable max length : " + VM_USERDATA_MAX_LENGTH.value());
-                }
-                decodedUserData = Base64.decodeBase64(userData.getBytes());
-                if (decodedUserData.length > MAX_HTTP_POST_LENGTH) {
-                    throw new InvalidParameterValueException("User data is too long for POST request");
-                }
-            }
-
-            if (decodedUserData == null || decodedUserData.length < 1) {
-                throw new InvalidParameterValueException("User data is too short");
-            }
-            // Re-encode so that the '=' paddings are added if necessary since 'isBase64' does not require it, but python does on the VR.
-            return Base64.encodeBase64String(decodedUserData);
-        }
-        return null;
-    }
-
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", async = true)
     public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException {
@@ -5727,9 +5682,9 @@
                     }
                     if (userDataId != null) {
                         UserData apiUserDataVO = userDataDao.findById(userDataId);
-                        return doConcateUserDatas(templateUserDataVO.getUserData(), apiUserDataVO.getUserData());
+                        return userDataManager.concatenateUserData(templateUserDataVO.getUserData(), apiUserDataVO.getUserData(), null);
                     } else if (StringUtils.isNotEmpty(userData)) {
-                        return doConcateUserDatas(templateUserDataVO.getUserData(), userData);
+                        return userDataManager.concatenateUserData(templateUserDataVO.getUserData(), userData, null);
                     } else {
                         return templateUserDataVO.getUserData();
                     }
@@ -5747,16 +5702,6 @@
         return null;
     }
 
-    private String doConcateUserDatas(String userdata1, String userdata2) {
-        byte[] userdata1Bytes = Base64.decodeBase64(userdata1.getBytes());
-        byte[] userdata2Bytes = Base64.decodeBase64(userdata2.getBytes());
-        byte[] finalUserDataBytes = new byte[userdata1Bytes.length + userdata2Bytes.length];
-        System.arraycopy(userdata1Bytes, 0, finalUserDataBytes, 0, userdata1Bytes.length);
-        System.arraycopy(userdata2Bytes, 0, finalUserDataBytes, userdata1Bytes.length, userdata2Bytes.length);
-
-        return Base64.encodeBase64String(finalUserDataBytes);
-    }
-
     @Override
     public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
     StorageUnavailableException, ResourceAllocationException {
@@ -5852,6 +5797,7 @@
         }
 
         String userData = cmd.getUserData();
+        userData = userDataManager.validateUserData(userData, cmd.getHttpMethod());
         Long userDataId = cmd.getUserdataId();
         String userDataDetails = null;
         if (MapUtils.isNotEmpty(cmd.getUserdataDetails())) {
@@ -6343,22 +6289,8 @@
     }
 
     public boolean isVMUsingLocalStorage(VMInstanceVO vm) {
-        boolean usesLocalStorage = false;
-
         List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
-        for (VolumeVO vol : volumes) {
-            DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
-            if (diskOffering.isUseLocalStorage()) {
-                usesLocalStorage = true;
-                break;
-            }
-            StoragePoolVO storagePool = _storagePoolDao.findById(vol.getPoolId());
-            if (storagePool.isLocal()) {
-                usesLocalStorage = true;
-                break;
-            }
-        }
-        return usesLocalStorage;
+        return isAnyVmVolumeUsingLocalStorage(volumes);
     }
 
     @Override
@@ -6417,7 +6349,7 @@
 
         DeployDestination dest = null;
         if (destinationHost == null) {
-            dest = chooseVmMigrationDestination(vm, srcHost);
+            dest = chooseVmMigrationDestination(vm, srcHost, null);
         } else {
             dest = checkVmMigrationDestination(vm, srcHost, destinationHost);
         }
@@ -6432,7 +6364,7 @@
         return findMigratedVm(vm.getId(), vm.getType());
     }
 
-    private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host srcHost) {
+    private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host srcHost, Long poolId) {
         vm.setLastHostId(null); // Last host does not have higher priority in vm migration
         final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
         final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offering, null, null);
@@ -6440,7 +6372,7 @@
         final Host host = _hostDao.findById(srcHostId);
         ExcludeList excludes = new ExcludeList();
         excludes.addHost(srcHostId);
-        final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, null, excludes);
+        final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, poolId, excludes);
         try {
             return _planningMgr.planDeployment(profile, plan, excludes, null);
         } catch (final AffinityConflictException e2) {
@@ -6740,8 +6672,21 @@
         return implicitPlannerUsed;
     }
 
-    private boolean isVmVolumesOnZoneWideStore(VMInstanceVO vm) {
-        final List<VolumeVO> volumes = _volsDao.findCreatedByInstance(vm.getId());
+    protected boolean isAnyVmVolumeUsingLocalStorage(final List<VolumeVO> volumes) {
+        for (VolumeVO vol : volumes) {
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
+            if (diskOffering.isUseLocalStorage()) {
+                return true;
+            }
+            StoragePoolVO storagePool = _storagePoolDao.findById(vol.getPoolId());
+            if (storagePool.isLocal()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected boolean isAllVmVolumesOnZoneWideStore(final List<VolumeVO> volumes) {
         if (CollectionUtils.isEmpty(volumes)) {
             return false;
         }
@@ -6765,6 +6710,10 @@
             throw new InvalidParameterValueException("Cannot migrate VM, host with ID: " + srcHostId + " for VM not found");
         }
 
+        if (destinationHost == null) {
+            return new Pair<>(srcHost, null);
+        }
+
         // Check if source and destination hosts are valid and migrating to same host
         if (destinationHost.getId() == srcHostId) {
             throw new InvalidParameterValueException(String.format("Cannot migrate VM as it is already present on host %s (ID: %s), please specify valid destination host to migrate the VM",
@@ -6884,6 +6833,25 @@
         return volToPoolObjectMap;
     }
 
+    protected boolean isVmCanBeMigratedWithoutStorage(Host srcHost, Host destinationHost, List<VolumeVO> volumes,
+          Map<String, String> volumeToPool) {
+        return !isAnyVmVolumeUsingLocalStorage(volumes) &&
+                MapUtils.isEmpty(volumeToPool) && destinationHost != null
+                && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isAllVmVolumesOnZoneWideStore(volumes));
+    }
+
+    protected Host chooseVmMigrationDestinationUsingVolumePoolMap(VMInstanceVO vm, Host srcHost, Map<Long, Long> volToPoolObjectMap) {
+        Long poolId = null;
+        if (MapUtils.isNotEmpty(volToPoolObjectMap)) {
+            poolId = new ArrayList<>(volToPoolObjectMap.values()).get(0);
+        }
+        DeployDestination deployDestination = chooseVmMigrationDestination(vm, srcHost, poolId);
+        if (deployDestination == null || deployDestination.getHost() == null) {
+            throw new CloudRuntimeException("Unable to find suitable destination to migrate VM " + vm.getInstanceName());
+        }
+        return deployDestination.getHost();
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true)
     public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map<String, String> volumeToPool) throws ResourceUnavailableException,
@@ -6928,8 +6896,8 @@
         Pair<Host, Host> sourceDestinationHosts = getHostsForMigrateVmWithStorage(vm, destinationHost);
         Host srcHost = sourceDestinationHosts.first();
 
-        if (!isVMUsingLocalStorage(vm) && MapUtils.isEmpty(volumeToPool)
-                && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isVmVolumesOnZoneWideStore(vm))){
+        final List<VolumeVO> volumes = _volsDao.findCreatedByInstance(vm.getId());
+        if (isVmCanBeMigratedWithoutStorage(srcHost, destinationHost, volumes, volumeToPool)) {
             // If volumes do not have to be migrated
             // call migrateVirtualMachine for non-user VMs else throw exception
             if (!VirtualMachine.Type.User.equals(vm.getType())) {
@@ -6941,6 +6909,10 @@
 
         Map<Long, Long> volToPoolObjectMap = getVolumePoolMappingForMigrateVmWithStorage(vm, volumeToPool);
 
+        if (destinationHost == null) {
+            destinationHost = chooseVmMigrationDestinationUsingVolumePoolMap(vm, srcHost, volToPoolObjectMap);
+        }
+
         checkHostsDedication(vm, srcHost.getId(), destinationHost.getId());
 
         _itMgr.migrateWithStorage(vm.getUuid(), srcHost.getId(), destinationHost.getId(), volToPoolObjectMap);
diff --git a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
index f2f4a6f..ff97d7e 100644
--- a/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/acl/RoleManagerImpl.java
@@ -56,7 +56,6 @@
 import com.cloud.utils.Pair;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.component.PluggableService;
-import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.db.TransactionCallback;
 import com.cloud.utils.db.TransactionStatus;
@@ -95,7 +94,7 @@
     }
 
     @Override
-    public Role findRole(Long id) {
+    public Role findRole(Long id, boolean removePrivateRoles) {
         if (id == null || id < 1L) {
             logger.trace(String.format("Role ID is invalid [%s]", id));
             return null;
@@ -105,14 +104,18 @@
             logger.trace(String.format("Role not found [id=%s]", id));
             return null;
         }
-        Account account = getCurrentAccount();
-        if (!accountManager.isRootAdmin(account.getId()) && RoleType.Admin == role.getRoleType()) {
-            logger.debug(String.format("Role [id=%s, name=%s] is of 'Admin' type and is only visible to 'Root admins'.", id, role.getName()));
+        if (!isCallerRootAdmin() && (RoleType.Admin == role.getRoleType() || (!role.isPublicRole() && removePrivateRoles))) {
+            logger.debug(String.format("Role [id=%s, name=%s] is either of 'Admin' type or is private and is only visible to 'Root admins'.", id, role.getName()));
             return null;
         }
         return role;
     }
 
+    @Override
+    public Role findRole(Long id) {
+        return findRole(id, false);
+    }
+
     /**
      * Simple call to {@link CallContext#current()} to retrieve the current calling account.
      * This method facilitates unit testing, it avoids mocking static methods.
@@ -140,7 +143,7 @@
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_CREATE, eventDescription = "creating Role")
-    public Role createRole(final String name, final RoleType roleType, final String description) {
+    public Role createRole(final String name, final RoleType roleType, final String description, boolean publicRole) {
         checkCallerAccess();
         if (roleType == null || roleType == RoleType.Unknown) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
@@ -148,7 +151,9 @@
         return Transaction.execute(new TransactionCallback<RoleVO>() {
             @Override
             public RoleVO doInTransaction(TransactionStatus status) {
-                RoleVO role = roleDao.persist(new RoleVO(name, roleType, description));
+                RoleVO role = new RoleVO(name, roleType, description);
+                role.setPublicRole(publicRole);
+                role = roleDao.persist(role);
                 CallContext.current().putContextParameter(Role.class, role.getUuid());
                 return role;
             }
@@ -157,12 +162,14 @@
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_CREATE, eventDescription = "creating role by cloning another role")
-    public Role createRole(String name, Role role, String description) {
+    public Role createRole(String name, Role role, String description, boolean publicRole) {
         checkCallerAccess();
         return Transaction.execute(new TransactionCallback<RoleVO>() {
             @Override
             public RoleVO doInTransaction(TransactionStatus status) {
-                RoleVO newRoleVO = roleDao.persist(new RoleVO(name, role.getRoleType(), description));
+                RoleVO newRole = new RoleVO(name, role.getRoleType(), description);
+                newRole.setPublicRole(publicRole);
+                RoleVO newRoleVO = roleDao.persist(newRole);
                 if (newRoleVO == null) {
                     throw new CloudRuntimeException("Unable to create the role: " + name + ", failed to persist in DB");
                 }
@@ -181,7 +188,7 @@
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_IMPORT, eventDescription = "importing Role")
-    public Role importRole(String name, RoleType type, String description, List<Map<String, Object>> rules, boolean forced) {
+    public Role importRole(String name, RoleType type, String description, List<Map<String, Object>> rules, boolean forced, boolean isPublicRole) {
         checkCallerAccess();
         if (StringUtils.isEmpty(name)) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role name provided");
@@ -190,7 +197,7 @@
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid role type provided");
         }
 
-        List<RoleVO> existingRoles = roleDao.findByName(name);
+        List<RoleVO> existingRoles = roleDao.findByName(name, isCallerRootAdmin());
         if (CollectionUtils.isNotEmpty(existingRoles) && !forced) {
             throw new CloudRuntimeException("Role already exists");
         }
@@ -199,7 +206,7 @@
             @Override
             public RoleVO doInTransaction(TransactionStatus status) {
                 RoleVO newRole = null;
-                RoleVO existingRole = roleDao.findByNameAndType(name, type);
+                RoleVO existingRole = roleDao.findByNameAndType(name, type, isCallerRootAdmin());
                 if (existingRole != null) {
                     if (existingRole.isDefault()) {
                         throw new CloudRuntimeException("Failed to import the role: " + name + ", default role cannot be overriden");
@@ -216,11 +223,14 @@
                     existingRole.setName(name);
                     existingRole.setRoleType(type);
                     existingRole.setDescription(description);
+                    existingRole.setPublicRole(isPublicRole);
                     roleDao.update(existingRole.getId(), existingRole);
 
                     newRole = existingRole;
                 } else {
-                    newRole = roleDao.persist(new RoleVO(name, type, description));
+                    RoleVO role = new RoleVO(name, type, description);
+                    role.setPublicRole(isPublicRole);
+                    newRole = roleDao.persist(role);
                 }
 
                 if (newRole == null) {
@@ -243,16 +253,23 @@
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_ROLE_UPDATE, eventDescription = "updating Role")
-    public Role updateRole(final Role role, final String name, final RoleType roleType, final String description) {
+    public Role updateRole(final Role role, final String name, final RoleType roleType, final String description, Boolean publicRole) {
         checkCallerAccess();
-        if (role.isDefault()) {
-            throw new PermissionDeniedException("Default roles cannot be updated");
-        }
 
         if (roleType != null && roleType == RoleType.Unknown) {
             throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unknown is not a valid role type");
         }
         RoleVO roleVO = (RoleVO)role;
+
+        if (role.isDefault()) {
+            if (publicRole == null || roleType != null || !StringUtils.isAllEmpty(name, description)) {
+                throw new PermissionDeniedException("Default roles cannot be updated (with the exception of making it private/public).");
+            }
+            roleVO.setPublicRole(publicRole);
+            roleDao.update(role.getId(), roleVO);
+            return role;
+        }
+
         if (StringUtils.isNotEmpty(name)) {
             roleVO.setName(name);
         }
@@ -268,6 +285,10 @@
             roleVO.setDescription(description);
         }
 
+        if (publicRole == null) {
+            publicRole = role.isPublicRole();
+        }
+        roleVO.setPublicRole(publicRole);
         roleDao.update(role.getId(), roleVO);
         return role;
     }
@@ -363,7 +384,7 @@
     @Override
     public Pair<List<Role>, Integer> findRolesByName(String name, String keyword, Long startIndex, Long limit) {
         if (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(keyword)) {
-            Pair<List<RoleVO>, Integer> data = roleDao.findAllByName(name, keyword, startIndex, limit);
+            Pair<List<RoleVO>, Integer> data = roleDao.findAllByName(name, keyword, startIndex, limit, isCallerRootAdmin());
             int removed = removeRootAdminRolesIfNeeded(data.first());
             return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
         }
@@ -375,8 +396,7 @@
      *  The actual removal is executed via {@link #removeRootAdminRoles(List)}. Therefore, if the method is called by a 'root admin', we do nothing here.
      */
     protected int removeRootAdminRolesIfNeeded(List<? extends Role> roles) {
-        Account account = getCurrentAccount();
-        if (!accountManager.isRootAdmin(account.getId())) {
+        if (!isCallerRootAdmin()) {
             return removeRootAdminRoles(roles);
         }
         return 0;
@@ -408,10 +428,10 @@
 
     @Override
     public Pair<List<Role>, Integer> findRolesByType(RoleType roleType, Long startIndex, Long limit) {
-        if (roleType == null || RoleType.Admin == roleType && !accountManager.isRootAdmin(getCurrentAccount().getId())) {
+        if (roleType == null || RoleType.Admin == roleType && !isCallerRootAdmin()) {
             return new Pair<List<Role>, Integer>(Collections.emptyList(), 0);
         }
-        Pair<List<RoleVO>, Integer> data = roleDao.findAllByRoleType(roleType, startIndex, limit);
+        Pair<List<RoleVO>, Integer> data = roleDao.findAllByRoleType(roleType, startIndex, limit, isCallerRootAdmin());
         return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second()));
     }
 
@@ -424,8 +444,7 @@
 
     @Override
     public Pair<List<Role>, Integer> listRoles(Long startIndex, Long limit) {
-        Pair<List<RoleVO>, Integer> data = roleDao.searchAndCount(null,
-                new Filter(RoleVO.class, "id", Boolean.TRUE, startIndex, limit));
+        Pair<List<RoleVO>, Integer> data = roleDao.listAllRoles(startIndex, limit, isCallerRootAdmin());
         int removed = removeRootAdminRolesIfNeeded(data.first());
         return new Pair<List<Role>,Integer>(ListUtils.toListOfInterface(data.first()), Integer.valueOf(data.second() - removed));
     }
@@ -439,6 +458,10 @@
         return Collections.emptyList();
     }
 
+    private boolean isCallerRootAdmin() {
+        return accountManager.isRootAdmin(getCurrentAccount().getId());
+    }
+
     @Override
     public Permission getRolePermission(String permission) {
         if (StringUtils.isEmpty(permission)) {
diff --git a/server/src/main/java/org/apache/cloudstack/annotation/AnnotationManagerImpl.java b/server/src/main/java/org/apache/cloudstack/annotation/AnnotationManagerImpl.java
index cc59846..5987979 100644
--- a/server/src/main/java/org/apache/cloudstack/annotation/AnnotationManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/annotation/AnnotationManagerImpl.java
@@ -26,6 +26,7 @@
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.cluster.ManagementServerHostVO;
 import com.cloud.user.dao.UserDataDao;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.Role;
@@ -50,6 +51,7 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
+import com.cloud.cluster.dao.ManagementServerHostDao;
 import com.cloud.dc.ClusterVO;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.HostPodVO;
@@ -158,6 +160,8 @@
     @Inject
     private UserDataDao userDataDao;
     @Inject
+    private ManagementServerHostDao managementServerHostDao;
+    @Inject
     EntityManager entityManager;
 
     private static final List<RoleType> adminRoles = Collections.singletonList(RoleType.Admin);
@@ -192,6 +196,7 @@
         s_typeMap.put(EntityType.VR, ApiCommandResourceType.DomainRouter);
         s_typeMap.put(EntityType.SYSTEM_VM, ApiCommandResourceType.SystemVm);
         s_typeMap.put(EntityType.AUTOSCALE_VM_GROUP, ApiCommandResourceType.AutoScaleVmGroup);
+        s_typeMap.put(EntityType.MANAGEMENT_SERVER, ApiCommandResourceType.Host);
     }
 
     public List<KubernetesClusterHelper> getKubernetesClusterHelpers() {
@@ -532,6 +537,8 @@
                 return kubernetesClusterHelpers.get(0).findByUuid(entityUuid);
             case AUTOSCALE_VM_GROUP:
                 return autoScaleVmGroupDao.findByUuid(entityUuid);
+            case MANAGEMENT_SERVER:
+                return managementServerHostDao.findByUuid(entityUuid);
             default:
                 throw new CloudRuntimeException("Invalid entity type " + type);
         }
@@ -607,6 +614,9 @@
             case SYSTEM_VM:
                 VMInstanceVO instance = vmInstanceDao.findByUuid(entityUuid);
                 return instance != null ? instance.getInstanceName() : null;
+            case MANAGEMENT_SERVER:
+                ManagementServerHostVO mgmtServer = managementServerHostDao.findByUuid(entityUuid);
+                return mgmtServer != null ? mgmtServer.getName() : null;
             default:
                 return null;
         }
diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
index 4e18caa..5da3514 100644
--- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
@@ -27,6 +27,9 @@
 import java.util.Timer;
 import java.util.TimerTask;
 
+import com.cloud.storage.VolumeApiService;
+import com.cloud.utils.fsm.NoTransitionException;
+import com.cloud.vm.VirtualMachineManager;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
@@ -53,6 +56,7 @@
 import org.apache.cloudstack.backup.dao.BackupOfferingDao;
 import org.apache.cloudstack.backup.dao.BackupScheduleDao;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
 import org.apache.cloudstack.framework.jobs.AsyncJobManager;
@@ -147,6 +151,12 @@
     private ApiDispatcher apiDispatcher;
     @Inject
     private AsyncJobManager asyncJobManager;
+    @Inject
+    private VirtualMachineManager virtualMachineManager;
+    @Inject
+    private VolumeApiService volumeApiService;
+    @Inject
+    private VolumeOrchestrationService volumeOrchestrationService;
 
     private AsyncJobDispatcher asyncJobDispatcher;
     private Timer backupTimer;
@@ -585,17 +595,100 @@
 
         final BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(vm.getBackupOfferingId());
         if (offering == null) {
-            throw new CloudRuntimeException("Failed to find backup offering of the VM backup");
+            throw new CloudRuntimeException("Failed to find backup offering of the VM backup.");
         }
 
-        final BackupProvider backupProvider = getBackupProvider(offering.getProvider());
-        if (!backupProvider.restoreVMFromBackup(vm, backup)) {
-            throw new CloudRuntimeException("Error restoring VM from backup ID " + backup.getId());
-        }
+        String backupDetailsInMessage = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(backup, "uuid", "externalId", "vmId", "type", "status", "date");
+        tryRestoreVM(backup, vm, offering, backupDetailsInMessage);
+        updateVolumeState(vm, Volume.Event.RestoreSucceeded, Volume.State.Ready);
+        updateVmState(vm, VirtualMachine.Event.RestoringSuccess, VirtualMachine.State.Stopped);
+
         return importRestoredVM(vm.getDataCenterId(), vm.getDomainId(), vm.getAccountId(), vm.getUserId(),
                 vm.getInstanceName(), vm.getHypervisorType(), backup);
     }
 
+    /**
+     * Tries to restore a VM from a backup. <br/>
+     * First update the VM state to {@link VirtualMachine.Event#RestoringRequested} and its volume states to {@link Volume.Event#RestoreRequested}, <br/>
+     * and then try to restore the backup. <br/>
+     *
+     * If restore fails, then update the VM state to {@link VirtualMachine.Event#RestoringFailed}, and its volumes to {@link Volume.Event#RestoreFailed} and throw an {@link CloudRuntimeException}.
+     */
+    protected void tryRestoreVM(BackupVO backup, VMInstanceVO vm, BackupOffering offering, String backupDetailsInMessage) {
+        try {
+            updateVmState(vm, VirtualMachine.Event.RestoringRequested, VirtualMachine.State.Restoring);
+            updateVolumeState(vm, Volume.Event.RestoreRequested, Volume.State.Restoring);
+            final BackupProvider backupProvider = getBackupProvider(offering.getProvider());
+            if (!backupProvider.restoreVMFromBackup(vm, backup)) {
+                throw new CloudRuntimeException(String.format("Error restoring %s from backup [%s].", vm, backupDetailsInMessage));
+            }
+        // The restore process is executed by a backup provider outside of ACS, I am using the catch-all (Exception) to
+        // ensure that no provider-side exception is missed. Therefore, we have a proper handling of exceptions, and rollbacks if needed.
+        } catch (Exception e) {
+            LOG.error(String.format("Failed to restore backup [%s] due to: [%s].", backupDetailsInMessage, e.getMessage()), e);
+            updateVolumeState(vm, Volume.Event.RestoreFailed, Volume.State.Ready);
+            updateVmState(vm, VirtualMachine.Event.RestoringFailed, VirtualMachine.State.Stopped);
+            throw new CloudRuntimeException(String.format("Error restoring VM from backup [%s].", backupDetailsInMessage));
+        }
+    }
+
+    /**
+     * Tries to update the state of given VM, given specified event
+     * @param vm The VM to update its state
+     * @param event The event to update the VM state
+     * @param next The desired state, just needed to add more context to the logs
+     */
+    private void updateVmState(VMInstanceVO vm, VirtualMachine.Event event, VirtualMachine.State next) {
+        LOG.debug(String.format("Trying to update state of VM [%s] with event [%s].", vm, event));
+        Transaction.execute(TransactionLegacy.CLOUD_DB, (TransactionCallback<VMInstanceVO>) status -> {
+            try {
+                if (!virtualMachineManager.stateTransitTo(vm, event, vm.getHostId())) {
+                    throw new CloudRuntimeException(String.format("Unable to change state of VM [%s] to [%s].", vm, next));
+                }
+            } catch (NoTransitionException e) {
+                String errMsg = String.format("Failed to update state of VM [%s] with event [%s] due to [%s].", vm, event, e.getMessage());
+                LOG.error(errMsg, e);
+                throw new RuntimeException(errMsg);
+            }
+            return null;
+        });
+    }
+
+    /**
+     * Tries to update all volume states of given VM, given specified event
+     * @param vm The VM to which the volumes belong
+     * @param event The event to update the volume states
+     * @param next The desired state, just needed to add more context to the logs
+     */
+    private void updateVolumeState(VMInstanceVO vm, Volume.Event event, Volume.State next) {
+        Transaction.execute(TransactionLegacy.CLOUD_DB, (TransactionCallback<VolumeVO>) status -> {
+            for (VolumeVO volume : volumeDao.findIncludingRemovedByInstanceAndType(vm.getId(), null)) {
+                tryToUpdateStateOfSpecifiedVolume(volume, event, next);
+            }
+            return null;
+        });
+    }
+
+    /**
+     * Tries to update the state of just one volume using any passed {@link Volume.Event}. Throws an {@link RuntimeException} when fails.
+     * @param volume The volume to update it state
+     * @param event The event to update the volume state
+     * @param next The desired state, just needed to add more context to the logs
+     *
+     */
+    private void tryToUpdateStateOfSpecifiedVolume(VolumeVO volume, Volume.Event event, Volume.State next) {
+        LOG.debug(String.format("Trying to update state of volume [%s] with event [%s].", volume, event));
+        try {
+            if (!volumeApiService.stateTransitTo(volume, event)) {
+                throw new CloudRuntimeException(String.format("Unable to change state of volume [%s] to [%s].", volume, next));
+            }
+        } catch (NoTransitionException e) {
+            String errMsg = String.format("Failed to update state of volume [%s] with event [%s] due to [%s].", volume, event, e.getMessage());
+            LOG.error(errMsg, e);
+            throw new RuntimeException(errMsg);
+        }
+    }
+
     private Backup.VolumeInfo getVolumeInfo(List<Backup.VolumeInfo> backedUpVolumes, String volumeUuid) {
         for (Backup.VolumeInfo volInfo : backedUpVolumes) {
             if (volInfo.getUuid().equals(volumeUuid)) {
@@ -652,16 +745,20 @@
         String[] hostPossibleValues = {host.getPrivateIpAddress(), host.getName()};
         String[] datastoresPossibleValues = {datastore.getUuid(), datastore.getName()};
 
+        updateVmState(vm, VirtualMachine.Event.RestoringRequested, VirtualMachine.State.Restoring);
         Pair<Boolean, String> result = restoreBackedUpVolume(backedUpVolumeUuid, backup, backupProvider, hostPossibleValues, datastoresPossibleValues);
 
         if (BooleanUtils.isFalse(result.first())) {
+            updateVmState(vm, VirtualMachine.Event.RestoringFailed, VirtualMachine.State.Stopped);
             throw new CloudRuntimeException(String.format("Error restoring volume [%s] of VM [%s] to host [%s] using backup provider [%s] due to: [%s].",
                     backedUpVolumeUuid, vm.getUuid(), host.getUuid(), backupProvider.getName(), result.second()));
         }
         if (!attachVolumeToVM(vm.getDataCenterId(), result.second(), vmFromBackup.getBackupVolumeList(),
                             backedUpVolumeUuid, vm, datastore.getUuid(), backup)) {
+            updateVmState(vm, VirtualMachine.Event.RestoringFailed, VirtualMachine.State.Stopped);
             throw new CloudRuntimeException(String.format("Error attaching volume [%s] to VM [%s]." + backedUpVolumeUuid, vm.getUuid()));
         }
+        updateVmState(vm, VirtualMachine.Event.RestoringSuccess, VirtualMachine.State.Stopped);
         return true;
     }
 
diff --git a/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImpl.java
new file mode 100644
index 0000000..4b183be
--- /dev/null
+++ b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImpl.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm.schedule;
+
+import com.cloud.api.query.MutualExclusiveIdsManagerBase;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.PluggableService;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
+import org.apache.commons.lang.time.DateUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+import org.springframework.scheduling.support.CronExpression;
+
+import javax.inject.Inject;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.TimeZone;
+
+public class VMScheduleManagerImpl extends MutualExclusiveIdsManagerBase implements VMScheduleManager, PluggableService {
+
+    private static Logger LOGGER = Logger.getLogger(VMScheduleManagerImpl.class);
+
+    @Inject
+    private VMScheduleDao vmScheduleDao;
+    @Inject
+    private UserVmManager userVmManager;
+    @Inject
+    private VMScheduler vmScheduler;
+    @Inject
+    private AccountManager accountManager;
+
+    @Override
+    public List<Class<?>> getCommands() {
+        final List<Class<?>> cmdList = new ArrayList<>();
+        cmdList.add(CreateVMScheduleCmd.class);
+        cmdList.add(ListVMScheduleCmd.class);
+        cmdList.add(UpdateVMScheduleCmd.class);
+        cmdList.add(DeleteVMScheduleCmd.class);
+        return cmdList;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_CREATE, eventDescription = "Creating VM Schedule", create = true)
+    public VMScheduleResponse createSchedule(CreateVMScheduleCmd cmd) {
+        VirtualMachine vm = userVmManager.getUserVm(cmd.getVmId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
+        if (vm == null) {
+            throw new InvalidParameterValueException(String.format("Invalid value for vmId: %s", cmd.getVmId()));
+        }
+
+        VMSchedule.Action action = null;
+        if (cmd.getAction() != null) {
+            try {
+                action = VMSchedule.Action.valueOf(cmd.getAction().toUpperCase());
+            } catch (IllegalArgumentException exception) {
+                throw new InvalidParameterValueException(String.format("Invalid value for action: %s", cmd.getAction()));
+            }
+        }
+
+        Date cmdStartDate = cmd.getStartDate();
+        Date cmdEndDate = cmd.getEndDate();
+        String cmdTimeZone = cmd.getTimeZone();
+        TimeZone timeZone = TimeZone.getTimeZone(cmdTimeZone);
+        String timeZoneId = timeZone.getID();
+        Date startDate = DateUtils.addMinutes(new Date(), 1);
+        if (cmdStartDate != null) {
+            startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
+        }
+        Date endDate = null;
+        if (cmdEndDate != null) {
+            endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
+        }
+
+        CronExpression cronExpression = DateUtil.parseSchedule(cmd.getSchedule());
+
+        validateStartDateEndDate(startDate, endDate, timeZone);
+
+        String description = null;
+        if (StringUtils.isBlank(cmd.getDescription())) {
+            description = String.format("%s - %s", action, DateUtil.getHumanReadableSchedule(cronExpression));
+        } else description = cmd.getDescription();
+
+        LOGGER.warn(String.format("Using timezone [%s] for running the schedule for VM [%s], as an equivalent of [%s].", timeZoneId, vm.getUuid(), cmdTimeZone));
+
+        String finalDescription = description;
+        VMSchedule.Action finalAction = action;
+        Date finalStartDate = startDate;
+        Date finalEndDate = endDate;
+
+        return Transaction.execute((TransactionCallback<VMScheduleResponse>) status -> {
+            VMScheduleVO vmSchedule = vmScheduleDao.persist(new VMScheduleVO(cmd.getVmId(), finalDescription, cronExpression.toString(), timeZoneId, finalAction, finalStartDate, finalEndDate, cmd.getEnabled()));
+            vmScheduler.scheduleNextJob(vmSchedule);
+            CallContext.current().setEventResourceId(vm.getId());
+            CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
+            return createResponse(vmSchedule);
+        });
+    }
+
+    @Override
+    public VMScheduleResponse createResponse(VMSchedule vmSchedule) {
+        VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
+        VMScheduleResponse response = new VMScheduleResponse();
+
+        response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
+        response.setId(vmSchedule.getUuid());
+        response.setVmId(vm.getUuid());
+        response.setDescription(vmSchedule.getDescription());
+        response.setSchedule(vmSchedule.getSchedule());
+        response.setTimeZone(vmSchedule.getTimeZone());
+        response.setAction(vmSchedule.getAction());
+        response.setEnabled(vmSchedule.getEnabled());
+        response.setStartDate(vmSchedule.getStartDate());
+        response.setEndDate(vmSchedule.getEndDate());
+        response.setCreated(vmSchedule.getCreated());
+        return response;
+    }
+
+    @Override
+    public ListResponse<VMScheduleResponse> listSchedule(ListVMScheduleCmd cmd) {
+        Long id = cmd.getId();
+        Boolean enabled = cmd.getEnabled();
+        Long vmId = cmd.getVmId();
+
+        VirtualMachine vm = userVmManager.getUserVm(vmId);
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
+
+        VMSchedule.Action action = null;
+        if (cmd.getAction() != null) {
+            try {
+                action = VMSchedule.Action.valueOf(cmd.getAction());
+            } catch (IllegalArgumentException exception) {
+                throw new InvalidParameterValueException("Invalid value for action: " + cmd.getAction());
+            }
+        }
+
+        Pair<List<VMScheduleVO>, Integer> result = vmScheduleDao.searchAndCount(id, vmId, action, enabled, cmd.getStartIndex(), cmd.getPageSizeVal());
+
+        ListResponse<VMScheduleResponse> response = new ListResponse<>();
+        List<VMScheduleResponse> responsesList = new ArrayList<>();
+        for (VMSchedule vmSchedule : result.first()) {
+            responsesList.add(createResponse(vmSchedule));
+        }
+        response.setResponses(responsesList, result.second());
+        return response;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_UPDATE, eventDescription = "Updating VM Schedule")
+    public VMScheduleResponse updateSchedule(UpdateVMScheduleCmd cmd) {
+        Long id = cmd.getId();
+        VMScheduleVO vmSchedule = vmScheduleDao.findById(id);
+
+        if (vmSchedule == null) {
+            throw new CloudRuntimeException("VM schedule doesn't exist");
+        }
+
+        VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
+
+        CronExpression cronExpression = Objects.requireNonNullElse(
+                DateUtil.parseSchedule(cmd.getSchedule()),
+                DateUtil.parseSchedule(vmSchedule.getSchedule())
+        );
+
+        String description = cmd.getDescription();
+        if (description == null && vmSchedule.getDescription() == null) {
+            description = String.format("%s - %s", vmSchedule.getAction(), DateUtil.getHumanReadableSchedule(cronExpression));
+        }
+        String cmdTimeZone = cmd.getTimeZone();
+        Date cmdStartDate = cmd.getStartDate();
+        Date cmdEndDate = cmd.getEndDate();
+        Boolean enabled = cmd.getEnabled();
+
+        TimeZone timeZone;
+        String timeZoneId;
+        if (cmdTimeZone != null) {
+            timeZone = TimeZone.getTimeZone(cmdTimeZone);
+            timeZoneId = timeZone.getID();
+            if (!timeZoneId.equals(cmdTimeZone)) {
+                LOGGER.warn(String.format("Using timezone [%s] for running the schedule [%s] for VM %s, as an equivalent of [%s].",
+                        timeZoneId, vmSchedule.getSchedule(), vmSchedule.getVmId(), cmdTimeZone));
+            }
+            vmSchedule.setTimeZone(timeZoneId);
+        } else {
+            timeZoneId = vmSchedule.getTimeZone();
+            timeZone = TimeZone.getTimeZone(timeZoneId);
+        }
+
+        Date startDate = vmSchedule.getStartDate().before(DateUtils.addMinutes(new Date(), 1)) ? DateUtils.addMinutes(new Date(), 1) : vmSchedule.getStartDate();
+        Date endDate = vmSchedule.getEndDate();
+        if (cmdEndDate != null) {
+            endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
+        }
+
+        if (cmdStartDate != null) {
+            startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
+        }
+
+        validateStartDateEndDate(Objects.requireNonNullElse(startDate, DateUtils.addMinutes(new Date(), 1)), endDate, timeZone);
+
+        if (enabled != null) {
+            vmSchedule.setEnabled(enabled);
+        }
+        if (description != null) {
+            vmSchedule.setDescription(description);
+        }
+        if (cmdEndDate != null) {
+            vmSchedule.setEndDate(endDate);
+        }
+        if (cmdStartDate != null) {
+            vmSchedule.setStartDate(startDate);
+        }
+        vmSchedule.setSchedule(cronExpression.toString());
+
+        return Transaction.execute((TransactionCallback<VMScheduleResponse>) status -> {
+            vmScheduleDao.update(cmd.getId(), vmSchedule);
+            vmScheduler.updateScheduledJob(vmSchedule);
+            CallContext.current().setEventResourceId(vm.getId());
+            CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
+            return createResponse(vmSchedule);
+        });
+    }
+
+    void validateStartDateEndDate(Date startDate, Date endDate, TimeZone tz) {
+        ZonedDateTime now = ZonedDateTime.now(tz.toZoneId());
+        ZonedDateTime zonedStartDate = ZonedDateTime.ofInstant(startDate.toInstant(), tz.toZoneId());
+
+        if (zonedStartDate.isBefore(now)) {
+            throw new InvalidParameterValueException(String.format("Invalid value for start date. Start date [%s] can't be before current time [%s].", zonedStartDate, now));
+        }
+
+        if (endDate != null) {
+            ZonedDateTime zonedEndDate = ZonedDateTime.ofInstant(endDate.toInstant(), tz.toZoneId());
+            if (zonedEndDate.isBefore(now)) {
+                throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before current time [%s].", zonedEndDate, now));
+            }
+            if (zonedEndDate.isBefore(zonedStartDate)) {
+                throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before start date [%s].", zonedEndDate, zonedStartDate));
+            }
+        }
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_DELETE, eventDescription = "Deleting VM Schedule for VM")
+    public long removeScheduleByVmId(long vmId, boolean expunge) {
+        SearchCriteria<VMScheduleVO> sc = vmScheduleDao.getSearchCriteriaForVMId(vmId);
+        List<VMScheduleVO> vmSchedules = vmScheduleDao.search(sc, null);
+        List<Long> ids = new ArrayList<>();
+        for (final VMScheduleVO vmSchedule : vmSchedules) {
+            ids.add(vmSchedule.getId());
+        }
+        vmScheduler.removeScheduledJobs(ids);
+        if (expunge) {
+            return vmScheduleDao.expunge(sc);
+        }
+        CallContext.current().setEventResourceId(vmId);
+        CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
+        return vmScheduleDao.remove(sc);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_DELETE, eventDescription = "Deleting VM Schedule")
+    public Long removeSchedule(DeleteVMScheduleCmd cmd) {
+        VirtualMachine vm = userVmManager.getUserVm(cmd.getVmId());
+        accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
+
+        List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
+
+        if (ids.isEmpty()) {
+            throw new InvalidParameterValueException("Either id or ids parameter must be specified");
+        }
+        return Transaction.execute((TransactionCallback<Long>) status -> {
+            vmScheduler.removeScheduledJobs(ids);
+            CallContext.current().setEventResourceId(vm.getId());
+            CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
+            return vmScheduleDao.removeSchedulesForVmIdAndIds(vm.getId(), ids);
+        });
+    }
+}
diff --git a/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduler.java b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduler.java
new file mode 100644
index 0000000..94867dd
--- /dev/null
+++ b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduler.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.vm.schedule;
+
+import com.cloud.utils.component.Manager;
+import com.cloud.utils.concurrency.Scheduler;
+import org.apache.cloudstack.framework.config.ConfigKey;
+
+import java.util.Date;
+import java.util.List;
+
+public interface VMScheduler extends Manager, Scheduler {
+    ConfigKey<Integer> VMScheduledJobExpireInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, "vmscheduler.jobs.expire.interval", "30", "VM Scheduler expire interval in days", true);
+
+    void removeScheduledJobs(List<Long> vmScheduleIds);
+
+    void updateScheduledJob(VMScheduleVO vmSchedule);
+
+    Date scheduleNextJob(VMScheduleVO vmSchedule);
+}
diff --git a/server/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedulerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedulerImpl.java
new file mode 100644
index 0000000..aab970e
--- /dev/null
+++ b/server/src/main/java/org/apache/cloudstack/vm/schedule/VMSchedulerImpl.java
@@ -0,0 +1,405 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm.schedule;
+
+import com.cloud.api.ApiGsonHelper;
+import com.cloud.event.ActionEventUtils;
+import com.cloud.event.EventTypes;
+import com.cloud.user.User;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.component.ComponentContext;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.GlobalLock;
+import com.cloud.vm.UserVmManager;
+import com.cloud.vm.VirtualMachine;
+import com.google.common.primitives.Longs;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
+import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
+import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDao;
+import org.apache.commons.lang.time.DateUtils;
+import org.apache.log4j.Logger;
+import org.springframework.scheduling.support.CronExpression;
+
+import javax.inject.Inject;
+import javax.persistence.EntityExistsException;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class VMSchedulerImpl extends ManagerBase implements VMScheduler {
+    private static Logger LOGGER = Logger.getLogger(VMSchedulerImpl.class);
+    @Inject
+    private VMScheduledJobDao vmScheduledJobDao;
+    @Inject
+    private VMScheduleDao vmScheduleDao;
+    @Inject
+    private UserVmManager userVmManager;
+    @Inject
+    private AsyncJobManager asyncJobManager;
+    private AsyncJobDispatcher asyncJobDispatcher;
+    private Timer vmSchedulerTimer;
+    private Date currentTimestamp;
+
+    private EnumMap<VMSchedule.Action, String> actionEventMap = new EnumMap<>(VMSchedule.Action.class);
+
+    public AsyncJobDispatcher getAsyncJobDispatcher() {
+        return asyncJobDispatcher;
+    }
+
+    public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) {
+        asyncJobDispatcher = dispatcher;
+    }
+
+    @Override
+    public void removeScheduledJobs(List<Long> vmScheduleIds) {
+        if (vmScheduleIds == null || vmScheduleIds.isEmpty()) {
+            LOGGER.debug("Removed 0 scheduled jobs");
+            return;
+        }
+        Date now = new Date();
+        int rowsRemoved = vmScheduledJobDao.expungeJobsForSchedules(vmScheduleIds, now);
+        LOGGER.debug(String.format("Removed %s VM scheduled jobs", rowsRemoved));
+    }
+
+    @Override
+    public void updateScheduledJob(VMScheduleVO vmSchedule) {
+        removeScheduledJobs(Longs.asList(vmSchedule.getId()));
+        scheduleNextJob(vmSchedule);
+    }
+
+    public Date scheduleNextJob(Long vmScheduleId) {
+        VMScheduleVO vmSchedule = vmScheduleDao.findById(vmScheduleId);
+        if (vmSchedule != null) {
+            return scheduleNextJob(vmSchedule);
+        }
+        LOGGER.debug(String.format("VM Schedule [id=%s] is removed. Not scheduling next job.", vmScheduleId));
+        return null;
+    }
+
+    @Override
+    public Date scheduleNextJob(VMScheduleVO vmSchedule) {
+        if (!vmSchedule.getEnabled()) {
+            LOGGER.debug(String.format("VM Schedule [id=%s] for VM [id=%s] is disabled. Not scheduling next job.", vmSchedule.getUuid(), vmSchedule.getVmId()));
+            return null;
+        }
+
+        CronExpression cron = DateUtil.parseSchedule(vmSchedule.getSchedule());
+        Date startDate = vmSchedule.getStartDate();
+        Date endDate = vmSchedule.getEndDate();
+        VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
+
+        if (vm == null) {
+            LOGGER.info(String.format("VM [id=%s] is removed. Disabling VM schedule [id=%s].", vmSchedule.getVmId(), vmSchedule.getUuid()));
+            vmSchedule.setEnabled(false);
+            vmScheduleDao.persist(vmSchedule);
+            return null;
+        }
+
+        ZonedDateTime now = ZonedDateTime.now(vmSchedule.getTimeZoneId());
+        ZonedDateTime zonedStartDate = ZonedDateTime.ofInstant(startDate.toInstant(), vmSchedule.getTimeZoneId());
+        ZonedDateTime zonedEndDate = null;
+        if (endDate != null) {
+            zonedEndDate = ZonedDateTime.ofInstant(endDate.toInstant(), vmSchedule.getTimeZoneId());
+        }
+        if (zonedEndDate != null && now.isAfter(zonedEndDate)) {
+            LOGGER.info(String.format("End time is less than current time. Disabling VM schedule [id=%s] for VM [id=%s].", vmSchedule.getUuid(), vmSchedule.getVmId()));
+            vmSchedule.setEnabled(false);
+            vmScheduleDao.persist(vmSchedule);
+            return null;
+        }
+
+        ZonedDateTime ts = null;
+        if (zonedStartDate.isAfter(now)) {
+            ts = cron.next(zonedStartDate);
+        } else {
+            ts = cron.next(now);
+        }
+
+        if (ts == null) {
+            LOGGER.info(String.format("No next schedule found. Disabling VM schedule [id=%s] for VM [id=%s].", vmSchedule.getUuid(), vmSchedule.getVmId()));
+            vmSchedule.setEnabled(false);
+            vmScheduleDao.persist(vmSchedule);
+            return null;
+        }
+
+        Date scheduledDateTime = Date.from(ts.toInstant());
+        VMScheduledJobVO scheduledJob = new VMScheduledJobVO(vmSchedule.getVmId(), vmSchedule.getId(), vmSchedule.getAction(), scheduledDateTime);
+        try {
+            vmScheduledJobDao.persist(scheduledJob);
+            ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, vm.getAccountId(), actionEventMap.get(vmSchedule.getAction()),
+                    String.format("Scheduled action (%s) [vmId: %s scheduleId: %s]  at %s", vmSchedule.getAction(), vm.getUuid(), vmSchedule.getUuid(), scheduledDateTime),
+                    vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), true, 0);
+        } catch (EntityExistsException exception) {
+            LOGGER.debug("Job is already scheduled.");
+        }
+        return scheduledDateTime;
+    }
+
+    @Override
+    public boolean start() {
+        actionEventMap.put(VMSchedule.Action.START, EventTypes.EVENT_VM_SCHEDULE_START);
+        actionEventMap.put(VMSchedule.Action.STOP, EventTypes.EVENT_VM_SCHEDULE_STOP);
+        actionEventMap.put(VMSchedule.Action.REBOOT, EventTypes.EVENT_VM_SCHEDULE_REBOOT);
+        actionEventMap.put(VMSchedule.Action.FORCE_STOP, EventTypes.EVENT_VM_SCHEDULE_FORCE_STOP);
+        actionEventMap.put(VMSchedule.Action.FORCE_REBOOT, EventTypes.EVENT_VM_SCHEDULE_FORCE_REBOOT);
+
+        // Adding 1 minute to currentTimestamp to ensure that
+        // jobs which were to be run at current time, doesn't cause issues
+        currentTimestamp = DateUtils.addMinutes(new Date(), 1);
+
+        scheduleNextJobs();
+
+        final TimerTask schedulerPollTask = new ManagedContextTimerTask() {
+            @Override
+            protected void runInContext() {
+                try {
+                    poll(new Date());
+                } catch (final Throwable t) {
+                    LOGGER.warn("Catch throwable in VM scheduler ", t);
+                }
+            }
+        };
+
+        vmSchedulerTimer = new Timer("VMSchedulerPollTask");
+        vmSchedulerTimer.schedule(schedulerPollTask, 5000L, 60 * 1000L);
+        return true;
+    }
+
+    @Override
+    public void poll(Date timestamp) {
+        currentTimestamp = DateUtils.round(timestamp, Calendar.MINUTE);
+        String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
+        LOGGER.debug(String.format("VM scheduler.poll is being called at %s", displayTime));
+
+        GlobalLock scanLock = GlobalLock.getInternLock("vmScheduler.poll");
+        try {
+            if (scanLock.lock(30)) {
+                try {
+                    scheduleNextJobs();
+                } finally {
+                    scanLock.unlock();
+                }
+            }
+        } finally {
+            scanLock.releaseRef();
+        }
+
+        scanLock = GlobalLock.getInternLock("vmScheduler.poll");
+        try {
+            if (scanLock.lock(30)) {
+                try {
+                    startJobs(); // Create async job and update scheduled job
+                } finally {
+                    scanLock.unlock();
+                }
+            }
+        } finally {
+            scanLock.releaseRef();
+        }
+
+        try {
+            cleanupVMScheduledJobs();
+        } catch (Exception e) {
+            LOGGER.warn("Error in cleaning up vm scheduled jobs", e);
+        }
+    }
+
+    private void scheduleNextJobs() {
+        for (final VMScheduleVO schedule : vmScheduleDao.listAllActiveSchedules()) {
+            try {
+                scheduleNextJob(schedule);
+            } catch (Exception e) {
+                LOGGER.warn("Error in scheduling next job for schedule " + schedule.getUuid(), e);
+            }
+        }
+    }
+
+    /**
+     * Delete scheduled jobs before vm.scheduler.expire.interval days
+     */
+    private void cleanupVMScheduledJobs() {
+        Date deleteBeforeDate = DateUtils.addDays(currentTimestamp, -1 * VMScheduledJobExpireInterval.value());
+        int rowsRemoved = vmScheduledJobDao.expungeJobsBefore(deleteBeforeDate);
+        LOGGER.info(String.format("Cleaned up %d VM scheduled job entries", rowsRemoved));
+    }
+
+    void executeJobs(Map<Long, VMScheduledJob> jobsToExecute) {
+        String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
+
+        for (Map.Entry<Long, VMScheduledJob> entry : jobsToExecute.entrySet()) {
+            VMScheduledJob vmScheduledJob = entry.getValue();
+            VirtualMachine vm = userVmManager.getUserVm(vmScheduledJob.getVmId());
+
+            VMScheduledJobVO tmpVMScheduleJob = null;
+            try {
+                if (LOGGER.isDebugEnabled()) {
+                    final Date scheduledTimestamp = vmScheduledJob.getScheduledTime();
+                    displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
+                    LOGGER.debug(String.format("Executing %s for VM id %d for schedule id: %d at %s", vmScheduledJob.getAction(), vmScheduledJob.getVmId(), vmScheduledJob.getVmScheduleId(), displayTime));
+                }
+
+                tmpVMScheduleJob = vmScheduledJobDao.acquireInLockTable(vmScheduledJob.getId());
+                Long jobId = processJob(vmScheduledJob, vm);
+                if (jobId != null) {
+                    tmpVMScheduleJob.setAsyncJobId(jobId);
+                    vmScheduledJobDao.update(vmScheduledJob.getId(), tmpVMScheduleJob);
+                }
+            } catch (final Exception e) {
+                LOGGER.warn(String.format("Executing scheduled job id: %s failed due to %s", vmScheduledJob.getId(), e));
+            } finally {
+                if (tmpVMScheduleJob != null) {
+                    vmScheduledJobDao.releaseFromLockTable(vmScheduledJob.getId());
+                }
+            }
+        }
+    }
+
+    Long processJob(VMScheduledJob vmScheduledJob, VirtualMachine vm) {
+        if (!Arrays.asList(VirtualMachine.State.Running, VirtualMachine.State.Stopped).contains(vm.getState())) {
+            LOGGER.info(String.format("Skipping action (%s) for [vmId:%s scheduleId: %s] because VM is invalid state: %s", vmScheduledJob.getAction(), vm.getUuid(), vmScheduledJob.getVmScheduleId(), vm.getState()));
+            return null;
+        }
+
+        final Long eventId = ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), null,
+                actionEventMap.get(vmScheduledJob.getAction()), true,
+                String.format("Executing action (%s) for VM Id:%s", vmScheduledJob.getAction(), vm.getUuid()),
+                vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0);
+
+        if (vm.getState() == VirtualMachine.State.Running) {
+            switch (vmScheduledJob.getAction()) {
+                case STOP:
+                    return executeStopVMJob(vm, false, eventId);
+                case FORCE_STOP:
+                    return executeStopVMJob(vm, true, eventId);
+                case REBOOT:
+                    return executeRebootVMJob(vm, false, eventId);
+                case FORCE_REBOOT:
+                    return executeRebootVMJob(vm, true, eventId);
+            }
+        } else if (vm.getState() == VirtualMachine.State.Stopped && vmScheduledJob.getAction() == VMSchedule.Action.START) {
+            return executeStartVMJob(vm, eventId);
+        }
+
+        LOGGER.warn(String.format("Skipping action (%s) for [vmId:%s scheduleId: %s] because VM is in state: %s",
+                vmScheduledJob.getAction(), vm.getUuid(), vmScheduledJob.getVmScheduleId(), vm.getState()));
+        return null;
+    }
+
+    private void skipJobs(Map<Long, VMScheduledJob> jobsToExecute, Map<Long, List<VMScheduledJob>> jobsNotToExecute) {
+        for (Map.Entry<Long, List<VMScheduledJob>> entry : jobsNotToExecute.entrySet()) {
+            Long vmId = entry.getKey();
+            List<VMScheduledJob> skippedVmScheduledJobVOS = entry.getValue();
+            VirtualMachine vm = userVmManager.getUserVm(vmId);
+            for (final VMScheduledJob skippedVmScheduledJobVO : skippedVmScheduledJobVOS) {
+                VMScheduledJob scheduledJob = jobsToExecute.get(vmId);
+                LOGGER.info(String.format("Skipping scheduled job [id: %s, vmId: %s] because of conflict with another scheduled job [id: %s]", skippedVmScheduledJobVO.getUuid(), vm.getUuid(), scheduledJob.getUuid()));
+            }
+        }
+    }
+
+    /**
+     * Create async jobs for VM scheduled jobs
+     */
+    private void startJobs() {
+        String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
+
+        final List<VMScheduledJobVO> vmScheduledJobs = vmScheduledJobDao.listJobsToStart(currentTimestamp);
+        LOGGER.debug(String.format("Got %d scheduled jobs to be executed at %s", vmScheduledJobs.size(), displayTime));
+
+        Map<Long, VMScheduledJob> jobsToExecute = new HashMap<>();
+        Map<Long, List<VMScheduledJob>> jobsNotToExecute = new HashMap<>();
+        for (final VMScheduledJobVO vmScheduledJobVO : vmScheduledJobs) {
+            long vmId = vmScheduledJobVO.getVmId();
+            if (jobsToExecute.get(vmId) == null) {
+                jobsToExecute.put(vmId, vmScheduledJobVO);
+            } else {
+                jobsNotToExecute.computeIfAbsent(vmId, k -> new ArrayList<>()).add(vmScheduledJobVO);
+            }
+        }
+
+        executeJobs(jobsToExecute);
+        skipJobs(jobsToExecute, jobsNotToExecute);
+    }
+
+    long executeStartVMJob(VirtualMachine vm, long eventId) {
+        final Map<String, String> params = new HashMap<>();
+        params.put(ApiConstants.ID, String.valueOf(vm.getId()));
+        params.put("ctxUserId", "1");
+        params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
+        params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
+
+        final StartVMCmd cmd = new StartVMCmd();
+        ComponentContext.inject(cmd);
+
+        AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), StartVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
+        job.setDispatcher(asyncJobDispatcher.getName());
+
+        return asyncJobManager.submitAsyncJob(job);
+    }
+
+    long executeStopVMJob(VirtualMachine vm, boolean isForced, long eventId) {
+        final Map<String, String> params = new HashMap<>();
+        params.put(ApiConstants.ID, String.valueOf(vm.getId()));
+        params.put("ctxUserId", "1");
+        params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
+        params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
+        params.put(ApiConstants.FORCED, String.valueOf(isForced));
+
+        final StopVMCmd cmd = new StopVMCmd();
+        ComponentContext.inject(cmd);
+
+        AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), StopVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
+        job.setDispatcher(asyncJobDispatcher.getName());
+
+        return asyncJobManager.submitAsyncJob(job);
+    }
+
+    long executeRebootVMJob(VirtualMachine vm, boolean isForced, long eventId) {
+        final Map<String, String> params = new HashMap<>();
+        params.put(ApiConstants.ID, String.valueOf(vm.getId()));
+        params.put("ctxUserId", "1");
+        params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
+        params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
+        params.put(ApiConstants.FORCED, String.valueOf(isForced));
+
+        final RebootVMCmd cmd = new RebootVMCmd();
+        ComponentContext.inject(cmd);
+
+        AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), RebootVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
+        job.setDispatcher(asyncJobDispatcher.getName());
+
+        return asyncJobManager.submitAsyncJob(job);
+    }
+}
diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
index a9db159..ee676aa 100644
--- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
+++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml
@@ -21,10 +21,10 @@
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:util="http://www.springframework.org/schema/util"
-       
+
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                       http://www.springframework.org/schema/beans/spring-beans.xsd
-                      http://www.springframework.org/schema/aop 
+                      http://www.springframework.org/schema/aop
                       http://www.springframework.org/schema/aop/spring-aop.xsd
                       http://www.springframework.org/schema/context
                       http://www.springframework.org/schema/context/spring-context.xsd
@@ -129,7 +129,7 @@
 
     <bean id="capacityManagerImpl" class="com.cloud.capacity.CapacityManagerImpl" />
 
-    <bean id="configurationManagerImpl" class="com.cloud.configuration.ConfigurationManagerImpl" >  
+    <bean id="configurationManagerImpl" class="com.cloud.configuration.ConfigurationManagerImpl" >
         <property name="secChecker" value="#{securityCheckersRegistry.registered}" />
     </bean>
 
@@ -211,45 +211,45 @@
 
     <bean id="uploadMonitorImpl" class="com.cloud.storage.upload.UploadMonitorImpl" />
     <bean id="usageServiceImpl" class="com.cloud.usage.UsageServiceImpl" />
-    
+
     <bean id="virtualNetworkApplianceManagerImpl"
         class="com.cloud.network.router.VirtualNetworkApplianceManagerImpl" />
-        
+
     <bean id="vpcManagerImpl" class="com.cloud.network.vpc.VpcManagerImpl" >
         <property name="vpcElements" value="#{vpcProvidersRegistry.registered}"></property>
     </bean>
-    
+
     <bean id="vpcTxCallable" class="com.cloud.network.vpc.VpcPrivateGatewayTransactionCallable" />
-    
+
     <bean id="vpcVirtualNetworkApplianceManagerImpl"
         class="com.cloud.network.router.VpcVirtualNetworkApplianceManagerImpl" />
-    
+
     <bean id="virtualNetworkApplianceFactory"
         class="com.cloud.network.rules.VirtualNetworkApplianceFactory" />
-    
+
     <bean id="topologyContext" class="org.apache.cloudstack.network.topology.NetworkTopologyContext" init-method="init" />
-    
+
     <bean id="basicNetworkTopology" class="org.apache.cloudstack.network.topology.BasicNetworkTopology" />
     <bean id="advancedNetworkTopology" class="org.apache.cloudstack.network.topology.AdvancedNetworkTopology" />
-    
+
     <bean id="basicNetworkVisitor" class="org.apache.cloudstack.network.topology.BasicNetworkVisitor" />
     <bean id="advancedNetworkVisitor" class="org.apache.cloudstack.network.topology.AdvancedNetworkVisitor" />
-    
+
     <bean id="commandSetupHelper"
         class="com.cloud.network.router.CommandSetupHelper" />
-    
+
     <bean id="routerControlHelper"
         class="com.cloud.network.router.RouterControlHelper" />
-        
+
     <bean id="networkHelper"
         class="com.cloud.network.router.NetworkHelperImpl" />
-        
+
     <bean id="vpcNetworkHelper"
         class="com.cloud.network.router.VpcNetworkHelperImpl" />
-        
+
     <bean id="nicProfileHelper"
         class="com.cloud.network.router.NicProfileHelperImpl" />
-        
+
     <bean id="routerDeploymentDefinitionBuilder"
         class="org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinitionBuilder" />
 
@@ -257,6 +257,9 @@
         <property name="name" value="ApiAsyncJobDispatcher" />
     </bean>
 
+    <bean id="shutdownManager" class="org.apache.cloudstack.shutdown.ShutdownManagerImpl" >
+        <property name="name" value="shutdownManager" />
+    </bean>
 
     <bean id="statsCollector" class="com.cloud.server.StatsCollector" />
 
@@ -265,14 +268,14 @@
     <bean id="domainManagerImpl" class="com.cloud.user.DomainManagerImpl" />
 
     <bean id="downloadMonitorImpl" class="com.cloud.storage.download.DownloadMonitorImpl" />
-  
+
     <bean id="lBHealthCheckManagerImpl" class="com.cloud.network.lb.LBHealthCheckManagerImpl" />
 
     <bean id="volumeApiServiceImpl" class="com.cloud.storage.VolumeApiServiceImpl">
         <property name="storagePoolAllocators"
             value="#{storagePoolAllocatorsRegistry.registered}" />
     </bean>
-    
+
     <bean id="ApplicationLoadBalancerService" class="org.apache.cloudstack.network.lb.ApplicationLoadBalancerManagerImpl" />
 
     <bean id="vMSnapshotManagerImpl" class="com.cloud.vm.snapshot.VMSnapshotManagerImpl" />
@@ -344,4 +347,9 @@
 
     <bean id="resourceIconManager" class="com.cloud.resourceicon.ResourceIconManagerImpl" />
 
+    <bean id="VMScheduleManagerImpl" class="org.apache.cloudstack.vm.schedule.VMScheduleManagerImpl" />
+    <bean id="VMSchedulerImpl" class="org.apache.cloudstack.vm.schedule.VMSchedulerImpl">
+        <property name="asyncJobDispatcher" ref="ApiAsyncJobDispatcher" />
+    </bean>
+
 </beans>
diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java
index 715924d..5d1986e 100644
--- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java
+++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.Set;
 import java.util.UUID;
 
 import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
@@ -548,15 +549,67 @@
     }
 
     @Test
+    public void validateEmptySourceNatServiceCapablitiesTest() {
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+
+        configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
+    }
+
+    @Test
+    public void validateInvalidSourceNatTypeForSourceNatServiceCapablitiesTest() {
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "perDomain");
+
+        boolean caught = false;
+        try {
+            configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage(), e.getMessage().contains("Either peraccount or perzone source NAT type can be specified for SupportedSourceNatTypes"));
+            caught = true;
+        }
+        Assert.assertTrue("should not be accepted", caught);
+    }
+
+    @Test
+    public void validateInvalidBooleanValueForSourceNatServiceCapablitiesTest() {
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "maybe");
+
+        boolean caught = false;
+        try {
+            configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage(), e.getMessage().contains("Unknown specified value for RedundantRouter"));
+            caught = true;
+        }
+        Assert.assertTrue("should not be accepted", caught);
+    }
+
+    @Test
+    public void validateInvalidCapabilityForSourceNatServiceCapablitiesTest() {
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.ElasticIp, "perDomain");
+
+        boolean caught = false;
+        try {
+            configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage(), e.getMessage().contains("Only SupportedSourceNatTypes, Network.Capability[name=RedundantRouter] capabilities can be specified for source nat service"));
+            caught = true;
+        }
+        Assert.assertTrue("should not be accepted", caught);
+    }
+
+    @Test
     public void validateEmptyStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
 
         configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap);
     }
 
     @Test
     public void validateInvalidStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
         staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "Frue and Talse");
 
         boolean caught = false;
@@ -570,8 +623,42 @@
     }
 
     @Test
+    public void isRedundantRouter() {
+        Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
+        sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "true");
+        Assert.assertTrue(configurationMgr.isRedundantRouter(serviceCapabilityMap, sourceNatServiceCapabilityMap));
+    }
+
+    @Test
+    public void isSharedSourceNat() {
+        Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "perzone");
+        Assert.assertTrue(configurationMgr.isSharedSourceNat(serviceCapabilityMap, sourceNatServiceCapabilityMap));
+    }
+
+    @Test
+    public void isNotSharedSourceNat() {
+        Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
+        Assert.assertFalse(configurationMgr.isSharedSourceNat(serviceCapabilityMap, sourceNatServiceCapabilityMap));
+    }
+
+    @Test
+    public void sourceNatCapabilitiesContainValidValues() {
+        Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
+        sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
+        sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "True");
+
+        Assert.assertTrue(configurationMgr.sourceNatCapabilitiesContainValidValues(sourceNatServiceCapabilityMap));
+    }
+
+    @Test
     public void validateTTStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
         staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse");
         staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
 
@@ -580,7 +667,7 @@
 
     @Test
     public void validateFTStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
         staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false");
         staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
 
@@ -589,7 +676,7 @@
 
     @Test
     public void validateTFStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
         staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse");
         staticNatServiceCapabilityMap.put(Capability.ElasticIp, "false");
 
@@ -608,7 +695,7 @@
 
     @Test
     public void validateFFStaticNatServiceCapablitiesTest() {
-        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
+        Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
         staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false");
         staticNatServiceCapabilityMap.put(Capability.ElasticIp, "False");
 
diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
index d79010e..0b6004b 100644
--- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
+++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
@@ -21,39 +21,31 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import com.cloud.dc.ClusterDetailsVO;
-import com.cloud.dc.DataCenter;
-import com.cloud.gpu.GPU;
-import com.cloud.host.Host;
-import com.cloud.host.HostVO;
-import com.cloud.host.Status;
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.storage.Storage;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.Volume;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.user.AccountVO;
-import com.cloud.user.dao.AccountDao;
-import com.cloud.utils.Pair;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.VirtualMachine;
-import com.cloud.vm.VirtualMachine.Type;
-import com.cloud.vm.VirtualMachineProfile;
-import com.cloud.vm.VirtualMachineProfileImpl;
+import org.apache.cloudstack.affinity.AffinityGroupProcessor;
+import org.apache.cloudstack.affinity.AffinityGroupService;
+import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
 import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao;
+import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
+import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.framework.messagebus.MessageBus;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.test.utils.SpringUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.junit.Assert;
 import org.junit.Before;
@@ -79,22 +71,14 @@
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.context.support.AnnotationConfigContextLoader;
 
-import org.apache.cloudstack.affinity.AffinityGroupProcessor;
-import org.apache.cloudstack.affinity.AffinityGroupService;
-import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
-import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
-import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
-import org.apache.cloudstack.framework.messagebus.MessageBus;
-import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.test.utils.SpringUtils;
-
 import com.cloud.agent.AgentManager;
 import com.cloud.capacity.CapacityManager;
 import com.cloud.capacity.dao.CapacityDao;
+import com.cloud.configuration.ConfigurationManagerImpl;
 import com.cloud.dc.ClusterDetailsDao;
+import com.cloud.dc.ClusterDetailsVO;
 import com.cloud.dc.ClusterVO;
+import com.cloud.dc.DataCenter;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.dao.ClusterDao;
 import com.cloud.dc.dao.DataCenterDao;
@@ -105,26 +89,46 @@
 import com.cloud.deploy.dao.PlannerHostReservationDao;
 import com.cloud.exception.AffinityConflictException;
 import com.cloud.exception.InsufficientServerCapacityException;
+import com.cloud.gpu.GPU;
 import com.cloud.gpu.dao.HostGpuGroupsDao;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.Status;
 import com.cloud.host.dao.HostDao;
+import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.host.dao.HostTagsDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.resource.ResourceManager;
 import com.cloud.org.Grouping.AllocationState;
+import com.cloud.resource.ResourceManager;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDetailsDao;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.ScopeType;
+import com.cloud.storage.Storage;
 import com.cloud.storage.StorageManager;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSCategoryDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.StoragePoolHostDao;
+import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.Pair;
 import com.cloud.utils.component.ComponentContext;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.Type;
+import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.VirtualMachineProfileImpl;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.VMInstanceDao;
-import com.cloud.host.dao.HostDetailsDao;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@@ -191,6 +195,9 @@
     @Inject
     ClusterDetailsDao clusterDetailsDao;
 
+    @Inject
+    PrimaryDataStoreDao primaryDataStoreDao;
+
     @Mock
     Host host;
 
@@ -1075,4 +1082,84 @@
         Assert.assertEquals(6, hosts.get(6).getId());
         Assert.assertEquals(2, hosts.get(7).getId());
     }
+
+    private List<Long> prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(boolean configValue, boolean mockVolumes, boolean mockClusterStoreVolume) {
+        try {
+            Field f = ConfigKey.class.getDeclaredField("_defaultValue");
+            f.setAccessible(true);
+            f.set(ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS, String.valueOf(configValue));
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+        List<Long> allClusters = List.of(101L, 102L, 103L, 104L);
+        Mockito.when(_clusterDao.listAllClusters(Mockito.anyLong())).thenReturn(allClusters);
+        if (mockVolumes) {
+            VolumeVO vol1 = Mockito.mock(VolumeVO.class);
+            Mockito.when(vol1.getPoolId()).thenReturn(1L);
+            VolumeVO vol2 = Mockito.mock(VolumeVO.class);
+            Mockito.when(vol2.getPoolId()).thenReturn(2L);
+            StoragePoolVO pool1 = Mockito.mock(StoragePoolVO.class);
+            Mockito.when(pool1.getScope()).thenReturn(ScopeType.ZONE);
+            Mockito.when(primaryDataStoreDao.findById(1L)).thenReturn(pool1);
+            StoragePoolVO pool2 = Mockito.mock(StoragePoolVO.class);
+            Mockito.when(pool2.getScope()).thenReturn(mockClusterStoreVolume ? ScopeType.CLUSTER : ScopeType.GLOBAL);
+            Mockito.when(primaryDataStoreDao.findById(2L)).thenReturn(pool2);
+            Mockito.when(volDao.findUsableVolumesForInstance(1L)).thenReturn(List.of(vol1, vol2));
+        } else {
+            Mockito.when(volDao.findUsableVolumesForInstance(1L)).thenReturn(new ArrayList<>());
+        }
+        return allClusters;
+    }
+
+    @Test
+    public void avoidOtherClustersForDeploymentIfMigrationDisabledNonValidHost() {
+        prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(false, false, false);
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        ExcludeList excludeList = new ExcludeList();
+        _dpm.avoidOtherClustersForDeploymentIfMigrationDisabled(vm, null, excludeList);
+        Assert.assertTrue(CollectionUtils.isEmpty(excludeList.getClustersToAvoid()));
+
+        Host lastHost = Mockito.mock(Host.class);
+        Mockito.when(lastHost.getClusterId()).thenReturn(null);
+        _dpm.avoidOtherClustersForDeploymentIfMigrationDisabled(vm, lastHost, excludeList);
+        Assert.assertTrue(CollectionUtils.isEmpty(excludeList.getClustersToAvoid()));
+    }
+
+    private Set<Long> runAvoidOtherClustersForDeploymentIfMigrationDisabledTest() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(vm.getId()).thenReturn(1L);
+        ExcludeList excludeList = new ExcludeList();
+        Host lastHost = Mockito.mock(Host.class);
+        Long sourceClusterId = 101L;
+        Mockito.when(lastHost.getClusterId()).thenReturn(sourceClusterId);
+        _dpm.avoidOtherClustersForDeploymentIfMigrationDisabled(vm, lastHost, excludeList);
+        return excludeList.getClustersToAvoid();
+    }
+
+    @Test
+    public void avoidOtherClustersForDeploymentIfMigrationDisabledConfigAllows() {
+        prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(true,false, false);
+        Assert.assertTrue(CollectionUtils.isEmpty(runAvoidOtherClustersForDeploymentIfMigrationDisabledTest()));
+    }
+
+    @Test
+    public void avoidOtherClustersForDeploymentIfMigrationDisabledNoVmVolumes() {
+        prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(false,false, false);
+        Assert.assertTrue(CollectionUtils.isEmpty(runAvoidOtherClustersForDeploymentIfMigrationDisabledTest()));
+    }
+
+    @Test
+    public void avoidOtherClustersForDeploymentIfMigrationDisabledVmVolumesNonValidScope() {
+        prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(false,true, false);
+        Assert.assertTrue(CollectionUtils.isEmpty(runAvoidOtherClustersForDeploymentIfMigrationDisabledTest()));
+    }
+
+    @Test
+    public void avoidOtherClustersForDeploymentIfMigrationDisabledValid() {
+        List<Long> allClusters = prepareMockForAvoidOtherClustersForDeploymentIfMigrationDisabled(false,true, true);
+        Set<Long> avoidedClusters = runAvoidOtherClustersForDeploymentIfMigrationDisabledTest();
+        Assert.assertTrue(CollectionUtils.isNotEmpty(avoidedClusters));
+        Assert.assertEquals(allClusters.size()-1, avoidedClusters.size());
+        Assert.assertFalse(avoidedClusters.contains(allClusters.get(0)));
+    }
 }
diff --git a/server/src/test/java/com/cloud/network/IpAddressManagerTest.java b/server/src/test/java/com/cloud/network/IpAddressManagerTest.java
index 50ad62e..ef5ad8c 100644
--- a/server/src/test/java/com/cloud/network/IpAddressManagerTest.java
+++ b/server/src/test/java/com/cloud/network/IpAddressManagerTest.java
@@ -23,11 +23,13 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Vector;
 
 import com.cloud.user.Account;
 import org.junit.Assert;
@@ -230,4 +232,14 @@
         return network;
     }
 
+    @Test
+    public void updateSourceNatIpAddress() throws Exception {
+        IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class);
+        IPAddressVO oldIp = Mockito.mock(IPAddressVO.class);
+        List<IPAddressVO> userIps = new Vector<>();
+        userIps.add(oldIp);
+        ipAddressManager.updateSourceNatIpAddress(requestedIp, userIps);
+        verify(requestedIp).setSourceNat(true);
+        verify(oldIp).setSourceNat(false);
+    }
 }
diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
index fcf7ad3..8235395 100644
--- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
+++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
@@ -17,8 +17,10 @@
 package com.cloud.network;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.mock;
@@ -34,6 +36,7 @@
 import java.util.Map;
 import java.util.UUID;
 
+import com.cloud.exception.InsufficientAddressCapacityException;
 import org.apache.cloudstack.alert.AlertService;
 import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
 import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
@@ -81,7 +84,6 @@
 import com.cloud.org.Grouping;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
-import com.cloud.user.AccountManagerImpl;
 import com.cloud.user.AccountService;
 import com.cloud.user.AccountVO;
 import com.cloud.user.User;
@@ -122,8 +124,6 @@
     @Mock
     NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
     @Mock
-    AccountManager accountMgr;
-    @Mock
     EntityManager entityMgr;
     @Mock
     NetworkService networkService;
@@ -162,8 +162,8 @@
     @Mock
     ServiceOfferingVO serviceOfferingVoMock;
 
-    @InjectMocks
-    AccountManagerImpl accountManagerImpl;
+    @Mock
+    IpAddressManager ipAddressManager;
     @Mock
     ConfigKey<Integer> privateMtuKey;
     @Mock
@@ -223,7 +223,7 @@
         service._networkOfferingDao = networkOfferingDao;
         service._physicalNetworkDao = physicalNetworkDao;
         service._dcDao = dcDao;
-        service._accountMgr = accountMgr;
+        service._accountMgr = accountManager;
         service._networkMgr = networkManager;
         service.alertManager = alertManager;
         service._configMgr = configMgr;
@@ -236,6 +236,7 @@
         service.routerDao = routerDao;
         service.commandSetupHelper = commandSetupHelper;
         service.networkHelper = networkHelper;
+        service._ipAddrMgr = ipAddressManager;
         PowerMockito.mockStatic(CallContext.class);
         CallContext callContextMock = PowerMockito.mock(CallContext.class);
         PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
@@ -248,8 +249,8 @@
         Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering);
         Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet);
         Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc);
-        Mockito.lenient().doNothing().when(accountMgr).checkAccess(accountMock, networkOffering, dc);
-        Mockito.when(accountMgr.isRootAdmin(accountMock.getId())).thenReturn(true);
+        Mockito.lenient().doNothing().when(accountManager).checkAccess(accountMock, networkOffering, dc);
+        Mockito.when(accountManager.isRootAdmin(accountMock.getId())).thenReturn(true);
     }
 
     @Test
@@ -352,6 +353,7 @@
         ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu);
         ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
         Mockito.when(offering.isSystemOnly()).thenReturn(false);
+        Mockito.when(dc.getId()).thenReturn(1L);
         Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
         Map<String, String> networkProvidersMap = new HashMap<String, String>();
         Mockito.when(networkManager.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap);
@@ -409,6 +411,7 @@
         ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu);
         ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
         ReflectionTestUtils.setField(createNetworkCmd, "vpcId", 1L);
+        Mockito.when(dc.getId()).thenReturn(1L);
         Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(true);
         Mockito.when(vpcDao.findById(anyLong())).thenReturn(vpc);
 
@@ -562,7 +565,7 @@
         }
     }
 
-    @Test(expected = InvalidParameterValueException.class)
+    @Test(expected = CloudRuntimeException.class)
     public void testCreateNetworkDnsOfferingServiceFailure() {
         registerCallContext();
         CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@@ -577,7 +580,7 @@
         }
     }
 
-    @Test(expected = InvalidParameterValueException.class)
+    @Test(expected = CloudRuntimeException.class)
     public void testCreateIp4NetworkIp6DnsFailure() {
         registerCallContext();
         CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@@ -770,4 +773,143 @@
 
         networkServiceImplMock.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(1l);
     }
+
+    @Test
+    public void validateNotSharedNetworkRouterIPv4() {
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.L2);
+        service.validateSharedNetworkRouterIPs(null, null, null, null, null, null, null, null, null, ntwkOff);
+    }
+
+    @Test
+    public void validateSharedNetworkRouterIPs() {
+        String startIP = "10.0.16.2";
+        String endIP = "10.0.16.100";
+        String routerIPv4 = "10.0.16.100";
+        String routerPv6 = "fd17:ac56:1234:2000::fb";
+        String startIPv6 = "fd17:ac56:1234:2000::1";
+        String endIPv6 = "fd17:ac56:1234:2000::fc";
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
+    }
+
+    @Test
+    public void validateSharedNetworkWrongRouterIPv4() {
+        String startIP = "10.0.16.2";
+        String endIP = "10.0.16.100";
+        String routerIPv4 = "10.0.16.101";
+        String routerPv6 = "fd17:ac56:1234:2000::fb";
+        String startIPv6 = "fd17:ac56:1234:2000::1";
+        String endIPv6 = "fd17:ac56:1234:2000::fc";
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        boolean passing = false;
+        try {
+            service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
+        } catch (CloudRuntimeException e) {
+            Assert.assertTrue(e.getMessage().contains("Router IPv4 IP provided is not within the specified range: "));
+            passing = true;
+        }
+        Assert.assertTrue(passing);
+    }
+
+    @Test
+    public void validateSharedNetworkNoEndOfIPv6Range() {
+        String startIP = null;
+        String endIP = null;
+        String routerIPv4 = null;
+        String routerPv6 = "fd17:ac56:1234:2000::1";
+        String startIPv6 = "fd17:ac56:1234:2000::1";
+        String endIPv6 = null;
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
+    }
+
+    @Test
+    public void validateSharedNetworkIPv6RouterNotInRange() {
+        String routerIPv4 = null;
+        String routerIPv6 = "fd17:ac56:1234:2001::1";
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        boolean passing = true;
+        try {
+            service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, routerIPv4, routerIPv6, null, null, IP6_CIDR, ntwkOff);
+            passing = false;
+        } catch (CloudRuntimeException e) {
+            Assert.assertTrue(e.getMessage().contains("Router IPv6 address provided is not with the network range"));
+        }
+        Assert.assertTrue(passing);
+    }
+
+    @Test
+    public void invalidateSharedNetworkIPv6RouterAddress() {
+        String routerIPv6 = "fd17:ac56:1234:2000::fg";
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        boolean passing = false;
+        try {
+            service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, null, routerIPv6, null, null, IP6_CIDR, ntwkOff);
+        } catch (CloudRuntimeException e) {
+            Assert.assertTrue(e.getMessage().contains("Router IPv6 address provided is of incorrect format"));
+            passing = true;
+        }
+        Assert.assertTrue(passing);
+    }
+
+    @Test
+    public void invalidateSharedNetworkIPv4RouterAddress() {
+        String routerIPv4 = "10.100.1000.1";
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
+        boolean passing = false;
+        try {
+            service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, routerIPv4, null, null, null, IP6_CIDR, ntwkOff);
+        } catch (CloudRuntimeException e) {
+            Assert.assertTrue(e.getMessage().contains("Router IPv4 IP provided is of incorrect format"));
+            passing = true;
+        }
+        Assert.assertTrue(passing);
+    }
+
+    @Test
+    public void checkAndDontSetSourceNatIp() {
+        CreateNetworkCmd cmd = new CreateNetworkCmd();
+        try {
+            service.checkAndSetRouterSourceNatIp(account, cmd, null);
+        } catch (InsufficientAddressCapacityException | ResourceAllocationException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    @Test
+    public void checkAndSetSourceNatIp() {
+        String srcNatIp = "10.100.1000.10000";
+        Long networkOfferingId = 2l;
+        Long zoneId = 3l;
+        registerCallContext();
+        ReflectionTestUtils.setField(createNetworkCmd, "networkOfferingId", networkOfferingId);
+        ReflectionTestUtils.setField(createNetworkCmd, "sourceNatIP", srcNatIp);
+        ReflectionTestUtils.setField(createNetworkCmd, "zoneId", zoneId);
+        ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
+        NetworkVO networkVO = Mockito.spy(NetworkVO.class);
+        IpAddress ipAddress = Mockito.mock(IPAddressVO.class);
+        NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
+        Long networkId = 7l;
+        when(networkVO.getId()).thenReturn(networkId);
+        when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
+        when(networkDao.findById(networkId)).thenReturn(networkVO);
+        when(entityMgr.findById(NetworkOffering.class, networkOfferingId)).thenReturn(ntwkOff);
+        when(entityMgr.findById(eq(DataCenter.class), anyLong())).thenReturn(dc);
+        when(ipAddress.getId()).thenReturn(5l);
+        when(networkVO.getId()).thenReturn(networkId);
+        when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
+        try {
+            when(ipAddressManager.allocateIp(any(), anyBoolean(), any(), anyLong(), any(), any(), eq(srcNatIp))).thenReturn(ipAddress);
+            service.checkAndSetRouterSourceNatIp(account, createNetworkCmd, networkVO);
+        } catch (InsufficientAddressCapacityException | ResourceAllocationException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
 }
diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
index c820026..13cee3a 100644
--- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
@@ -42,8 +42,10 @@
 
 import com.cloud.alert.AlertManager;
 import com.cloud.network.NetworkService;
+import com.cloud.network.dao.FirewallRulesDao;
 import org.apache.cloudstack.acl.SecurityChecker;
 import org.apache.cloudstack.alert.AlertService;
+import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.junit.After;
@@ -149,6 +151,8 @@
     AlertManager alertManager;
     @Mock
     NetworkService networkServiceMock;
+    @Mock
+    FirewallRulesDao firewallDao;
 
     public static final long ACCOUNT_ID = 1;
     private AccountVO account;
@@ -200,6 +204,7 @@
         manager._vpcOffDao = vpcOfferingDao;
         manager._dcDao = dataCenterDao;
         manager._ntwkSvc = networkServiceMock;
+        manager._firewallDao = firewallDao;
         CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
         registerCallContext();
     }
@@ -354,9 +359,10 @@
     }
 
     @Test
-    public void testUpdateVpcNetwork() throws ResourceUnavailableException {
+    public void testUpdateVpcNetwork() throws ResourceUnavailableException, InsufficientCapacityException {
         long vpcId = 1L;
         Integer publicMtu = 1450;
+        String sourceNatIp = "1.2.3.4";
         Account accountMock = Mockito.mock(Account.class);
         VpcVO vpcVO = new VpcVO();
 
@@ -398,9 +404,31 @@
         Mockito.when(networkDao.update(anyLong(), any())).thenReturn(true);
         Mockito.when(vpcDao.update(vpcId, vpcVO)).thenReturn(true);
 
-        manager.updateVpc(vpcId, null, null, null, true, publicMtu);
-        Assert.assertEquals(publicMtu, vpcVO.getPublicMtu());
+        UpdateVPCCmd cmd = Mockito.mock(UpdateVPCCmd.class);
+        Mockito.when(cmd.getId()).thenReturn(vpcId);
+        Mockito.when(cmd.getVpcName()).thenReturn(null);
+        Mockito.when(cmd.getDisplayText()).thenReturn(null);
+        Mockito.when(cmd.getCustomId()).thenReturn(null);
+        Mockito.when(cmd.isDisplayVpc()).thenReturn(true);
+        Mockito.when(cmd.getPublicMtu()).thenReturn(publicMtu);
+        Mockito.when(cmd.getSourceNatIP()).thenReturn(sourceNatIp);
 
+        manager.updateVpc(cmd);
+        Assert.assertEquals(publicMtu, vpcVO.getPublicMtu());
+    }
+
+    @Test
+    public void verifySourceNatIp() {
+        String sourceNatIp = "1.2.3.4";
+        VpcVO vpcVO = Mockito.mock(VpcVO.class); //new VpcVO(1l, "vpc", null, 10l, 1l, 1l, "10.1.0.0/16", null, false, false, false, null, null, null, null);
+        Mockito.when(vpcVO.getId()).thenReturn(1l);
+        IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class);//new IPAddressVO(new Ip(sourceNatIp), 1l, 1l, 1l, true);
+        Mockito.when(ipAddressDao.findByIp(sourceNatIp)).thenReturn(requestedIp);
+        Mockito.when(requestedIp.getVpcId()).thenReturn(1l);
+        Mockito.when(requestedIp.getVpcId()).thenReturn(1l);
+        Mockito.when(firewallDao.countRulesByIpId(1l)).thenReturn(0l);
+        Assert.assertNull(manager.validateSourceNatip(vpcVO, null));
+        Assert.assertEquals(requestedIp, manager.validateSourceNatip(vpcVO, sourceNatIp));
     }
 
     @Test
diff --git a/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java b/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java
index 988d675..4aa50fe 100644
--- a/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java
+++ b/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java
@@ -85,12 +85,12 @@
     }
 
     @Override
-    public Project updateProject(long id, String displayText, String newOwnerName) throws ResourceAllocationException {
+    public Project updateProject(long id, String name, String displayText, String newOwnerName) throws ResourceAllocationException {
         return null;
     }
 
     @Override
-    public Project updateProject(long id, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException {
+    public Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException {
         // TODO Auto-generated method stub
         return null;
     }
diff --git a/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java b/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java
new file mode 100644
index 0000000..90bc2b4
--- /dev/null
+++ b/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java
@@ -0,0 +1,98 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.projects;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+
+import com.cloud.projects.dao.ProjectDao;
+
+
+@RunWith(MockitoJUnitRunner.class)
+public class ProjectManagerImplTest {
+
+    @Spy
+    @InjectMocks
+    ProjectManagerImpl projectManager;
+
+    @Mock
+    ProjectDao projectDao;
+
+    List<ProjectVO> updateProjects;
+
+    @Before
+    public void setUp() throws Exception {
+        updateProjects = new ArrayList<>();
+        Mockito.when(projectDao.update(Mockito.anyLong(), Mockito.any(ProjectVO.class))).thenAnswer((Answer<Boolean>) invocation -> {
+            ProjectVO project = (ProjectVO)invocation.getArguments()[1];
+            updateProjects.add(project);
+            return true;
+        });
+    }
+
+    private void runUpdateProjectNameAndDisplayTextTest(boolean nonNullName, boolean nonNullDisplayText) {
+        ProjectVO projectVO = new ProjectVO();
+        String newName = nonNullName ? "NewName" : null;
+        String newDisplayText = nonNullDisplayText ? "NewDisplayText" : null;
+        projectManager.updateProjectNameAndDisplayText(projectVO, newName, newDisplayText);
+        if (!nonNullName && !nonNullDisplayText) {
+            Assert.assertTrue(updateProjects.isEmpty());
+        } else {
+            Assert.assertFalse(updateProjects.isEmpty());
+            Assert.assertEquals(1, updateProjects.size());
+            ProjectVO updatedProject = updateProjects.get(0);
+            Assert.assertNotNull(updatedProject);
+            if (nonNullName) {
+                Assert.assertEquals(newName, updatedProject.getName());
+            }
+            if (nonNullDisplayText) {
+                Assert.assertEquals(newDisplayText, updatedProject.getDisplayText());
+            }
+        }
+    }
+
+    @Test
+    public void testUpdateProjectNameAndDisplayTextNoUpdate() {
+        runUpdateProjectNameAndDisplayTextTest(false, false);
+    }
+
+    @Test
+    public void testUpdateProjectNameAndDisplayTextUpdateName() {
+        runUpdateProjectNameAndDisplayTextTest(true, false);
+    }
+
+    @Test
+    public void testUpdateProjectNameAndDisplayTextUpdateDisplayText() {
+        runUpdateProjectNameAndDisplayTextTest(false, true);
+    }
+
+    @Test
+    public void testUpdateProjectNameAndDisplayTextUpdateNameDisplayText() {
+        runUpdateProjectNameAndDisplayTextTest(true, true);
+    }
+}
diff --git a/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java b/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
index 4d5b5ba..73d4adf 100755
--- a/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
+++ b/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
@@ -73,6 +73,11 @@
         return null;
     }
 
+    @Override
+    public Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException {
+        return null;
+    }
+
     /* (non-Javadoc)
      * @see com.cloud.resource.ResourceService#cancelMaintenance(com.cloud.api.commands.CancelMaintenanceCmd)
      */
diff --git a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
index cf8df1a..1de5b25 100644
--- a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
+++ b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java
@@ -22,6 +22,35 @@
 import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.annotation.dao.AnnotationDao;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
+import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
+import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
+import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
+import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.userdata.UserDataManager;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+import org.springframework.test.util.ReflectionTestUtils;
+
 import com.cloud.dc.Vlan.VlanType;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.DetailVO;
@@ -49,37 +78,8 @@
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.UserVmDetailVO;
 import com.cloud.vm.UserVmVO;
-import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.UserVmDao;
-
-import org.apache.cloudstack.annotation.dao.AnnotationDao;
-import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
-import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
-import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
-import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
-import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
-import org.apache.cloudstack.context.CallContext;
-import org.apache.cloudstack.framework.config.ConfigKey;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.powermock.reflect.Whitebox;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.cloud.vm.dao.UserVmDetailsDao;
 
 @RunWith(PowerMockRunner.class)
 @PrepareForTest(CallContext.class)
@@ -121,6 +121,9 @@
     @Mock
     UserVmDao _userVmDao;
 
+    @Mock
+    UserDataManager userDataManager;
+
     @Spy
     ManagementServerImpl spy = new ManagementServerImpl();
 
@@ -145,6 +148,7 @@
         spy.annotationDao = annotationDao;
         spy._UserVmDetailsDao = userVmDetailsDao;
         spy._detailsDao = hostDetailsDao;
+        spy.userDataManager = userDataManager;
     }
 
     @After
@@ -304,13 +308,15 @@
         when(callContextMock.getCallingAccount()).thenReturn(account);
         when(_accountMgr.finalizeOwner(nullable(Account.class), nullable(String.class), nullable(Long.class), nullable(Long.class))).thenReturn(account);
 
+        String testUserData = "testUserdata";
         RegisterUserDataCmd cmd = Mockito.mock(RegisterUserDataCmd.class);
-        when(cmd.getUserData()).thenReturn("testUserdata");
+        when(cmd.getUserData()).thenReturn(testUserData);
         when(cmd.getName()).thenReturn("testName");
         when(cmd.getHttpMethod()).thenReturn(BaseCmd.HTTPMethod.GET);
 
         when(_userDataDao.findByName(account.getAccountId(), account.getDomainId(), "testName")).thenReturn(null);
-        when(_userDataDao.findByUserData(account.getAccountId(), account.getDomainId(), "testUserdata")).thenReturn(null);
+        when(_userDataDao.findByUserData(account.getAccountId(), account.getDomainId(), testUserData)).thenReturn(null);
+        when(userDataManager.validateUserData(testUserData,BaseCmd.HTTPMethod.GET)).thenReturn(testUserData);
 
         UserData userData = spy.registerUserData(cmd);
         Assert.assertEquals("testName", userData.getName());
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index 18b4a89..ef00190 100644
--- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
@@ -16,8 +16,6 @@
 // under the License.
 package com.cloud.vm;
 
-import com.cloud.storage.Volume;
-import com.cloud.storage.dao.VolumeDao;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -38,18 +36,16 @@
 import java.util.List;
 import java.util.Map;
 
-import com.cloud.template.VirtualMachineTemplate;
-import com.cloud.user.UserData;
-import com.cloud.user.UserDataVO;
-import com.cloud.user.dao.UserDataDao;
-import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
-import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd;
 import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd;
 import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
 import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
+import org.apache.cloudstack.userdata.UserDataManager;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -67,11 +63,19 @@
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.deploy.DataCenterDeployment;
+import com.cloud.deploy.DeployDestination;
+import com.cloud.deploy.DeploymentPlanner;
+import com.cloud.deploy.DeploymentPlanningManager;
 import com.cloud.exception.InsufficientAddressCapacityException;
 import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InsufficientServerCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor;
 import com.cloud.network.NetworkModel;
 import com.cloud.network.dao.NetworkDao;
@@ -81,21 +85,30 @@
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.ScopeType;
 import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeApiService;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.template.VirtualMachineTemplate;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.AccountService;
 import com.cloud.user.AccountVO;
 import com.cloud.user.ResourceLimitService;
+import com.cloud.user.UserData;
+import com.cloud.user.UserDataVO;
 import com.cloud.user.UserVO;
 import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDataDao;
 import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
 import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.dao.NicDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
@@ -182,6 +195,18 @@
     UserDataDao userDataDao;
 
     @Mock
+    PrimaryDataStoreDao primaryDataStoreDao;
+
+    @Mock
+    VirtualMachineManager virtualMachineManager;
+
+    @Mock
+    DeploymentPlanningManager planningManager;
+
+    @Mock
+    HostDao hostDao;
+
+    @Mock
     private VolumeVO volumeVOMock;
 
     @Mock
@@ -193,6 +218,9 @@
     @Mock
     private ServiceOfferingVO serviceOffering;
 
+    @Mock
+    UserDataManager userDataManager;
+
     private static final long vmId = 1l;
     private static final long zoneId = 2L;
     private static final long accountId = 3L;
@@ -690,29 +718,6 @@
     }
 
     @Test
-    public void testUserDataAppend() {
-        String userData = "testUserdata";
-        String templateUserData = "testTemplateUserdata";
-        Long userDataId = 1L;
-
-        VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
-        when(template.getUserDataId()).thenReturn(2L);
-        when(template.getUserDataOverridePolicy()).thenReturn(UserData.UserDataOverridePolicy.APPEND);
-
-        UserDataVO templateUserDataVO = Mockito.mock(UserDataVO.class);
-        doReturn(templateUserDataVO).when(userDataDao).findById(2L);
-        when(templateUserDataVO.getUserData()).thenReturn(templateUserData);
-
-        UserDataVO apiUserDataVO = Mockito.mock(UserDataVO.class);
-        doReturn(apiUserDataVO).when(userDataDao).findById(userDataId);
-        when(apiUserDataVO.getUserData()).thenReturn(userData);
-
-        String finalUserdata = userVmManagerImpl.finalizeUserData(null, userDataId, template);
-
-        Assert.assertEquals(finalUserdata, templateUserData+userData);
-    }
-
-    @Test
     public void testUserDataWithoutTemplate() {
         String userData = "testUserdata";
         Long userDataId = 1L;
@@ -831,10 +836,13 @@
         when(templateDao.findByIdIncludingRemoved(2L)).thenReturn(template);
         when(template.getUserDataId()).thenReturn(null);
 
-        when(cmd.getUserData()).thenReturn("testUserdata");
+        String testUserData = "testUserdata";
+        when(cmd.getUserData()).thenReturn(testUserData);
         when(cmd.getUserdataId()).thenReturn(null);
         when(cmd.getHttpMethod()).thenReturn(HTTPMethod.GET);
 
+        when(userDataManager.validateUserData(testUserData, HTTPMethod.GET)).thenReturn(testUserData);
+
         try {
             doNothing().when(userVmManagerImpl).updateUserData(userVmVO);
             userVmManagerImpl.resetVMUserData(cmd);
@@ -868,12 +876,15 @@
         when(templateDao.findByIdIncludingRemoved(2L)).thenReturn(template);
         when(template.getUserDataId()).thenReturn(null);
 
+        String testUserData = "testUserdata";
         when(cmd.getUserdataId()).thenReturn(1L);
         UserDataVO apiUserDataVO = Mockito.mock(UserDataVO.class);
         when(userDataDao.findById(1L)).thenReturn(apiUserDataVO);
-        when(apiUserDataVO.getUserData()).thenReturn("testUserdata");
+        when(apiUserDataVO.getUserData()).thenReturn(testUserData);
         when(cmd.getHttpMethod()).thenReturn(HTTPMethod.GET);
 
+        when(userDataManager.validateUserData(testUserData, HTTPMethod.GET)).thenReturn(testUserData);
+
         try {
             doNothing().when(userVmManagerImpl).updateUserData(userVmVO);
             userVmManagerImpl.resetVMUserData(cmd);
@@ -912,4 +923,122 @@
 
         userVmManagerImpl.createVirtualMachine(deployVMCmd);
     }
+
+    private List<VolumeVO> mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(int localVolumes, int nonLocalVolumes) {
+        List<VolumeVO> volumes = new ArrayList<>();
+        for (int i=0; i< localVolumes + nonLocalVolumes; ++i) {
+            VolumeVO vol = Mockito.mock(VolumeVO.class);
+            long index = i + 1;
+            Mockito.when(vol.getDiskOfferingId()).thenReturn(index);
+            Mockito.when(vol.getPoolId()).thenReturn(index);
+            DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
+            Mockito.when(diskOfferingDao.findById(index)).thenReturn(diskOffering);
+            StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
+            Mockito.when(primaryDataStoreDao.findById(index)).thenReturn(storagePool);
+            if (i < localVolumes) {
+                if ((localVolumes + nonLocalVolumes) % 2 == 0) {
+                    Mockito.when(diskOffering.isUseLocalStorage()).thenReturn(true);
+                } else {
+
+                    Mockito.when(diskOffering.isUseLocalStorage()).thenReturn(false);
+                    Mockito.when(storagePool.isLocal()).thenReturn(true);
+                }
+            } else {
+                Mockito.when(diskOffering.isUseLocalStorage()).thenReturn(false);
+                Mockito.when(storagePool.isLocal()).thenReturn(false);
+            }
+            volumes.add(vol);
+        }
+        return volumes;
+    }
+
+    @Test
+    public void testIsAnyVmVolumeUsingLocalStorage() {
+        Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 0)));
+        Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(2, 0)));
+        Assert.assertTrue(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(1, 1)));
+        Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 2)));
+        Assert.assertFalse(userVmManagerImpl.isAnyVmVolumeUsingLocalStorage(mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(0, 0)));
+    }
+
+    private List<VolumeVO> mockVolumesForIsAllVmVolumesOnZoneWideStore(int nullPoolIdVolumes, int nullPoolVolumes, int zoneVolumes, int nonZoneVolumes) {
+        List<VolumeVO> volumes = new ArrayList<>();
+        for (int i=0; i< nullPoolIdVolumes + nullPoolVolumes + zoneVolumes + nonZoneVolumes; ++i) {
+            VolumeVO vol = Mockito.mock(VolumeVO.class);
+            volumes.add(vol);
+            if (i < nullPoolIdVolumes) {
+                Mockito.when(vol.getPoolId()).thenReturn(null);
+                continue;
+            }
+            long index = i + 1;
+            Mockito.when(vol.getPoolId()).thenReturn(index);
+            if (i < nullPoolVolumes) {
+                Mockito.when(primaryDataStoreDao.findById(index)).thenReturn(null);
+                continue;
+            }
+            StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
+            Mockito.when(primaryDataStoreDao.findById(index)).thenReturn(storagePool);
+            if (i < zoneVolumes) {
+                Mockito.when(storagePool.getScope()).thenReturn(ScopeType.ZONE);
+            } else {
+                Mockito.when(storagePool.getScope()).thenReturn(ScopeType.CLUSTER);
+            }
+        }
+        return volumes;
+    }
+
+    @Test
+    public void testIsAllVmVolumesOnZoneWideStoreCombinations() {
+        Assert.assertTrue(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(0, 0, 1, 0)));
+        Assert.assertTrue(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(0, 0, 2, 0)));
+        Assert.assertFalse(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(0, 0, 1, 1)));
+        Assert.assertFalse(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(0, 0, 0, 0)));
+        Assert.assertFalse(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(1, 0, 1, 1)));
+        Assert.assertFalse(userVmManagerImpl.isAllVmVolumesOnZoneWideStore(mockVolumesForIsAllVmVolumesOnZoneWideStore(0, 1, 1, 1)));
+    }
+
+    private Pair<VMInstanceVO, Host> mockObjectsForChooseVmMigrationDestinationUsingVolumePoolMapTest(boolean nullPlan, Host destinationHost) {
+        VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
+        Mockito.when(vm.getId()).thenReturn(1L);
+        Mockito.when(vm.getServiceOfferingId()).thenReturn(1L);
+        Host host = Mockito.mock(Host.class);
+        Mockito.when(host.getId()).thenReturn(1L);
+        Mockito.when(hostDao.findById(1L)).thenReturn(Mockito.mock(HostVO.class));
+        Mockito.when(virtualMachineManager.getMigrationDeployment(Mockito.any(VirtualMachine.class),
+                        Mockito.any(Host.class), Mockito.nullable(Long.class),
+                        Mockito.any(DeploymentPlanner.ExcludeList.class)))
+                .thenReturn(Mockito.mock(DataCenterDeployment.class));
+        if (!nullPlan) {
+            try {
+                DeployDestination destination = Mockito.mock(DeployDestination.class);
+                Mockito.when(destination.getHost()).thenReturn(destinationHost);
+                Mockito.when(planningManager.planDeployment(Mockito.any(VirtualMachineProfile.class),
+                                Mockito.any(DataCenterDeployment.class), Mockito.any(DeploymentPlanner.ExcludeList.class),
+                                Mockito.nullable(DeploymentPlanner.class)))
+                        .thenReturn(destination);
+            } catch (InsufficientServerCapacityException e) {
+                Assert.fail("Failed to mock DeployDestination");
+            }
+        }
+        return new Pair<>(vm, host);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testChooseVmMigrationDestinationUsingVolumePoolMapNullDestination() {
+        Pair<VMInstanceVO, Host> pair = mockObjectsForChooseVmMigrationDestinationUsingVolumePoolMapTest(true, null);
+        userVmManagerImpl.chooseVmMigrationDestinationUsingVolumePoolMap(pair.first(), pair.second(), null);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testChooseVmMigrationDestinationUsingVolumePoolMapNullHost() {
+        Pair<VMInstanceVO, Host> pair = mockObjectsForChooseVmMigrationDestinationUsingVolumePoolMapTest(false, null);
+        userVmManagerImpl.chooseVmMigrationDestinationUsingVolumePoolMap(pair.first(), pair.second(), null);
+    }
+
+    @Test
+    public void testChooseVmMigrationDestinationUsingVolumePoolMapValid() {
+        Host destinationHost = Mockito.mock(Host.class);
+        Pair<VMInstanceVO, Host> pair = mockObjectsForChooseVmMigrationDestinationUsingVolumePoolMapTest(false, destinationHost);
+        Assert.assertEquals(destinationHost, userVmManagerImpl.chooseVmMigrationDestinationUsingVolumePoolMap(pair.first(), pair.second(), null));
+    }
 }
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerTest.java
index 7cc2c8a..a0ad321 100644
--- a/server/src/test/java/com/cloud/vm/UserVmManagerTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerTest.java
@@ -18,7 +18,6 @@
 package com.cloud.vm;
 
 import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -38,7 +37,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -48,7 +46,6 @@
 
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
-import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
 import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
@@ -841,26 +838,4 @@
         _userVmMgr.persistDeviceBusInfo(_vmMock, "lsilogic");
         verify(_vmDao, times(1)).saveDetails(any(UserVmVO.class));
     }
-
-    @Test
-    public void testValideBase64WithoutPadding() {
-        // fo should be encoded in base64 either as Zm8 or Zm8=
-        String encodedUserdata = "Zm8";
-        String encodedUserdataWithPadding = "Zm8=";
-
-        // Verify that we accept both but return the padded version
-        assertTrue("validate return the value with padding", encodedUserdataWithPadding.equals(_userVmMgr.validateUserData(encodedUserdata, BaseCmd.HTTPMethod.GET)));
-        assertTrue("validate return the value with padding", encodedUserdataWithPadding.equals(_userVmMgr.validateUserData(encodedUserdataWithPadding, BaseCmd.HTTPMethod.GET)));
-    }
-
-    @Test
-    public void testValidateUrlEncodedBase64() throws UnsupportedEncodingException {
-        // fo should be encoded in base64 either as Zm8 or Zm8=
-        String encodedUserdata = "Zm+8/w8=";
-        String urlEncodedUserdata = java.net.URLEncoder.encode(encodedUserdata, "UTF-8");
-
-        // Verify that we accept both but return the padded version
-        assertEquals("validate return the value with padding", encodedUserdata, _userVmMgr.validateUserData(encodedUserdata, BaseCmd.HTTPMethod.GET));
-        assertEquals("validate return the value with padding", encodedUserdata, _userVmMgr.validateUserData(urlEncodedUserdata, BaseCmd.HTTPMethod.GET));
-    }
 }
diff --git a/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
index 049bc17..9f0d40a 100644
--- a/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
+++ b/server/src/test/java/org/apache/cloudstack/acl/RoleManagerImplTest.java
@@ -166,7 +166,7 @@
         String roleName = "roleName";
         List<Role> roles = new ArrayList<>();
         Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 0);
-        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByName(roleName, null, null, null);
+        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByName(roleName, null, null, null, false);
 
         roleManagerImpl.findRolesByName(roleName);
         Mockito.verify(roleManagerImpl).removeRootAdminRolesIfNeeded(roles);
@@ -239,7 +239,7 @@
 
         Assert.assertEquals(0, returnedRoles.size());
         Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
-        Mockito.verify(roleDaoMock, Mockito.times(0)).findAllByRoleType(Mockito.any(RoleType.class));
+        Mockito.verify(roleDaoMock, Mockito.times(0)).findAllByRoleType(Mockito.any(RoleType.class), Mockito.anyBoolean());
     }
 
     @Test
@@ -250,11 +250,11 @@
         List<Role> roles = new ArrayList<>();
         roles.add(Mockito.mock(Role.class));
         Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 1);
-        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.Admin, null, null);
+        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.Admin, null, null, true);
         List<Role> returnedRoles = roleManagerImpl.findRolesByType(RoleType.Admin);
 
         Assert.assertEquals(1, returnedRoles.size());
-        Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
+        Mockito.verify(accountManagerMock, Mockito.times(2)).isRootAdmin(Mockito.anyLong());
     }
 
     @Test
@@ -265,11 +265,11 @@
         List<Role> roles = new ArrayList<>();
         roles.add(Mockito.mock(Role.class));
         Pair<ArrayList<RoleVO>, Integer> toBeReturned = new Pair(roles, 1);
-        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.User, null, null);
+        Mockito.doReturn(toBeReturned).when(roleDaoMock).findAllByRoleType(RoleType.User, null, null, true);
         List<Role> returnedRoles = roleManagerImpl.findRolesByType(RoleType.User);
 
         Assert.assertEquals(1, returnedRoles.size());
-        Mockito.verify(accountManagerMock, Mockito.times(0)).isRootAdmin(Mockito.anyLong());
+        Mockito.verify(accountManagerMock, Mockito.times(1)).isRootAdmin(Mockito.anyLong());
     }
 
     @Test
diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
index 077518c..ce95f4d 100644
--- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
+++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java
@@ -16,9 +16,19 @@
 // under the License.
 package org.apache.cloudstack.backup;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeApiService;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.fsm.NoTransitionException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineManager;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
 import org.apache.cloudstack.backup.dao.BackupOfferingDao;
@@ -33,6 +43,8 @@
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.utils.Pair;
 
+import java.util.Collections;
+
 public class BackupManagerTest {
     @Spy
     @InjectMocks
@@ -44,6 +56,15 @@
     @Mock
     BackupProvider backupProvider;
 
+    @Mock
+    VirtualMachineManager virtualMachineManager;
+
+    @Mock
+    VolumeApiService volumeApiService;
+
+    @Mock
+    VolumeDao volumeDao;
+
     private String[] hostPossibleValues = {"127.0.0.1", "hostname"};
     private String[] datastoresPossibleValues = {"e9804933-8609-4de3-bccc-6278072a496c", "datastore-name"};
 
@@ -189,4 +210,50 @@
         Mockito.verify(backupProvider, times(4)).restoreBackedUpVolume(Mockito.any(), Mockito.anyString(),
                 Mockito.anyString(), Mockito.anyString());
     }
+
+    @Test
+    public void tryRestoreVMTestRestoreSucceeded() throws NoTransitionException {
+        BackupOffering offering = Mockito.mock(BackupOffering.class);
+        VolumeVO volumeVO = Mockito.mock(VolumeVO.class);
+        VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
+        BackupVO backup = Mockito.mock(BackupVO.class);
+
+        Mockito.when(volumeDao.findIncludingRemovedByInstanceAndType(1L, null)).thenReturn(Collections.singletonList(volumeVO));
+        Mockito.when(virtualMachineManager.stateTransitTo(Mockito.eq(vm), Mockito.eq(VirtualMachine.Event.RestoringRequested), Mockito.any())).thenReturn(true);
+        Mockito.when(volumeApiService.stateTransitTo(Mockito.eq(volumeVO), Mockito.eq(Volume.Event.RestoreRequested))).thenReturn(true);
+
+
+
+        Mockito.when(vm.getId()).thenReturn(1L);
+        Mockito.when(offering.getProvider()).thenReturn("veeam");
+        Mockito.doReturn(backupProvider).when(backupManager).getBackupProvider("veeam");
+        Mockito.when(backupProvider.restoreVMFromBackup(vm, backup)).thenReturn(true);
+
+        backupManager.tryRestoreVM(backup, vm, offering, "Nothing to write here.");
+    }
+
+    @Test
+    public void tryRestoreVMTestRestoreFails() throws NoTransitionException {
+        BackupOffering offering = Mockito.mock(BackupOffering.class);
+        VolumeVO volumeVO = Mockito.mock(VolumeVO.class);
+        VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
+        BackupVO backup = Mockito.mock(BackupVO.class);
+
+        Mockito.when(volumeDao.findIncludingRemovedByInstanceAndType(1L, null)).thenReturn(Collections.singletonList(volumeVO));
+        Mockito.when(virtualMachineManager.stateTransitTo(Mockito.eq(vm), Mockito.eq(VirtualMachine.Event.RestoringRequested), Mockito.any())).thenReturn(true);
+        Mockito.when(volumeApiService.stateTransitTo(Mockito.eq(volumeVO), Mockito.eq(Volume.Event.RestoreRequested))).thenReturn(true);
+        Mockito.when(virtualMachineManager.stateTransitTo(Mockito.eq(vm), Mockito.eq(VirtualMachine.Event.RestoringFailed), Mockito.any())).thenReturn(true);
+        Mockito.when(volumeApiService.stateTransitTo(Mockito.eq(volumeVO), Mockito.eq(Volume.Event.RestoreFailed))).thenReturn(true);
+
+        Mockito.when(vm.getId()).thenReturn(1L);
+        Mockito.when(offering.getProvider()).thenReturn("veeam");
+        Mockito.doReturn(backupProvider).when(backupManager).getBackupProvider("veeam");
+        Mockito.when(backupProvider.restoreVMFromBackup(vm, backup)).thenReturn(false);
+        try {
+            backupManager.tryRestoreVM(backup, vm, offering, "Checking message error.");
+            fail("An exception is needed.");
+        } catch (CloudRuntimeException e) {
+            assertEquals("Error restoring VM from backup [Checking message error.].", e.getMessage());
+        }
+    }
 }
diff --git a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
index 72bd7bb..a37b3af 100644
--- a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
+++ b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java
@@ -249,5 +249,4 @@
         // System.out.println("Creating Vpc Network Offering");
         assertNotNull("Vpc Isolated network offering with Vpc and Netscaler provider ", off);
     }
-
 }
diff --git a/server/src/test/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImplTest.java
new file mode 100644
index 0000000..3d21be5
--- /dev/null
+++ b/server/src/test/java/org/apache/cloudstack/vm/schedule/VMScheduleManagerImplTest.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm.schedule;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.User;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.vm.UserVmManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.VMScheduleResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import static org.junit.Assert.assertNotNull;
+
+public class VMScheduleManagerImplTest {
+
+    @Spy
+    @InjectMocks
+    VMScheduleManagerImpl vmScheduleManager = new VMScheduleManagerImpl();
+
+    @Mock
+    VMScheduleDao vmScheduleDao;
+
+    @Mock
+    VMScheduler vmScheduler;
+
+    @Mock
+    UserVmManager userVmManager;
+
+    @Mock
+    AccountManager accountManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Account callingAccount = Mockito.mock(Account.class);
+        User callingUser = Mockito.mock(User.class);
+        CallContext.register(callingUser, callingAccount);
+    }
+
+    private void validateResponse(VMScheduleResponse response, VMSchedule vmSchedule, VirtualMachine vm) {
+        assertNotNull(response);
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "id"), vmSchedule.getUuid());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "vmId"), vm.getUuid());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "schedule"), vmSchedule.getSchedule());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "timeZone"), vmSchedule.getTimeZone());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "action"), vmSchedule.getAction());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "startDate"), vmSchedule.getStartDate());
+        Assert.assertEquals(ReflectionTestUtils.getField(response, "endDate"), vmSchedule.getEndDate());
+    }
+
+    @Test
+    public void createSchedule() {
+        UserVm vm = Mockito.mock(UserVm.class);
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        CreateVMScheduleCmd cmd = Mockito.mock(CreateVMScheduleCmd.class);
+
+        Mockito.when(cmd.getVmId()).thenReturn(1L);
+        Mockito.when(cmd.getSchedule()).thenReturn("0 0 * * *");
+        Mockito.when(cmd.getTimeZone()).thenReturn("UTC");
+        Mockito.when(cmd.getAction()).thenReturn("start");
+        Mockito.when(cmd.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
+        Mockito.when(cmd.getEndDate()).thenReturn(DateUtils.addDays(new Date(), 2));
+        Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(vmScheduleDao.persist(Mockito.any(VMScheduleVO.class))).thenReturn(vmSchedule);
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+        Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.isNull(), Mockito.eq(false), Mockito.eq(vm));
+        VMScheduleResponse response = vmScheduleManager.createSchedule(cmd);
+        Mockito.verify(vmScheduleDao, Mockito.times(1)).persist(Mockito.any(VMScheduleVO.class));
+
+        validateResponse(response, vmSchedule, vm);
+    }
+
+    @Test
+    public void createResponse() {
+        VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
+        UserVm vm = Mockito.mock(UserVm.class);
+        Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
+        Mockito.when(userVmManager.getUserVm(vmSchedule.getVmId())).thenReturn(vm);
+
+        VMScheduleResponse response = vmScheduleManager.createResponse(vmSchedule);
+        validateResponse(response, vmSchedule, vm);
+    }
+
+    @Test
+    public void listSchedule() {
+        UserVm vm = Mockito.mock(UserVm.class);
+        VMScheduleVO vmSchedule1 = Mockito.mock(VMScheduleVO.class);
+        VMScheduleVO vmSchedule2 = Mockito.mock(VMScheduleVO.class);
+        List<VMScheduleVO> vmScheduleList = new ArrayList<>();
+        vmScheduleList.add(vmSchedule1);
+        vmScheduleList.add(vmSchedule2);
+
+        Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm);
+        Mockito.when(
+                vmScheduleDao.searchAndCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
+                        Mockito.anyBoolean(), Mockito.anyLong(), Mockito.anyLong())
+        ).thenReturn(new Pair<>(vmScheduleList, vmScheduleList.size()));
+        Mockito.when(vmSchedule1.getVmId()).thenReturn(1L);
+        Mockito.when(vmSchedule2.getVmId()).thenReturn(1L);
+
+        ListVMScheduleCmd cmd = Mockito.mock(ListVMScheduleCmd.class);
+        Mockito.when(cmd.getVmId()).thenReturn(1L);
+
+        ListResponse<VMScheduleResponse> responseList = vmScheduleManager.listSchedule(cmd);
+        Mockito.verify(vmScheduleDao, Mockito.times(1)).searchAndCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
+                Mockito.anyBoolean(), Mockito.anyLong(), Mockito.anyLong());
+        assertNotNull(responseList);
+        Assert.assertEquals(2, (int) responseList.getCount());
+        Assert.assertEquals(2, responseList.getResponses().size());
+
+        for (int i = 0; i < responseList.getResponses().size(); i++) {
+            VMScheduleResponse response = responseList.getResponses().get(i);
+            VMScheduleVO vmSchedule = vmScheduleList.get(i);
+            validateResponse(response, vmSchedule, vm);
+        }
+    }
+
+    @Test
+    public void updateSchedule() {
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        UpdateVMScheduleCmd cmd = Mockito.mock(UpdateVMScheduleCmd.class);
+        UserVm vm = Mockito.mock(UserVm.class);
+        Mockito.when(cmd.getId()).thenReturn(1L);
+        Mockito.when(cmd.getSchedule()).thenReturn("0 0 * * *");
+        Mockito.when(cmd.getTimeZone()).thenReturn("UTC");
+        Mockito.when(cmd.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
+        Mockito.when(cmd.getEndDate()).thenReturn(DateUtils.addDays(new Date(), 2));
+        Mockito.when(vmScheduleDao.findById(Mockito.anyLong())).thenReturn(vmSchedule);
+        Mockito.when(vmScheduleDao.update(Mockito.eq(cmd.getId()), Mockito.any(VMScheduleVO.class))).thenReturn(true);
+        Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
+        Mockito.when(userVmManager.getUserVm(vmSchedule.getVmId())).thenReturn(vm);
+
+        VMScheduleResponse response = vmScheduleManager.updateSchedule(cmd);
+        Mockito.verify(vmScheduleDao, Mockito.times(1)).update(Mockito.eq(cmd.getId()), Mockito.any(VMScheduleVO.class));
+
+        validateResponse(response, vmSchedule, vm);
+    }
+
+    @Test
+    public void removeScheduleByVmId() {
+        UserVm vm = Mockito.mock(UserVm.class);
+        VMScheduleVO vmSchedule1 = Mockito.mock(VMScheduleVO.class);
+        VMScheduleVO vmSchedule2 = Mockito.mock(VMScheduleVO.class);
+        List<VMScheduleVO> vmScheduleList = new ArrayList<>();
+        vmScheduleList.add(vmSchedule1);
+        vmScheduleList.add(vmSchedule2);
+        SearchCriteria<VMScheduleVO> sc = Mockito.mock(SearchCriteria.class);
+
+        Mockito.when(vm.getId()).thenReturn(1L);
+        Mockito.when(vmScheduleDao.getSearchCriteriaForVMId(vm.getId())).thenReturn(sc);
+        Mockito.when(vmScheduleDao.search(sc, null)).thenReturn(vmScheduleList);
+        Mockito.when(vmSchedule1.getId()).thenReturn(1L);
+        Mockito.when(vmSchedule2.getId()).thenReturn(2L);
+        Mockito.when(vmScheduleDao.remove(sc)).thenReturn(2);
+
+        long rowsRemoved = vmScheduleManager.removeScheduleByVmId(vm.getId(), false);
+
+        Mockito.verify(vmScheduleDao, Mockito.times(1)).remove(sc);
+        Assert.assertEquals(2, rowsRemoved);
+    }
+
+    @Test
+    public void removeSchedule() {
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        UserVm vm = Mockito.mock(UserVm.class);
+        DeleteVMScheduleCmd cmd = Mockito.mock(DeleteVMScheduleCmd.class);
+
+        Mockito.when(cmd.getId()).thenReturn(1L);
+        Mockito.when(cmd.getVmId()).thenReturn(1L);
+        Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
+        Mockito.when(userVmManager.getUserVm(cmd.getVmId())).thenReturn(vm);
+        Mockito.when(vmScheduleDao.findById(Mockito.anyLong())).thenReturn(vmSchedule);
+        Mockito.when(vmScheduleDao.removeSchedulesForVmIdAndIds(Mockito.anyLong(), Mockito.anyList())).thenReturn(1L);
+
+        Long rowsRemoved = vmScheduleManager.removeSchedule(cmd);
+
+        Mockito.verify(vmScheduleDao, Mockito.times(1)).removeSchedulesForVmIdAndIds(Mockito.anyLong(), Mockito.anyList());
+        Assert.assertEquals(1L, (long) rowsRemoved);
+    }
+
+    @Test
+    public void validateStartDateEndDate() {
+        // Valid scenario 1
+        // Start date is before end date
+        Date startDate = DateUtils.addDays(new Date(), 1);
+        Date endDate = DateUtils.addDays(new Date(), 2);
+        vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
+
+        // Valid Scenario 2
+        // Start date is before current date and end date is null
+        endDate = null;
+        vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
+
+        // Invalid Scenario 2
+        // Start date is before current date
+        startDate = DateUtils.addDays(new Date(), -1);
+        try {
+            vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
+            Assert.fail("Should have thrown InvalidParameterValueException");
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage().contains("Invalid value for start date. Start date") &&
+                    e.getMessage().contains("can't be before current time"));
+        }
+
+        // Invalid Scenario 2
+        // Start date is after end date
+        startDate = DateUtils.addDays(new Date(), 2);
+        endDate = DateUtils.addDays(new Date(), 1);
+        try {
+            vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
+            Assert.fail("Should have thrown InvalidParameterValueException");
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
+                    e.getMessage().contains("can't be before start date"));
+        }
+
+        // Invalid Scenario 3
+        // End date is before current date
+        startDate = DateUtils.addDays(new Date(), 1);
+        endDate = DateUtils.addDays(new Date(), -1);
+        try {
+            vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
+            Assert.fail("Should have thrown InvalidParameterValueException");
+        } catch (InvalidParameterValueException e) {
+            Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
+                    e.getMessage().contains("can't be before current time"));
+        }
+    }
+}
diff --git a/server/src/test/java/org/apache/cloudstack/vm/schedule/VMSchedulerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/schedule/VMSchedulerImplTest.java
new file mode 100644
index 0000000..bb622f5
--- /dev/null
+++ b/server/src/test/java/org/apache/cloudstack/vm/schedule/VMSchedulerImplTest.java
@@ -0,0 +1,397 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES 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.vm.schedule;
+
+import com.cloud.event.ActionEventUtils;
+import com.cloud.user.User;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.component.ComponentContext;
+import com.cloud.vm.UserVmManager;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.api.ApiCommandResourceType;
+import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
+import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
+import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
+import org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDao;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import javax.persistence.EntityExistsException;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ActionEventUtils.class, ComponentContext.class})
+public class VMSchedulerImplTest {
+    @Spy
+    @InjectMocks
+    private VMSchedulerImpl vmScheduler = new VMSchedulerImpl();
+
+    @Mock
+    private UserVmManager userVmManager;
+
+    @Mock
+    private VMScheduleDao vmScheduleDao;
+
+    @Mock
+    private VMScheduledJobDao vmScheduledJobDao;
+
+    @Mock
+    private EnumMap<VMSchedule.Action, String> actionEventMap;
+
+    @Mock
+    private AsyncJobManager asyncJobManager;
+
+    @Mock
+    private AsyncJobDispatcher asyncJobDispatcher;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        PowerMockito.mockStatic(ActionEventUtils.class);
+        Mockito.when(ActionEventUtils.onScheduledActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(),
+                Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyLong())).thenReturn(1L);
+        Mockito.when(ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
+                Mockito.anyString(), Mockito.anyBoolean(),
+                Mockito.anyString(),
+                Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L);
+    }
+
+    private void prepareMocksForProcessJob(VirtualMachine vm, VMScheduledJob vmScheduledJob, VirtualMachine.State vmState, VMSchedule.Action action, Long executeJobReturnValue) {
+        Mockito.when(vm.getState()).thenReturn(vmState);
+        Mockito.when(vmScheduledJob.getAction()).thenReturn(action);
+
+        if (executeJobReturnValue != null) {
+            Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeStartVMJob(Mockito.any(VirtualMachine.class), Mockito.anyLong());
+            Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeStopVMJob(Mockito.any(VirtualMachine.class), Mockito.anyBoolean(), Mockito.anyLong());
+            Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeRebootVMJob(Mockito.any(VirtualMachine.class), Mockito.anyBoolean(), Mockito.anyLong());
+        }
+    }
+
+    private void executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State state, VMSchedule.Action action) {
+        VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+
+        Long expectedValue = 1L;
+
+        prepareMocksForProcessJob(vm, vmScheduledJob, state, action, expectedValue);
+
+        Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
+
+        PowerMockito.verifyStatic(ActionEventUtils.class);
+        ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), null,
+                actionEventMap.get(action), true,
+                String.format("Executing action (%s) for VM Id:%s", vmScheduledJob.getAction(), vm.getUuid()),
+                vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0);
+        Assert.assertEquals(expectedValue, jobId);
+    }
+
+    @Test
+    public void testProcessJobRunning() {
+        executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.STOP);
+        executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.FORCE_STOP);
+        executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.REBOOT);
+        executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.FORCE_REBOOT);
+        executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Stopped, VMSchedule.Action.START);
+    }
+
+    @Test
+    public void testProcessJobInvalidAction() {
+        VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+
+        prepareMocksForProcessJob(vm, vmScheduledJob, VirtualMachine.State.Running, VMSchedule.Action.START, null);
+
+        Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
+
+        Assert.assertNull(jobId);
+    }
+
+    @Test
+    public void testProcessJobVMInInvalidState() {
+        VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+
+        prepareMocksForProcessJob(vm, vmScheduledJob, VirtualMachine.State.Unknown, VMSchedule.Action.START, null);
+
+        Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
+
+        Assert.assertNull(jobId);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleScheduleExists() {
+        Date now = DateUtils.setSeconds(new Date(), 0);
+        Date startDate = DateUtils.addDays(now, 1);
+        Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
+        UserVm vm = Mockito.mock(UserVm.class);
+
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
+        Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+        Mockito.when(vmScheduledJobDao.persist(Mockito.any())).thenThrow(EntityExistsException.class);
+        Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule);
+
+        Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleFutureSchedule() {
+        Date now = DateUtils.setSeconds(new Date(), 0);
+        Date startDate = DateUtils.addDays(now, 1);
+        Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
+        UserVm vm = Mockito.mock(UserVm.class);
+
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
+        Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+        Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule);
+
+        Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleFutureScheduleWithTimeZoneChecks() throws Exception {
+        // Ensure that Date vmSchedulerImpl.scheduleNextJob(VMScheduleVO vmSchedule) generates
+        // the correct scheduled time on basis of schedule(cron), start date
+        // and the timezone of the user. The system running the test can have any timezone.
+        String cron = "30 5 * * *";
+
+        Date now = DateUtils.setSeconds(new Date(), 0);
+        Date startDate = DateUtils.addDays(now, 1);
+
+        UserVm vm = Mockito.mock(UserVm.class);
+
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Mockito.when(vmSchedule.getSchedule()).thenReturn(cron);
+        Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+
+        // The timezone of the user is EST. The cron expression is 30 5 * * *.
+        // The start date is 1 day from now. The expected scheduled time is 5:30 AM EST.
+        // The actual scheduled time is 10:30 AM UTC.
+        // The actual scheduled time is 5:30 AM EST.
+        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(startDate.toInstant(), vmSchedule.getTimeZoneId());
+        zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
+        Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
+
+        if (expectedScheduledTime.before(startDate)) {
+            expectedScheduledTime = DateUtils.addDays(expectedScheduledTime, 1);
+        }
+
+        Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule);
+        Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleCurrentSchedule() {
+        Date now = DateUtils.setSeconds(new Date(), 0);
+        Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(now, 1), Calendar.MINUTE);
+        UserVm vm = Mockito.mock(UserVm.class);
+
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
+        Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+        Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule);
+
+        Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleCurrentScheduleWithTimeZoneChecks() throws Exception {
+        // Ensure that Date vmSchedulerImpl.scheduleNextJob(VMScheduleVO vmSchedule) generates
+        // the correct scheduled time on basis of schedule(cron), start date
+        // and the timezone of the user. The system running the test can have any timezone.
+        String cron = "30 5 * * *";
+
+        Date now = DateUtils.setSeconds(new Date(), 0);
+
+        UserVm vm = Mockito.mock(UserVm.class);
+
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Mockito.when(vmSchedule.getSchedule()).thenReturn(cron);
+        Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
+        Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
+        Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
+
+        // The timezone of the user is EST. The cron expression is 30 5 * * *.
+        // The start date is 1 day ago. The expected scheduled time is 5:30 AM EST.
+        // The actual scheduled time is 10:30 AM UTC.
+        // The actual scheduled time is 5:30 AM EST.
+        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now.toInstant(), vmSchedule.getTimeZoneId());
+        zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
+        Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
+
+        if (expectedScheduledTime.before(now)) {
+            expectedScheduledTime = DateUtils.addDays(expectedScheduledTime, 1);
+        }
+
+        Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule);
+        Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleExpired() {
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEndDate()).thenReturn(DateUtils.addMinutes(new Date(), -5));
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
+        Date actualDate = vmScheduler.scheduleNextJob(vmSchedule);
+        Assert.assertNull(actualDate);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleDisabled() {
+        VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
+        Mockito.when(vmSchedule.getEnabled()).thenReturn(false);
+        Date actualDate = vmScheduler.scheduleNextJob(vmSchedule);
+        Assert.assertNull(actualDate);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleIdNotExists() {
+        long vmScheduleId = 1;
+        Mockito.when(vmScheduleDao.findById(vmScheduleId)).thenReturn(null);
+        Date actualDate = vmScheduler.scheduleNextJob(vmScheduleId);
+        Assert.assertNull(actualDate);
+    }
+
+    @Test
+    public void testScheduleNextJobScheduleIdExists() {
+        long vmScheduleId = 1;
+        VMScheduleVO vmScheduleVO = Mockito.mock(VMScheduleVO.class);
+        Date date = Mockito.mock(Date.class);
+        Mockito.when(vmScheduleDao.findById(vmScheduleId)).thenReturn(vmScheduleVO);
+        Mockito.doReturn(date).when(vmScheduler).scheduleNextJob(vmScheduleVO);
+        Date actualDate = vmScheduler.scheduleNextJob(vmScheduleId);
+        Assert.assertEquals(date, actualDate);
+    }
+
+    @Test
+    public void testExecuteJobs() {
+        /*
+         Test VMSchedulerImpl.executeJobs() method with a map of VMScheduledJob objects
+         covering all the possible scenarios
+         1. When the job is executed successfully
+         2. When the job is skipped (processJob returns null)
+        */
+
+        VMScheduledJobVO job1 = Mockito.mock(VMScheduledJobVO.class);
+        VMScheduledJobVO job2 = Mockito.mock(VMScheduledJobVO.class);
+
+        UserVm vm1 = Mockito.mock(UserVm.class);
+        UserVm vm2 = Mockito.mock(UserVm.class);
+
+        Mockito.when(job1.getVmId()).thenReturn(1L);
+        Mockito.when(job1.getScheduledTime()).thenReturn(new Date());
+        Mockito.when(job1.getAction()).thenReturn(VMSchedule.Action.START);
+        Mockito.when(job1.getVmScheduleId()).thenReturn(1L);
+        Mockito.when(job2.getVmId()).thenReturn(2L);
+        Mockito.when(job2.getScheduledTime()).thenReturn(new Date());
+        Mockito.when(job2.getAction()).thenReturn(VMSchedule.Action.STOP);
+        Mockito.when(job2.getVmScheduleId()).thenReturn(2L);
+
+        Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm1);
+        Mockito.when(userVmManager.getUserVm(2L)).thenReturn(vm2);
+
+        Mockito.doReturn(1L).when(vmScheduler).processJob(job1, vm1);
+        Mockito.doReturn(null).when(vmScheduler).processJob(job2, vm2);
+
+        Mockito.when(vmScheduledJobDao.acquireInLockTable(job1.getId())).thenReturn(job1);
+        Mockito.when(vmScheduledJobDao.acquireInLockTable(job2.getId())).thenReturn(job2);
+
+        Map<Long, VMScheduledJob> jobs = new HashMap<>();
+        jobs.put(1L, job1);
+        jobs.put(2L, job2);
+
+        ReflectionTestUtils.setField(vmScheduler, "currentTimestamp", new Date());
+
+        vmScheduler.executeJobs(jobs);
+
+        Mockito.verify(vmScheduler, Mockito.times(2)).processJob(Mockito.any(), Mockito.any());
+        Mockito.verify(vmScheduledJobDao, Mockito.times(2)).acquireInLockTable(Mockito.anyLong());
+    }
+
+    @Test
+    public void testExecuteStopVMJob() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
+        PowerMockito.mockStatic(ComponentContext.class);
+        when(ComponentContext.inject(StopVMCmd.class)).thenReturn(Mockito.mock(StopVMCmd.class));
+        long jobId = vmScheduler.executeStopVMJob(vm, false, 1L);
+
+        Assert.assertEquals(1L, jobId);
+    }
+
+    @Test
+    public void testExecuteRebootVMJob() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
+        PowerMockito.mockStatic(ComponentContext.class);
+        when(ComponentContext.inject(RebootVMCmd.class)).thenReturn(Mockito.mock(RebootVMCmd.class));
+        long jobId = vmScheduler.executeRebootVMJob(vm, false, 1L);
+
+        Assert.assertEquals(1L, jobId);
+    }
+
+    @Test
+    public void testExecuteStartVMJob() {
+        VirtualMachine vm = Mockito.mock(VirtualMachine.class);
+        Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
+        PowerMockito.mockStatic(ComponentContext.class);
+        when(ComponentContext.inject(StartVMCmd.class)).thenReturn(Mockito.mock(StartVMCmd.class));
+        long jobId = vmScheduler.executeStartVMJob(vm, 1L);
+
+        Assert.assertEquals(1L, jobId);
+    }
+}
diff --git a/server/src/test/resources/db.properties b/server/src/test/resources/db.properties
index 5eefc45..e1cc048 100644
--- a/server/src/test/resources/db.properties
+++ b/server/src/test/resources/db.properties
@@ -17,7 +17,7 @@
 

 

 # management server clustering parameters, change cluster.node.IP to the machine IP address

-# in which the management server is running
+# in which the management server is running

 cluster.node.IP=127.0.0.1

 cluster.servlet.port=9090

 

diff --git a/services/console-proxy/pom.xml b/services/console-proxy/pom.xml
index d561900..80a04e0 100644
--- a/services/console-proxy/pom.xml
+++ b/services/console-proxy/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-services</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/services/console-proxy/rdpconsole/pom.xml b/services/console-proxy/rdpconsole/pom.xml
index a48bff2..d3d79d9 100644
--- a/services/console-proxy/rdpconsole/pom.xml
+++ b/services/console-proxy/rdpconsole/pom.xml
@@ -26,7 +26,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-service-console-proxy</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/services/console-proxy/rdpconsole/src/main/java/streamer/debug/MockServer.java b/services/console-proxy/rdpconsole/src/main/java/streamer/debug/MockServer.java
index c8f08b4..d4e48c6 100644
--- a/services/console-proxy/rdpconsole/src/main/java/streamer/debug/MockServer.java
+++ b/services/console-proxy/rdpconsole/src/main/java/streamer/debug/MockServer.java
@@ -198,8 +198,8 @@
         return shutdowned;
     }
 
-    public void waitUntilShutdowned(long timeToWaitMiliseconds) throws InterruptedException {
-        long deadline = System.currentTimeMillis() + timeToWaitMiliseconds;
+    public void waitUntilShutdowned(long timeToWaitMilliseconds) throws InterruptedException {
+        long deadline = System.currentTimeMillis() + timeToWaitMilliseconds;
         while (!shutdowned && System.currentTimeMillis() < deadline) {
             Thread.sleep(10);
         }
diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml
index 491afc9..1e5217a 100644
--- a/services/console-proxy/server/pom.xml
+++ b/services/console-proxy/server/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-service-console-proxy</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/services/pom.xml b/services/pom.xml
index 9977949..5491892 100644
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/services/secondary-storage/controller/pom.xml b/services/secondary-storage/controller/pom.xml
index a9f1caa..65d3a23 100644
--- a/services/secondary-storage/controller/pom.xml
+++ b/services/secondary-storage/controller/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-service-secondary-storage</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml
index ad6aa86..b086125 100644
--- a/services/secondary-storage/pom.xml
+++ b/services/secondary-storage/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-services</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/services/secondary-storage/server/pom.xml b/services/secondary-storage/server/pom.xml
index 9c24622..ae0e147 100644
--- a/services/secondary-storage/server/pom.xml
+++ b/services/secondary-storage/server/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack-service-secondary-storage</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/systemvm/pom.xml b/systemvm/pom.xml
index 6a185dd..f51505e 100644
--- a/systemvm/pom.xml
+++ b/systemvm/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <properties>
diff --git a/test/integration/broken/maint/test_zone_level_local_storage_setting.py b/test/integration/broken/maint/test_zone_level_local_storage_setting.py
index 19d3ed5..c8e1946 100644
--- a/test/integration/broken/maint/test_zone_level_local_storage_setting.py
+++ b/test/integration/broken/maint/test_zone_level_local_storage_setting.py
@@ -576,7 +576,7 @@
             ispersistent='true'
         )
 
-        # 3-list netwrok offerings
+        # 3-list network offerings
         list_nw_of = NetworkOffering.list(self.apiclient,
                                           id=self.network_offering.id)
         self.assertEqual(
diff --git a/test/integration/component/test_add_remove_network.py b/test/integration/component/test_add_remove_network.py
index 1562507..716ffa3 100644
--- a/test/integration/component/test_add_remove_network.py
+++ b/test/integration/component/test_add_remove_network.py
@@ -721,7 +721,7 @@
 
         self.debug("Created network %s" % network_2.name)
 
-        self.debug("Trying to add netwrok %s to VM %s, this should fail" %
+        self.debug("Trying to add network %s to VM %s, this should fail" %
                    (network_2.name, virtual_machine.id))
 
         with self.assertRaises(Exception) as e:
diff --git a/test/integration/component/test_escalations_instances.py b/test/integration/component/test_escalations_instances.py
index d2ff9b5..0004cce 100644
--- a/test/integration/component/test_escalations_instances.py
+++ b/test/integration/component/test_escalations_instances.py
@@ -4194,7 +4194,7 @@
             "Isolated Network Offerings with sourceNat enabled are not found"
         )
         """
-        Create Isolated netwrok with ip range
+        Create Isolated network with ip range
         """
         self.services["network"]["startip"] = "10.1.1.2"
         self.services["network"]["endip"] = "10.1.1.254"
@@ -4312,7 +4312,7 @@
             "Isolated Network Offerings with sourceNat enabled are not found"
         )
         """
-        Create Isolated netwrok with ip range
+        Create Isolated network with ip range
         """
         self.services["network"]["startip"] = "10.1.1.2"
         self.services["network"]["endip"] = "10.1.1.254"
diff --git a/test/integration/component/test_multiple_ips_per_nic.py b/test/integration/component/test_multiple_ips_per_nic.py
index 85cbd43..6c24ec1 100644
--- a/test/integration/component/test_multiple_ips_per_nic.py
+++ b/test/integration/component/test_multiple_ips_per_nic.py
@@ -340,7 +340,7 @@
                 self.apiclient, id=(
                     virtual_machine.nic[0].id + random_gen()))
             self.fail(
-                "Adding secondary IP with wrong NIC id succeeded, it shoud have failed")
+                "Adding secondary IP with wrong NIC id succeeded, it should have failed")
         except Exception as e:
             self.debug("Failed while adding secondary IP to wrong NIC")
 
diff --git a/test/integration/component/test_multiple_nic_support.py b/test/integration/component/test_multiple_nic_support.py
index d9ae681..cba1c4e 100644
--- a/test/integration/component/test_multiple_nic_support.py
+++ b/test/integration/component/test_multiple_nic_support.py
@@ -52,11 +52,11 @@
 import random
 import time
 
-class TestMulipleNicSupport(cloudstackTestCase):
+class TestMultipleNicSupport(cloudstackTestCase):
     @classmethod
     def setUpClass(cls):
         cls.testClient = super(
-            TestMulipleNicSupport,
+            TestMultipleNicSupport,
             cls).getClsTestClient()
         cls.apiclient = cls.testClient.getApiClient()
         cls.testdata = cls.testClient.getParsedTestDataConfig()
@@ -70,7 +70,7 @@
             cls.skip = True
             return
 
-        cls.logger = logging.getLogger("TestMulipleNicSupport")
+        cls.logger = logging.getLogger("TestMultipleNicSupport")
         cls.stream_handler = logging.StreamHandler()
         cls.logger.setLevel(logging.DEBUG)
         cls.logger.addHandler(cls.stream_handler)
@@ -255,7 +255,7 @@
 
     @classmethod
     def tearDownClass(self):
-        super(TestMulipleNicSupport, self).tearDownClass()
+        super(TestMultipleNicSupport, self).tearDownClass()
 
     def setUp(self):
         if self.skip:
@@ -265,7 +265,7 @@
         return
 
     def tearDown(self):
-        super(TestMulipleNicSupport, self).tearDown()
+        super(TestMultipleNicSupport, self).tearDown()
 
     def verify_network_rules(self, vm_id):
         virtual_machine = VirtualMachine.list(
diff --git a/test/integration/component/test_multiple_physical_network_creation.py b/test/integration/component/test_multiple_physical_network_creation.py
index 7f6117f..09127b9 100644
--- a/test/integration/component/test_multiple_physical_network_creation.py
+++ b/test/integration/component/test_multiple_physical_network_creation.py
@@ -39,11 +39,11 @@
 
 import logging
 
-class TestMulipleNetworkCreation(cloudstackTestCase):
+class TestMultipleNetworkCreation(cloudstackTestCase):
     @classmethod
     def setUpClass(cls):
         cls.testClient = super(
-            TestMulipleNetworkCreation,
+            TestMultipleNetworkCreation,
             cls).getClsTestClient()
         cls.apiclient = cls.testClient.getApiClient()
         cls.testdata = cls.testClient.getParsedTestDataConfig()
@@ -54,7 +54,7 @@
         cls.template = get_template(cls.apiclient, cls.zone.id)
         cls._cleanup = []
 
-        cls.logger = logging.getLogger("TestMulipleNetworkCreation")
+        cls.logger = logging.getLogger("TestMultipleNetworkCreation")
         cls.stream_handler = logging.StreamHandler()
         cls.logger.setLevel(logging.DEBUG)
         cls.logger.addHandler(cls.stream_handler)
@@ -127,7 +127,7 @@
                 allocationstate="Enabled"
             )
             # Cleanup resources used
-            super(TestMulipleNetworkCreation, cls).tearDownClass()
+            super(TestMultipleNetworkCreation, cls).tearDownClass()
         except Exception as e:
             raise Exception("Warning: Exception during cleanup : %s" % e)
         return
@@ -139,7 +139,7 @@
         return
 
     def tearDown(self):
-        super(TestMulipleNetworkCreation, self).tearDown()
+        super(TestMultipleNetworkCreation, self).tearDown()
 
     @attr(tags=["advanced"], required_hardware="false")
     def test_01_add_traffictype_for_untagged_networks(self):
diff --git a/test/integration/component/test_region_vpc.py b/test/integration/component/test_region_vpc.py
index 6866e48..0bfe112 100644
--- a/test/integration/component/test_region_vpc.py
+++ b/test/integration/component/test_region_vpc.py
@@ -490,7 +490,7 @@
                                   vpcid=vpc.id
                                   )
 
-        self.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.debug("Adding NetworkACl rules to make PF and LB accessible")
         NetworkACL.create(
                 self.apiclient,
                 networkid=network.id,
diff --git a/test/integration/component/test_shared_networks.py b/test/integration/component/test_shared_networks.py
index c7e2656..be6d663 100644
--- a/test/integration/component/test_shared_networks.py
+++ b/test/integration/component/test_shared_networks.py
@@ -584,7 +584,7 @@
         #  6. No checks reqd
         #  7. a. listVirtualMachines should show both VMs in running state
         #     in the user account and the admin account
-        #     b. VM's IPs shoud be in the range of the shared network ip ranges
+        #     b. VM's IPs should be in the range of the shared network ip ranges
 
         # Create admin account
         self.admin_account = Account.create(
@@ -1974,7 +1974,7 @@
         #     enabled offering
         #  4. listPhysicalNetworks should return at least one active
         #     physical network
-        #  5. network creation shoud PASS
+        #  5. network creation should PASS
         #  6. network creation should FAIL since VLAN is already used by
         #     previously created network
 
diff --git a/test/integration/component/test_vpc.py b/test/integration/component/test_vpc.py
index 2587d78..5870769 100644
--- a/test/integration/component/test_vpc.py
+++ b/test/integration/component/test_vpc.py
@@ -1004,7 +1004,7 @@
             vpcid=vpc.id
         )
 
-        self.debug("Adding NetwrokACl rules to make NAT rule accessible")
+        self.debug("Adding NetworkACl rules to make NAT rule accessible")
         NetworkACL.create(
             self.apiclient,
             networkid=network_1.id,
@@ -1090,7 +1090,7 @@
             vm_3.name, vm_4.name))
         lb_rule.assign(self.apiclient, [vm_3, vm_4])
 
-        self.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.debug("Adding NetworkACl rules to make PF and LB accessible")
         NetworkACL.create(
             self.apiclient,
             networkid=network_2.id,
@@ -1350,7 +1350,7 @@
             vpcid=vpc.id
         )
 
-        self.debug("Adding NetwrokACl rules to make NAT rule accessible")
+        self.debug("Adding NetworkACl rules to make NAT rule accessible")
         NetworkACL.create(
             self.apiclient,
             networkid=network_1.id,
@@ -1436,7 +1436,7 @@
             vm_3.name, vm_4.name))
         lb_rule.assign(self.apiclient, [vm_3, vm_4])
 
-        self.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.debug("Adding NetworkACl rules to make PF and LB accessible")
         NetworkACL.create(
             self.apiclient,
             networkid=network_2.id,
diff --git a/test/integration/component/test_vpc_distributed_routing_offering.py b/test/integration/component/test_vpc_distributed_routing_offering.py
index baa8f9b..97fcb80 100644
--- a/test/integration/component/test_vpc_distributed_routing_offering.py
+++ b/test/integration/component/test_vpc_distributed_routing_offering.py
@@ -481,7 +481,7 @@
                                   vpcid=vpc.id
                                   )
 
-        self.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.debug("Adding NetworkACl rules to make PF and LB accessible")
         NetworkACL.create(
                 self.apiclient,
                 networkid=network.id,
diff --git a/test/integration/component/test_vpc_network.py b/test/integration/component/test_vpc_network.py
index bf3f368..9a313e2 100644
--- a/test/integration/component/test_vpc_network.py
+++ b/test/integration/component/test_vpc_network.py
@@ -1864,7 +1864,7 @@
             "List public Ip for network should list the Ip addr"
         )
 
-        self.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.debug("Adding NetworkACl rules to make PF and LB accessible")
         nw_acl = NetworkACL.create(
             self.apiclient,
             networkid=network_1.id,
@@ -2023,7 +2023,7 @@
         )
         self.cleanup.append(nat_rule)
 
-        self.debug("Adding NetwrokACl rules to make NAT rule accessible")
+        self.debug("Adding NetworkACl rules to make NAT rule accessible")
         nat_acl = NetworkACL.create(
             self.apiclient,
             networkid=network_1.id,
diff --git a/test/integration/component/test_vpc_offerings.py b/test/integration/component/test_vpc_offerings.py
index fbfb61f..25206cf 100644
--- a/test/integration/component/test_vpc_offerings.py
+++ b/test/integration/component/test_vpc_offerings.py
@@ -416,7 +416,7 @@
         )
         self.cleanup.append(nat_rule)
 
-        self.logger.debug("Adding NetwrokACl rules to make PF and LB accessible")
+        self.logger.debug("Adding NetworkACl rules to make PF and LB accessible")
         networkacl_1 = NetworkACL.create(
             self.apiclient,
             networkid=network.id,
diff --git a/test/integration/component/test_vpc_vms_deployment.py b/test/integration/component/test_vpc_vms_deployment.py
index ded3374..4d3f934 100644
--- a/test/integration/component/test_vpc_vms_deployment.py
+++ b/test/integration/component/test_vpc_vms_deployment.py
@@ -2046,7 +2046,7 @@
             self.fail("Failed to enable static NAT on IP: %s - %s" % (
                                         public_ip_4.ipaddress.ipaddress, e))
 
-        self.debug("Adding NetwrokACl rules to make NAT rule accessible with network %s" % network_1.id)
+        self.debug("Adding NetworkACl rules to make NAT rule accessible with network %s" % network_1.id)
         NetworkACL.create(
                                          self.apiclient,
                                          networkid=network_1.id,
diff --git a/test/integration/smoke/test_annotations.py b/test/integration/smoke/test_annotations.py
index 812b3dc..e527b96 100644
--- a/test/integration/smoke/test_annotations.py
+++ b/test/integration/smoke/test_annotations.py
@@ -83,6 +83,7 @@
         cls.host = list_hosts(cls.apiclient,
                                zoneid=cls.zone.id,
                                type='Routing')[0]
+        cls.mgmt_server = list_mgmt_servers(cls.apiclient)[0]
 
     @classmethod
     def tearDownClass(cls):
@@ -136,6 +137,12 @@
         self.assertEqual(self.added_annotations[-1].annotation.annotation, "annotation1")
 
     @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
+    def test_01_add_ms_annotation(self):
+        """Testing the addAnnotations API ability to add an annoatation per management server"""
+        self.addAnnotation("mgmt-server-annotation1", self.mgmt_server.id, "MANAGEMENT_SERVER")
+        self.assertEqual(self.added_annotations[-1].annotation.annotation, "mgmt-server-annotation1")
+
+    @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
     def test_02_add_multiple_annotations(self):
         """Testing the addAnnotations API ability to add an annoatation per host
         when there are annotations already.
diff --git a/test/integration/smoke/test_guest_os.py b/test/integration/smoke/test_guest_os.py
new file mode 100644
index 0000000..01fe343
--- /dev/null
+++ b/test/integration/smoke/test_guest_os.py
@@ -0,0 +1,260 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#All tests inherit from cloudstackTestCase
+import unittest
+
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.cloudstackAPI import listHosts
+from marvin.cloudstackException import CloudstackAPIException
+from marvin.lib.base import (VirtualMachine,
+                             Account,
+                             GuestOSCategory,
+                             GuestOS,
+                             GuestOsMapping,
+                             NetworkOffering,
+                             Network)
+from marvin.lib.common import get_test_template, get_zone, list_virtual_machines
+from marvin.lib.utils import (validateList, cleanup_resources)
+from nose.plugins.attrib import attr
+from marvin.codes import PASS,FAIL
+
+class Services:
+    def __init__(self):
+        self.services = {
+        }
+
+class TestGuestOS(cloudstackTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestGuestOS, cls)
+        cls.api_client = cls.testClient.getApiClient()
+        cls.services = Services().services
+
+        cls.hypervisor = cls.get_hypervisor_type()
+
+    @classmethod
+    def setUp(self):
+        self.apiclient = self.testClient.getApiClient()
+
+        #build cleanup list
+        self.cleanup = []
+
+    @classmethod
+    def tearDown(self):
+        try:
+            cleanup_resources(self.apiclient, self.cleanup)
+        except Exception as e:
+            self.debug("Warning! Exception in tearDown: %s" % e)
+
+    @classmethod
+    def get_hypervisor_type(cls):
+
+        """Return the hypervisor available in setup"""
+
+        cmd = listHosts.listHostsCmd()
+        cmd.type = 'Routing'
+        cmd.listall = True
+        hosts = cls.api_client.listHosts(cmd)
+        hosts_list_validation_result = validateList(hosts)
+        assert hosts_list_validation_result[0] == PASS, "host list validation failed"
+        return hosts_list_validation_result[1]
+
+
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_CRUD_operations_guest_OS(self):
+        """Test add, list, update operations on guest OS
+            1. Add a guest OS
+            2. List the guest OS
+            3. Delete the added guest OS
+        """
+        list_os_categories = GuestOSCategory.list(self.apiclient, name="CentOS", listall=True)
+        self.assertNotEqual(
+            len(list_os_categories),
+            0,
+            "List OS categories was empty"
+        )
+        os_category = list_os_categories[0]
+
+        self.guestos1 = GuestOS.add(
+            self.apiclient,
+            osdisplayname="testCentOS",
+            oscategoryid=os_category.id
+        )
+        list_guestos = GuestOS.list(self.apiclient, id=self.guestos1.id, listall=True)
+        self.assertNotEqual(
+            len(list_guestos),
+            0,
+            "List guest OS was empty"
+        )
+        guestos = list_guestos[0]
+        self.assertEqual(
+            guestos.id,
+            self.guestos1.id,
+            "Guest os ids do not match"
+        )
+
+        GuestOS.remove(
+            self.apiclient,
+            id=self.guestos1.id
+        )
+
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_CRUD_operations_guest_OS_mapping(self):
+        """Test add, list, update operations on guest OS mapping
+            1. Add a guest OS
+            2. Add a guest OS mapping
+            3. Delete the added guest OS and mappings
+        """
+        list_os_categories = GuestOSCategory.list(self.apiclient, name="CentOS", listall=True)
+        os_category = list_os_categories[0]
+        self.guestos1 = GuestOS.add(
+            self.apiclient,
+            osdisplayname="testCentOS",
+            oscategoryid=os_category.id
+        )
+
+        if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
+            raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
+
+        self.guestosmapping1 = GuestOsMapping.add(
+            self.apiclient,
+            ostypeid=self.guestos1.id,
+            hypervisor=self.hypervisor.hypervisor,
+            hypervisorversion=self.hypervisor.hypervisorversion,
+            osnameforhypervisor="testOSMappingName"
+        )
+
+        list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True)
+        self.assertNotEqual(
+            len(list_guestos_mapping),
+            0,
+            "List guest OS mapping was empty"
+        )
+        guestosmapping = list_guestos_mapping[0]
+        self.assertEqual(
+            guestosmapping.id,
+            self.guestosmapping1.id,
+            "Guest os mapping ids do not match"
+        )
+
+        GuestOsMapping.remove(
+            self.apiclient,
+            id=self.guestosmapping1.id
+        )
+
+        GuestOS.remove(
+            self.apiclient,
+            id=self.guestos1.id
+        )
+
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_guest_OS_mapping_check_with_hypervisor(self):
+        """Test add, list, update operations on guest OS mapping
+            1. Add a guest OS
+            2. Add a guest OS mapping with osmappingcheckenabled true
+            3. Delete the added guest OS and mappings
+        """
+        list_os_categories = GuestOSCategory.list(self.apiclient, name="CentOS", listall=True)
+        os_category = list_os_categories[0]
+        self.guestos1 = GuestOS.add(
+            self.apiclient,
+            osdisplayname="testOSname1",
+            oscategoryid=os_category.id
+        )
+
+        if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
+            raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
+
+        if self.hypervisor.hypervisor.lower() == "xenserver":
+            testosname="Debian Squeeze 6.0 (32-bit)"
+        else:
+            testosname="debian4_64Guest"
+
+        self.guestosmapping1 = GuestOsMapping.add(
+            self.apiclient,
+            ostypeid=self.guestos1.id,
+            hypervisor=self.hypervisor.hypervisor,
+            hypervisorversion=self.hypervisor.hypervisorversion,
+            osnameforhypervisor=testosname,
+            osmappingcheckenabled=True
+        )
+
+        list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True)
+        self.assertNotEqual(
+            len(list_guestos_mapping),
+            0,
+            "List guest OS mapping was empty"
+        )
+        guestosmapping = list_guestos_mapping[0]
+        self.assertEqual(
+            guestosmapping.id,
+            self.guestosmapping1.id,
+            "Guest os mapping ids do not match"
+        )
+
+        GuestOsMapping.remove(
+            self.apiclient,
+            id=self.guestosmapping1.id
+        )
+
+        GuestOS.remove(
+            self.apiclient,
+            id=self.guestos1.id
+        )
+
+    @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
+    def test_guest_OS_mapping_check_with_hypervisor_failure(self):
+        """Test add, list, update operations on guest OS mapping
+            1. Add a guest OS
+            2. Add a guest OS mapping with osmappingcheckenabled true
+            3. Delete the added guest OS and mappings
+        """
+        list_os_categories = GuestOSCategory.list(self.apiclient, name="CentOS", listall=True)
+        os_category = list_os_categories[0]
+        self.guestos1 = GuestOS.add(
+            self.apiclient,
+            osdisplayname="testOSname2",
+            oscategoryid=os_category.id
+        )
+
+        if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
+            raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
+
+        testosname = "incorrectOSname"
+
+        try:
+            self.guestosmapping1 = GuestOsMapping.add(
+                self.apiclient,
+                ostypeid=self.guestos1.id,
+                hypervisor=self.hypervisor.hypervisor,
+                hypervisorversion=self.hypervisor.hypervisorversion,
+                osnameforhypervisor=testosname,
+                osmappingcheckenabled=True
+            )
+            GuestOsMapping.remove(
+                self.apiclient,
+                id=self.guestosmapping1.id
+            )
+            self.fail("Since os mapping name is wrong, this API should fail")
+        except CloudstackAPIException as e:
+            self.debug("Addition guest OS mapping failed as expected %s " % e)
+        GuestOS.remove(
+            self.apiclient,
+            id=self.guestos1.id
+        )
+        return
diff --git a/test/integration/smoke/test_host_control_state.py b/test/integration/smoke/test_host_control_state.py
index 809af7d..4b8409e 100644
--- a/test/integration/smoke/test_host_control_state.py
+++ b/test/integration/smoke/test_host_control_state.py
@@ -20,7 +20,7 @@
 Tests for host control state
 """
 
-from marvin.cloudstackAPI import updateHost
+from marvin.cloudstackAPI import (updateHost, updateConfiguration)
 from nose.plugins.attrib import attr
 from marvin.cloudstackTestCase import cloudstackTestCase
 from marvin.lib.common import (get_domain,
@@ -28,13 +28,18 @@
                                get_template,
                                list_hosts,
                                list_routers,
-                               list_ssvms)
+                               list_ssvms,
+                               list_clusters,
+                               list_hosts)
 from marvin.lib.base import (Account,
                              Domain,
                              Host,
                              ServiceOffering,
                              VirtualMachine)
 from marvin.sshClient import SshClient
+from marvin.lib.decoratorGenerators import skipTestIf
+from marvin.lib.utils import wait_until
+import logging
 import time
 
 
@@ -250,3 +255,220 @@
 
         self.enable_host(host_id)
         self.verify_router_host_control_state(router.id, "Enabled")
+
+
+class TestAutoEnableDisableHost(cloudstackTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.testClient = super(TestAutoEnableDisableHost, cls).getClsTestClient()
+        cls.apiclient = cls.testClient.getApiClient()
+        cls.services = cls.testClient.getParsedTestDataConfig()
+        # Get Zone, Domain and templates
+        cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
+        cls.hypervisor = cls.testClient.getHypervisorInfo()
+        cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__
+        if cls.hypervisor.lower() not in ['kvm']:
+            cls.hypervisorNotSupported = True
+            return
+
+        cls.logger = logging.getLogger('TestAutoEnableDisableHost')
+        return
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestAutoEnableDisableHost, cls).tearDownClass()
+
+    def tearDown(self):
+        super(TestAutoEnableDisableHost, self).tearDown()
+
+    def get_ssh_client(self, ip, username, password, retries=10):
+        """ Setup ssh client connection and return connection """
+        try:
+            ssh_client = SshClient(ip, 22, username, password, retries)
+        except Exception as e:
+            raise unittest.SkipTest("Unable to create ssh connection: " % e)
+
+        self.assertIsNotNone(
+            ssh_client, "Failed to setup ssh connection to ip=%s" % ip)
+
+        return ssh_client
+
+    def wait_until_host_is_in_state(self, hostid, resourcestate, interval=3, retries=20):
+        def check_resource_state():
+            response = Host.list(
+                self.apiclient,
+                id=hostid
+            )
+            if isinstance(response, list):
+                if response[0].resourcestate == resourcestate:
+                    self.logger.debug('Host with id %s is in resource state = %s' % (hostid, resourcestate))
+                    return True, None
+                else:
+                    self.logger.debug("Waiting for host " + hostid +
+                                      " to reach state " + resourcestate +
+                                      ", with current state " + response[0].resourcestate)
+            return False, None
+
+        done, _ = wait_until(interval, retries, check_resource_state)
+        if not done:
+            raise Exception("Failed to wait for host %s to be on resource state %s" % (hostid, resourcestate))
+        return True
+
+    def update_config(self, enable_feature):
+        cmd = updateConfiguration.updateConfigurationCmd()
+        cmd.name = "enable.kvm.host.auto.enable.disable"
+        cmd.value = enable_feature
+
+        response = self.apiclient.updateConfiguration(cmd)
+        self.debug("updated the parameter %s with value %s" % (response.name, response.value))
+
+    def update_health_check_script(self, ip_address, username, password, exit_code):
+        health_check_script_path = "/etc/cloudstack/agent/healthcheck.sh"
+        health_check_agent_property = "agent.health.check.script.path"
+        agent_properties_file_path = "/etc/cloudstack/agent/agent.properties"
+
+        ssh_client = self.get_ssh_client(ip_address, username, password)
+        ssh_client.execute("echo 'exit %s' > %s" % (exit_code, health_check_script_path))
+        ssh_client.execute("chmod +x %s" % health_check_script_path)
+        ssh_client.execute("echo '%s=%s' >> %s" % (health_check_agent_property, health_check_script_path,
+                                                   agent_properties_file_path))
+        ssh_client.execute("service cloudstack-agent restart")
+
+    def remove_host_health_check(self, ip_address, username, password):
+        health_check_script_path = "/etc/cloudstack/agent/healthcheck.sh"
+        ssh_client = self.get_ssh_client(ip_address, username, password)
+        ssh_client.execute("rm -f %s" % health_check_script_path)
+
+    def select_host_for_health_checks(self):
+        clusters = list_clusters(
+            self.apiclient,
+            zoneid=self.zone.id
+        )
+        if not clusters:
+            return None
+
+        for cluster in clusters:
+            list_hosts_response = list_hosts(
+                self.apiclient,
+                clusterid=cluster.id,
+                type="Routing",
+                resourcestate="Enabled"
+            )
+            assert isinstance(list_hosts_response, list)
+            if not list_hosts_response or len(list_hosts_response) < 1:
+                continue
+            return list_hosts_response[0]
+        return None
+
+    def update_host_allocation_state(self, id, enable):
+        cmd = updateHost.updateHostCmd()
+        cmd.id = id
+        cmd.allocationstate = "Enable" if enable else "Disable"
+        response = self.apiclient.updateHost(cmd)
+        self.assertEqual(response.resourcestate, "Enabled" if enable else "Disabled")
+
+    @attr(tags=["basic", "advanced"], required_hardware="false")
+    @skipTestIf("hypervisorNotSupported")
+    def test_01_auto_enable_disable_kvm_host(self):
+        """Test to auto-enable and auto-disable a KVM host based on health check results
+
+        # Validate the following:
+        # 1. Enable the KVM Auto Enable/Disable Feature
+        # 2. Set a health check script that fails and observe the host is Disabled
+        # 3. Make the health check script succeed and observe the host is Enabled
+        """
+
+        selected_host = self.select_host_for_health_checks()
+        if not selected_host:
+            self.skipTest("Cannot find a KVM host to test the auto-enable-disable feature")
+
+        username = self.hostConfig["username"]
+        password = self.hostConfig["password"]
+
+        # Enable the Auto Enable/Disable Configuration
+        self.update_config("true")
+
+        # Set health check script for failure
+        self.update_health_check_script(selected_host.ipaddress, username, password, 1)
+        self.wait_until_host_is_in_state(selected_host.id, "Disabled", 5, 200)
+
+        # Set health check script for success
+        self.update_health_check_script(selected_host.ipaddress, username, password, 0)
+
+        self.wait_until_host_is_in_state(selected_host.id, "Enabled", 5, 200)
+
+    @attr(tags=["basic", "advanced"], required_hardware="false")
+    @skipTestIf("hypervisorNotSupported")
+    def test_02_disable_host_overrides_auto_enable_kvm_host(self):
+        """Test to override the auto-enabling of a KVM host by an administrator
+
+        # Validate the following:
+        # 1. Enable the KVM Auto Enable/Disable Feature
+        # 2. Set a health check script that succeeds and observe the host is Enabled
+        # 3. Make the host Disabled
+        # 4. Verify the host does not get auto-enabled after the previous step
+        """
+
+        selected_host = self.select_host_for_health_checks()
+        if not selected_host:
+            self.skipTest("Cannot find a KVM host to test the auto-enable-disable feature")
+
+        username = self.hostConfig["username"]
+        password = self.hostConfig["password"]
+
+        # Enable the Auto Enable/Disable Configuration
+        self.update_config("true")
+
+        # Set health check script for failure
+        self.update_health_check_script(selected_host.ipaddress, username, password, 0)
+        self.wait_until_host_is_in_state(selected_host.id, "Enabled", 5, 200)
+
+        # Manually disable the host
+        self.update_host_allocation_state(selected_host.id, False)
+
+        # Wait for more than the ping interval
+        time.sleep(70)
+
+        # Verify the host continues on Disabled state
+        self.wait_until_host_is_in_state(selected_host.id, "Disabled", 5, 200)
+
+        # Restore the host to Enabled state
+        self.remove_host_health_check(selected_host.ipaddress, username, password)
+        self.update_host_allocation_state(selected_host.id, True)
+
+    @attr(tags=["basic", "advanced"], required_hardware="false")
+    @skipTestIf("hypervisorNotSupported")
+    def test_03_enable_host_does_not_override_auto_disable_kvm_host(self):
+        """Test to override the auto-disabling of a KVM host by an administrator
+
+        # Validate the following:
+        # 1. Enable the KVM Auto Enable/Disable Feature
+        # 2. Set a health check script that fails and observe the host is Disabled
+        # 3. Make the host Enabled
+        # 4. Verify the host does get auto-disabled after the previous step
+        """
+
+        selected_host = self.select_host_for_health_checks()
+        if not selected_host:
+            self.skipTest("Cannot find a KVM host to test the auto-enable-disable feature")
+
+        username = self.hostConfig["username"]
+        password = self.hostConfig["password"]
+
+        # Enable the Auto Enable/Disable Configuration
+        self.update_config("true")
+
+        # Set health check script for failure
+        self.update_health_check_script(selected_host.ipaddress, username, password, 1)
+        self.wait_until_host_is_in_state(selected_host.id, "Disabled", 5, 200)
+
+        # Manually enable the host
+        self.update_host_allocation_state(selected_host.id, True)
+
+        # Verify the host goes back to Disabled state
+        self.wait_until_host_is_in_state(selected_host.id, "Disabled", 5, 200)
+
+        # Restore the host to Enabled state
+        self.remove_host_health_check(selected_host.ipaddress, username, password)
+        self.update_host_allocation_state(selected_host.id, True)
diff --git a/test/integration/smoke/test_kubernetes_clusters.py b/test/integration/smoke/test_kubernetes_clusters.py
index c227e7f..3b45347 100644
--- a/test/integration/smoke/test_kubernetes_clusters.py
+++ b/test/integration/smoke/test_kubernetes_clusters.py
@@ -33,7 +33,9 @@
                                   scaleKubernetesCluster,
                                   getKubernetesClusterConfig,
                                   destroyVirtualMachine,
-                                  deleteNetwork)
+                                  deleteNetwork,
+                                  addVirtualMachinesToKubernetesCluster,
+                                  removeVirtualMachinesFromKubernetesCluster)
 from marvin.cloudstackException import CloudstackAPIException
 from marvin.codes import PASS, FAILED
 from marvin.lib.base import (Template,
@@ -46,12 +48,14 @@
                              VpcOffering,
                              VPC,
                              NetworkACLList,
-                             NetworkACL)
+                             NetworkACL,
+                             VirtualMachine)
 from marvin.lib.utils import (cleanup_resources,
                               validateList,
                               random_gen)
 from marvin.lib.common import (get_zone,
-                               get_domain)
+                               get_domain,
+                               get_template)
 from marvin.sshClient import SshClient
 from nose.plugins.attrib import attr
 from marvin.lib.decoratorGenerators import skipTestIf
@@ -86,6 +90,7 @@
         cls._cleanup = []
         cls.kubernetes_version_ids = []
         cls.vpcAllowAllAclDetailsMap = {}
+        cls.initial_configuration_cks_enabled = None
 
         if cls.hypervisorNotSupported == False:
             cls.endpoint_url = Configurations.list(cls.apiclient, name="endpoint.url")[0].value
@@ -610,7 +615,82 @@
         k8s_cluster = None
         return
 
-    def createKubernetesCluster(self, name, version_id, size=1, control_nodes=1):
+    @attr(tags=["advanced", "smoke"], required_hardware="true")
+    def test_11_test_unmanaged_cluster_lifecycle(self):
+        """Test all operations on unmanaged Kubernetes cluster
+
+        # Validate the following:
+        # 1. createKubernetesCluster should return valid info for new cluster
+        # 2. The Cloud Database contains the valid information
+        # 3. stopKubernetesCluster doesn't work
+        # 4. startKubernetesCluster doesn't work
+        # 5. upgradeKubernetesCluster doesn't work
+        # 6. Adding & removing vm from cluster works
+        # 7. deleteKubernetesCluster should delete an existing HA Kubernetes cluster
+        """
+        cluster = self.createKubernetesCluster("test-unmanaged-cluster", None,
+                                               cluster_type="ExternalManaged")
+        self.verifyKubernetesClusterState(cluster, 'Running')
+        self.debug("Stopping unmanaged Kubernetes cluster with ID: %s" % cluster.id)
+        try:
+            self.stopKubernetesCluster(cluster.id)
+            self.fail("Should not be able to stop unmanaged cluster")
+        except Exception as e:
+            self.debug("Expected exception: %s" % e)
+
+        self.debug("Starting unmanaged Kubernetes cluster with ID: %s" % cluster.id)
+        try:
+            self.startKubernetesCluster(cluster.id)
+            self.fail("Should not be able to start unmanaged cluster")
+        except Exception as e:
+            self.debug("Expected exception: %s" % e)
+
+        self.debug("Upgrading unmanaged Kubernetes cluster with ID: %s" % cluster.id)
+        try:
+            self.upgradeKubernetesCluster(cluster.id, self.kubernetes_version_1_24_0.id)
+            self.fail("Should not be able to upgrade unmanaged cluster")
+        except Exception as e:
+            self.debug("Expected exception: %s" % e)
+
+        template = get_template(self.apiclient,
+                                    self.zone.id,
+                                    self.services["ostype"])
+
+        self.services["virtual_machine"]["template"] = template.id
+        virtualMachine = VirtualMachine.create(self.apiclient, self.services["virtual_machine"], zoneid=self.zone.id,
+                                                            accountid=self.account.name, domainid=self.account.domainid,
+                                                            serviceofferingid=self.cks_service_offering.id)
+        self.debug("Adding VM %s to unmanaged Kubernetes cluster with ID: %s" % (virtualMachine.id, cluster.id))
+        self.addVirtualMachinesToKubernetesCluster(cluster.id, [virtualMachine.id])
+        cluster = self.listKubernetesCluster(cluster.id)
+        self.assertEqual(virtualMachine.id, cluster.virtualmachines[0].id, "VM should be part of the kubernetes cluster")
+        self.assertEqual(1, len(cluster.virtualmachines), "Only one VM should be part of the kubernetes cluster")
+
+        self.debug("Removing VM %s from unmanaged Kubernetes cluster with ID: %s" % (virtualMachine.id, cluster.id))
+        self.removeVirtualMachinesFromKubernetesCluster(cluster.id, [virtualMachine.id])
+        cluster = self.listKubernetesCluster(cluster.id)
+        self.assertEqual(0, len(cluster.virtualmachines), "No VM should be part of the kubernetes cluster")
+
+        self.debug("Deleting unmanaged Kubernetes cluster with ID: %s" % cluster.id)
+        self.deleteKubernetesClusterAndVerify(cluster.id)
+        return
+
+    def addVirtualMachinesToKubernetesCluster(self, cluster_id, vm_list):
+        cmd = addVirtualMachinesToKubernetesCluster.addVirtualMachinesToKubernetesClusterCmd()
+        cmd.id = cluster_id
+        cmd.virtualmachineids = vm_list
+
+        return self.apiclient.addVirtualMachinesToKubernetesCluster(cmd)
+
+    def removeVirtualMachinesFromKubernetesCluster(self, cluster_id, vm_list):
+        cmd = removeVirtualMachinesFromKubernetesCluster.removeVirtualMachinesFromKubernetesClusterCmd()
+        cmd.id = cluster_id
+        cmd.virtualmachineids = vm_list
+
+        return self.apiclient.removeVirtualMachinesFromKubernetesCluster(cmd)
+
+
+    def createKubernetesCluster(self, name, version_id, size=1, control_nodes=1, cluster_type='CloudManaged'):
         createKubernetesClusterCmd = createKubernetesCluster.createKubernetesClusterCmd()
         createKubernetesClusterCmd.name = name
         createKubernetesClusterCmd.description = name + "-description"
@@ -622,11 +702,10 @@
         createKubernetesClusterCmd.noderootdisksize = 10
         createKubernetesClusterCmd.account = self.account.name
         createKubernetesClusterCmd.domainid = self.domain.id
+        createKubernetesClusterCmd.clustertype = cluster_type
         if self.default_network:
             createKubernetesClusterCmd.networkid = self.default_network.id
         clusterResponse = self.apiclient.createKubernetesCluster(createKubernetesClusterCmd)
-        if not clusterResponse:
-            self.cleanup.append(clusterResponse)
         return clusterResponse
 
     def startKubernetesCluster(self, cluster_id):
diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py
index 0c37c08..56b434f 100644
--- a/test/integration/smoke/test_network.py
+++ b/test/integration/smoke/test_network.py
@@ -72,9 +72,6 @@
 
 class TestPublicIP(cloudstackTestCase):
 
-    def setUp(self):
-        self.apiclient = self.testClient.getApiClient()
-
     @classmethod
     def setUpClass(cls):
         testClient = super(TestPublicIP, cls).getClsTestClient()
@@ -85,6 +82,7 @@
         cls.domain = get_domain(cls.apiclient)
         cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
         cls.services['mode'] = cls.zone.networktype
+        cls._cleanup = []
         # Create Accounts & networks
         cls.account = Account.create(
             cls.apiclient,
@@ -92,18 +90,21 @@
             admin=True,
             domainid=cls.domain.id
         )
+        cls._cleanup.append(cls.account)
 
         cls.user = Account.create(
             cls.apiclient,
             cls.services["account"],
             domainid=cls.domain.id
         )
+        cls._cleanup.append(cls.user)
         cls.services["network"]["zoneid"] = cls.zone.id
 
         cls.network_offering = NetworkOffering.create(
             cls.apiclient,
             cls.services["network_offering"],
         )
+        cls._cleanup.append(cls.network_offering)
         # Enable Network offering
         cls.network_offering.update(cls.apiclient, state='Enabled')
 
@@ -114,17 +115,20 @@
             cls.account.name,
             cls.account.domainid
         )
+        cls._cleanup.append(cls.account_network)
         cls.user_network = Network.create(
             cls.apiclient,
             cls.services["network"],
             cls.user.name,
             cls.user.domainid
         )
+        cls._cleanup.append(cls.user_network)
 
         cls.service_offering = ServiceOffering.create(
             cls.apiclient,
             cls.services["service_offerings"]["tiny"],
         )
+        cls._cleanup.append(cls.service_offering)
 
         cls.hypervisor = testClient.getHypervisorInfo()
         cls.template = get_test_template(
@@ -146,6 +150,7 @@
             networkids=cls.account_network.id,
             serviceofferingid=cls.service_offering.id
         )
+        cls._cleanup.append(cls.account_vm)
 
         cls.user_vm = VirtualMachine.create(
             cls.apiclient,
@@ -156,6 +161,7 @@
             networkids=cls.user_network.id,
             serviceofferingid=cls.service_offering.id
         )
+        cls._cleanup.append(cls.user_vm)
 
         # Create Source NAT IP addresses
         PublicIPAddress.create(
@@ -170,25 +176,11 @@
             cls.zone.id,
             cls.user.domainid
         )
-        cls._cleanup = [
-            cls.account_vm,
-            cls.user_vm,
-            cls.account_network,
-            cls.user_network,
-            cls.account,
-            cls.user,
-            cls.network_offering
-        ]
         return
 
     @classmethod
     def tearDownClass(cls):
-        try:
-            # Cleanup resources used
-            cleanup_resources(cls.apiclient, cls._cleanup)
-        except Exception as e:
-            raise Exception("Warning: Exception during cleanup : %s" % e)
-        return
+        super(TestPublicIP, cls).tearDownClass()
 
     @attr(tags=["advanced", "advancedns", "smoke", "dvs"], required_hardware="false")
     def test_public_ip_admin_account(self):
diff --git a/test/integration/smoke/test_private_roles.py b/test/integration/smoke/test_private_roles.py
new file mode 100644
index 0000000..32d4883
--- /dev/null
+++ b/test/integration/smoke/test_private_roles.py
@@ -0,0 +1,275 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from marvin.cloudstackAPI import *
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.lib.base import Account, Role, User
+from marvin.lib.utils import cleanup_resources
+from nose.plugins.attrib import attr
+
+import random
+
+
+class TestData(object):
+    """Test data object that is required to create resources
+    """
+    def __init__(self):
+        self.testdata = {
+            "accountadmin": {
+                "email": "mtu@test.cloud",
+                "firstname": "Marvin",
+                "lastname": "TestAdminAccount",
+                "username": "TestAdminAccount",
+                "password": "password"
+            },
+            "accountdomainadmin": {
+                "email": "mtu@test.cloud",
+                "firstname": "Marvin",
+                "lastname": "TestDomainAdminAccount",
+                "username": "TestDomainAdminAccount",
+                "password": "password"
+            },
+            "accountroleuser": {
+                "email": "mtu@test.cloud",
+                "firstname": "Marvin",
+                "lastname": "TestUserAccount",
+                "username": "TestUserAccount",
+                "password": "password"
+            },
+            "roleadmin": {
+                "name": "MarvinFake Admin Role ",
+                "type": "Admin",
+                "description": "Fake Admin Role created by Marvin test"
+            },
+            "roleuser": {
+                "name": "MarvinFake User Role ",
+                "type": "User",
+                "description": "Fake User Role created by Marvin test",
+                "ispublic": False
+            },
+            "publicrole": {
+                "name": "MarvinFake Public Role ",
+                "type": "User",
+                "description": "Fake Public Role created by Marvin test"
+            },
+            "importrole": {
+                "name": "MarvinFake Import Role ",
+                "type": "User",
+                "description": "Fake Import User Role created by Marvin test",
+                "ispublic": True,
+                "rules": [{"rule":"list*", "permission":"allow","description":"Listing apis"},
+                           {"rule":"get*", "permission":"allow","description":"Get apis"},
+                           {"rule":"update*", "permission":"deny","description":"Update apis"}]
+            },
+            "roledomainadmin": {
+                "name": "MarvinFake DomainAdmin Role ",
+                "type": "DomainAdmin",
+                "description": "Fake Domain-Admin Role created by Marvin test",
+                "ispublic": False
+            },
+            "apiConfig": {
+                "listApis": "allow",
+                "listAccounts": "allow",
+                "listClusters": "deny",
+                "*VM*": "allow",
+                "*Host*": "deny"
+            }
+        }
+
+
+class TestPrivateRoles(cloudstackTestCase):
+    """Tests Visibility of private and public roles
+    """
+
+    def setUp(self):
+        self.apiclient = self.testClient.getApiClient()
+        self.testdata = TestData().testdata
+
+        self.testdata["roleadmin"]["name"] += self.getRandomString()
+        self.testdata["roledomainadmin"]["name"] += self.getRandomString()
+        self.testdata["roleuser"]["name"] += self.getRandomString()
+        self.cleanup = []
+
+        self.role_admin = Role.create(
+            self.apiclient,
+            self.testdata["roleadmin"]
+        )
+        self.cleanup.append(self.role_admin)
+
+        self.role_domain_admin = Role.create(
+            self.apiclient,
+            self.testdata["roledomainadmin"]
+        )
+        self.cleanup.append(self.role_domain_admin)
+
+        self.private_role = Role.create(
+            self.apiclient,
+            self.testdata["roleuser"]
+        )
+        self.cleanup.append(self.private_role)
+
+        self.account_admin = Account.create(
+            self.apiclient,
+            self.testdata["accountadmin"],
+            roleid=self.role_admin.id
+        )
+        self.cleanup.append(self.account_admin)
+
+        self.account_domain_admin = Account.create(
+            self.apiclient,
+            self.testdata["accountdomainadmin"],
+            roleid=self.role_domain_admin.id
+        )
+        self.cleanup.append(self.account_domain_admin)
+
+        self.admin_apiclient = self.testClient.getUserApiClient(
+            UserName=self.account_admin.name,
+            DomainName='ROOT',
+            type=1
+        )
+
+        self.domain_admin_apiclient = self.testClient.getUserApiClient(
+            UserName=self.account_domain_admin.name,
+            DomainName='ROOT',
+            type=2
+        )
+
+    def tearDown(self):
+        super(TestPrivateRoles, self).tearDown()
+
+    def getRandomString(self):
+        return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for _ in range(10))
+
+    def asserts_visibility_of_private_role(self, role_id):
+        list_roles_domain_admin = Role.list(self.domain_admin_apiclient, id=role_id)
+        self.assertEqual(
+            list_roles_domain_admin,
+            None,
+            "Domain Admins should not be able to list private roles"
+        )
+
+        list_roles_admin = Role.list(self.admin_apiclient, id=role_id)
+        self.assertNotEqual(
+            list_roles_admin,
+            None,
+            "Admins should be able to list private roles"
+        )
+
+    def asserts_visibility_of_public_role(self, role_id):
+        list_roles_domain_admin = Role.list(self.domain_admin_apiclient, id=role_id)
+        self.assertNotEqual(
+            list_roles_domain_admin,
+            None,
+            "Domain Admins should be able to list public roles"
+        )
+
+        list_roles_admin = Role.list(self.admin_apiclient, id=role_id)
+        self.assertNotEqual(
+            list_roles_admin,
+            None,
+            "Admins should be able to list public roles"
+        )
+
+    @attr(tags=['simulator', 'basic'], required_hardware=False)
+    def test_create_role(self):
+        """
+            1. Create a private role
+            2. Create a public role
+            3. Verify whether their visibility is as expected
+        """
+        self.testdata["roleuser"]["name"] += self.getRandomString()
+        self.testdata["publicrole"]["name"] += self.getRandomString()
+        private_role = Role.create(
+            self.apiclient,
+            self.testdata["roleuser"]
+        )
+        self.cleanup.append(self.private_role)
+        public_role = Role.create(
+            self.apiclient,
+            self.testdata["publicrole"]
+        )
+        self.cleanup.append(self.public_role)
+        self.asserts_visibility_of_private_role(private_role.id)
+        self.asserts_visibility_of_public_role(public_role.id)
+
+    @attr(tags=['simulator', 'basic'], required_hardware=False)
+    def test_update_role(self):
+        """
+            1. Create a public role
+            2. Check if its visibility is public
+            3. Update it to make it private
+            4. Verify if its visibility is private
+        """
+        self.testdata["publicrole"]["name"] += self.getRandomString()
+        role = Role.create(
+            self.apiclient,
+            self.testdata["publicrole"]
+        )
+        self.cleanup.append(role)
+        self.asserts_visibility_of_public_role(role.id)
+        role.update(self.apiclient, id=role.id, ispublic=False)
+        self.asserts_visibility_of_private_role(role.id)
+
+    @attr(tags=['simulator', 'basic'], required_hardware=False)
+    def test_import_role(self):
+        """
+            1. Import a public role
+            2. Import a private role
+            3. Verify their visibility
+        """
+        self.testdata["importrole"]["name"] += self.getRandomString()
+        imported_public_role = Role.importRole(
+            self.apiclient,
+            self.testdata["importrole"]
+        )
+        self.cleanup.append(imported_public_role)
+        self.testdata["importrole"]["name"] += self.getRandomString()
+        self.testdata["importrole"]["ispublic"] = False
+        imported_private_role = Role.importRole(
+            self.apiclient,
+            self.testdata["importrole"]
+        )
+        self.cleanup.append(imported_private_role)
+
+        self.asserts_visibility_of_public_role(imported_public_role.id)
+        self.asserts_visibility_of_private_role(imported_private_role.id)
+
+    @attr(tags=['simulator', 'basic'], required_hardware=False)
+    def test_login_private_role(self):
+        """
+            1. Crate a User account with a private role
+            2. Login with the created account
+            3. Verify that the login was successful
+        """
+        account_private_role = Account.create(
+            self.apiclient,
+            self.testdata["accountroleuser"],
+            roleid=self.private_role.id
+        )
+        self.cleanup.append(account_private_role)
+
+        response = User.login(
+            self.apiclient,
+            username=account_private_role.name,
+            password=self.testdata["accountroleuser"]["password"]
+        )
+
+        self.assertNotEqual(
+            response.sessionkey,
+            None,
+            "Accounts using private roles should be able to login."
+        )
diff --git a/test/integration/smoke/test_projects.py b/test/integration/smoke/test_projects.py
index 79d364f..f0696b4 100644
--- a/test/integration/smoke/test_projects.py
+++ b/test/integration/smoke/test_projects.py
@@ -1820,3 +1820,116 @@
                 "VM should be in Running state after project activation"
             )
         return
+
+class TestProjectWithNameDisplayTextAction(cloudstackTestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.testClient = super(
+            TestProjectWithNameDisplayTextAction,
+            cls).getClsTestClient()
+        cls.api_client = cls.testClient.getApiClient()
+        cls.services = Services().services
+        # Get Zone
+        cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+        cls.domain = get_domain(cls.api_client)
+        cls.services['mode'] = cls.zone.networktype
+        cls._cleanup = []
+
+        cls.account = Account.create(
+            cls.api_client,
+            cls.services["account"],
+            admin=True,
+            domainid=cls.domain.id
+        )
+        cls._cleanup.append(cls.account)
+        return
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestProjectWithNameDisplayTextAction, cls).tearDownClass()
+
+    def setUp(self):
+        self.apiclient = self.testClient.getApiClient()
+        self.dbclient = self.testClient.getDbConnection()
+        self.cleanup = []
+
+    def tearDown(self):
+        super(TestProjectWithNameDisplayTextAction, self).tearDown()
+
+    @attr(
+        tags=[
+            "advanced",
+            "basic",
+            "sg",
+            "eip",
+            "advancedns",
+            "simulator"],
+        required_hardware="false")
+    def test_11_create_project_with_empty_displayText(self):
+        """ create Project with Empty DisplayText
+        """
+        # Validate the following
+        # 1. Create a project while giving empty displayText
+        # 2. Verify displayText takes content of Project name.
+
+        self.services["project"]["displaytext"] = ""
+
+        project = Project.create(
+            self.apiclient,
+            self.services["project"],
+            account=self.account.name,
+            domainid=self.account.domainid
+        )
+        self.cleanup.append(project)
+
+        self.assertEqual(
+            project.displaytext,
+            project.name,
+            "displayText does not matches project name"
+        )
+        return
+
+    @attr(
+        tags=[
+            "advanced",
+            "basic",
+            "sg",
+            "eip",
+            "advancedns",
+            "simulator"],
+        required_hardware="false")
+    def test_12_update_project_name_display_text(self):
+        """ Create Project and update its name
+        """
+        # Validate the following
+        # 1. Create a project
+        # 2. Update project name and display text
+        # 2. Verify name and display text for the project are updated
+
+        project = Project.create(
+            self.apiclient,
+            self.services["project"],
+            account=self.account.name,
+            domainid=self.account.domainid
+        )
+        self.cleanup.append(project)
+
+        new_name = "NewName"
+        new_display_text = "NewDisplayText"
+        project.update(self.apiclient, name=new_name, displaytext=new_display_text)
+        updated_project = Project.list(
+            self.apiclient,
+            id=project.id,
+            listall=True
+        )[0]
+        self.assertEqual(
+            updated_project.name,
+            new_name,
+            "Project name not updated"
+        )
+        self.assertEqual(
+            updated_project.displaytext,
+            new_display_text,
+            "Project displaytext not updated"
+        )
+        return
diff --git a/test/integration/smoke/test_register_userdata.py b/test/integration/smoke/test_register_userdata.py
index bc38cd9..c89d08e 100644
--- a/test/integration/smoke/test_register_userdata.py
+++ b/test/integration/smoke/test_register_userdata.py
@@ -31,6 +31,8 @@
 from marvin.lib.utils import (validateList, cleanup_resources)
 from nose.plugins.attrib import attr
 from marvin.codes import PASS,FAIL
+import base64
+import email
 
 
 from marvin.lib.common import (get_domain, get_template)
@@ -589,21 +591,23 @@
             2. Link a userdata to template with override policy is append
             3. Deploy a VM with that template and also by passing another userdata id
             4. Since the override policy is append, userdata passed during VM deployment will be appended to template's
-            userdata and configured to VM. Verify the same by SSH into VM.
+            userdata and configured to VM as a multipart MIME userdata. Verify the same by SSH into VM.
         """
 
+        shellscript_userdata = str("#!/bin/bash\ndate > /provisioned")
         self.apiUserdata = UserData.register(
             self.apiclient,
             name="ApiUserdata",
-            userdata="QVBJdXNlcmRhdGE=", #APIuserdata
+            userdata=base64.encodebytes(shellscript_userdata.encode()).decode(),
             account=self.account.name,
             domainid=self.account.domainid
         )
 
+        cloudconfig_userdata = str("#cloud-config\npassword: atomic\nchpasswd: { expire: False }\nssh_pwauth: True")
         self.templateUserdata = UserData.register(
             self.apiclient,
             name="TemplateUserdata",
-            userdata="VGVtcGxhdGVVc2VyRGF0YQ==", #TemplateUserData
+            userdata=base64.encodebytes(cloudconfig_userdata.encode()).decode(),
             account=self.account.name,
             domainid=self.account.domainid
         )
@@ -700,10 +704,32 @@
         cmd = "curl http://%s/latest/user-data" % vr_ip
         res = ssh.execute(cmd)
         self.debug("Verifying userdata in the VR")
-        self.assertEqual(
-            str(res[0]),
-            "TemplateUserDataAPIuserdata",
-            "Failed to match userdata"
+        self.assertTrue(
+            res is not None and len(res) > 0,
+            "Resultant userdata is not valid"
+        )
+        msg = email.message_from_string('\n'.join(res))
+        self.assertTrue(
+            msg.is_multipart(),
+            "Failed to match multipart userdata"
+        )
+        shellscript_userdata_found = False
+        cloudconfig_userdata_found = False
+        for part in msg.get_payload():
+            content_type = part.get_content_type()
+            payload = part.get_payload(decode=True).decode()
+            if "shellscript" in content_type:
+                shellscript_userdata_found = shellscript_userdata == payload
+            elif "cloud-config" in content_type:
+                cloudconfig_userdata_found = cloudconfig_userdata == payload
+
+        self.assertTrue(
+            shellscript_userdata_found,
+            "Failed to find shellscript userdata in append result"
+        )
+        self.assertTrue(
+            cloudconfig_userdata_found,
+            "Failed to find cloud-config userdata in append result"
         )
 
     @attr(tags=['advanced', 'simulator', 'basic', 'sg', 'testnow'], required_hardware=True)
diff --git a/test/integration/smoke/test_safe_shutdown.py b/test/integration/smoke/test_safe_shutdown.py
new file mode 100644
index 0000000..d757bb6
--- /dev/null
+++ b/test/integration/smoke/test_safe_shutdown.py
@@ -0,0 +1,121 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from nose.plugins.attrib import attr
+from marvin.cloudstackTestCase import *
+from marvin.cloudstackAPI import *
+from marvin.lib.utils import *
+from marvin.lib.base import *
+from marvin.lib.common import *
+
+class TestSafeShutdown(cloudstackTestCase):
+    """
+        Tests safely shutting down the Management Server
+    """
+
+    def setUp(self):
+        self.apiclient = self.testClient.getApiClient()
+        self.mgtSvrDetails = self.config.__dict__["mgtSvr"][0].__dict__
+        self.cleanup = []
+
+    def tearDown(self):
+        self.startServer()
+        super(TestSafeShutdown, self).tearDown()
+
+    def isServerShutdown(self):
+        sshClient = SshClient(
+            self.mgtSvrDetails["mgtSvrIp"],
+            22,
+            self.mgtSvrDetails["user"],
+            self.mgtSvrDetails["passwd"]
+        )
+
+        timeout = time.time() + 300
+        while time.time() < timeout:
+            command = "service cloudstack-management status | grep dead"
+            results = sshClient.execute(command)
+
+            if len(results) > 0 and "(dead)" in results[0] :
+                return
+            time.sleep(30)
+        return self.fail("Management server did shut down, failing")
+
+    def isManagementUp(self):
+        try:
+            self.apiclient.listInfrastructure(listInfrastructure.listInfrastructureCmd())
+            return True
+        except Exception:
+            return False
+
+    def startServer(self):
+        """Start management server"""
+
+        sshClient = SshClient(
+                    self.mgtSvrDetails["mgtSvrIp"],
+            22,
+            self.mgtSvrDetails["user"],
+            self.mgtSvrDetails["passwd"]
+        )
+
+        command = "service cloudstack-management start"
+        sshClient.execute(command)
+
+        #Waits for management to come up in 5 mins, when it's up it will continue
+        timeout = time.time() + 300
+        while time.time() < timeout:
+            if self.isManagementUp() is True: return
+            time.sleep(5)
+        return self.fail("Management server did not come up, failing")
+
+    def run_async_cmd(self) :
+        return Project.create(
+            self.apiclient,
+            {"name": "test", "displaytext": "test"}
+        )
+
+    @attr(tags=["advanced", "smoke"])
+    def test_01_prepare_and_cancel_shutdown(self):
+        try :
+            prepare_for_shutdown_cmd = prepareForShutdown.prepareForShutdownCmd()
+            prepare_for_shutdown_cmd.managementserverid = 1
+            self.apiclient.prepareForShutdown(prepare_for_shutdown_cmd)
+            try :
+                self.run_async_cmd()
+            except Exception as e:
+                self.debug("Prepare for shutdown check successful, API failure: %s" % e)
+        finally :
+            cancel_shutdown_cmd = cancelShutdown.cancelShutdownCmd()
+            cancel_shutdown_cmd.managementserverid = 1
+            response = self.apiclient.cancelShutdown(cancel_shutdown_cmd)
+            self.assertEqual(
+                response.shutdowntriggered,
+                False,
+                "Failed to cancel shutdown"
+            )
+            ## Just to be sure, run another async command
+            project = self.run_async_cmd()
+            self.cleanup.append(project)
+
+    @attr(tags=["advanced", "smoke"])
+    def test_02_trigger_shutdown(self):
+        try :
+            cmd = triggerShutdown.triggerShutdownCmd()
+            cmd.managementserverid = 1
+            self.apiclient.triggerShutdown(cmd)
+            self.isServerShutdown()
+        finally :
+            self.startServer()
diff --git a/test/integration/smoke/test_set_sourcenat.py b/test/integration/smoke/test_set_sourcenat.py
new file mode 100644
index 0000000..e89af9f
--- /dev/null
+++ b/test/integration/smoke/test_set_sourcenat.py
@@ -0,0 +1,274 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+""" BVT tests for Network Life Cycle
+"""
+# Import Local Modules
+from marvin.codes import (FAILED, STATIC_NAT_RULE, LB_RULE,
+                          NAT_RULE, PASS)
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.lib.base import (Account,
+                             VPC,
+                             VpcOffering,
+                             ServiceOffering,
+                             PublicIPAddress,
+                             Network,
+                             NetworkOffering)
+from marvin.lib.common import (get_domain,
+                               get_free_vlan,
+                               get_zone,
+                               get_template,
+                               get_test_template,
+                               list_publicIP)
+
+from nose.plugins.attrib import attr
+# Import System modules
+import time
+import logging
+
+_multiprocess_shared_ = True
+
+logger = logging.getLogger('TestSetSourceNatIp')
+stream_handler = logging.StreamHandler()
+logger.setLevel(logging.DEBUG)
+logger.addHandler(stream_handler)
+
+
+class TestSetSourceNatIp(cloudstackTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        testClient = super(TestSetSourceNatIp, cls).getClsTestClient()
+        cls.apiclient = testClient.getApiClient()
+        cls.services = testClient.getParsedTestDataConfig()
+
+        # Get Zone, Domain and templates
+        cls.domain = get_domain(cls.apiclient)
+        cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
+        cls.services['mode'] = cls.zone.networktype
+        cls._cleanup = []
+        # Create Accounts & networks
+        cls.account = Account.create(
+            cls.apiclient,
+            cls.services["account"],
+            admin=True,
+            domainid=cls.domain.id
+        )
+        cls._cleanup.append(cls.account)
+
+        cls.services["network"]["zoneid"] = cls.zone.id
+
+        cls.vpc_offering = VpcOffering.create(
+            cls.apiclient,
+            cls.services["vpc_offering"],
+        )
+        cls._cleanup.append(cls.vpc_offering)
+        cls.vpc_offering.update(cls.apiclient, state='Enabled')
+        cls.services["vpc"]["vpcoffering"] = cls.vpc_offering.id
+
+        cls.network_offering = NetworkOffering.create(
+            cls.apiclient,
+            cls.services["network_offering"],
+        )
+        cls._cleanup.append(cls.network_offering)
+        # Enable Network offering
+        cls.network_offering.update(cls.apiclient, state='Enabled')
+
+        cls.services["network"]["networkoffering"] = cls.network_offering.id
+
+        cls.service_offering = ServiceOffering.create(
+            cls.apiclient,
+            cls.services["service_offerings"]["tiny"],
+        )
+        cls._cleanup.append(cls.service_offering)
+
+        cls.hypervisor = testClient.getHypervisorInfo()
+        cls.template = get_test_template(
+            cls.apiclient,
+            cls.zone.id,
+            cls.hypervisor
+        )
+        if cls.template == FAILED:
+            assert False, "get_test_template() failed to return template"
+
+        cls.services["virtual_machine"]["zoneid"] = cls.zone.id
+        network = Network.create(
+            cls.apiclient,
+            cls.services["network"],
+            cls.account.name,
+            cls.account.domainid
+        )
+        cls._cleanup.append(network)
+        ip_address = PublicIPAddress.create(
+            cls.apiclient,
+            cls.account.name,
+            cls.zone.id,
+            cls.account.domainid
+        )
+        cls._cleanup.append(ip_address)
+        cls.ip_to_want = ip_address.ipaddress.ipaddress
+        cls.debug(f'==== my local ip: {cls.ip_to_want}')
+        ip_address.delete(cls.apiclient)
+        cls._cleanup.remove(ip_address)
+        network.delete(cls.apiclient)
+        cls._cleanup.remove(network)
+        return
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestSetSourceNatIp, cls).tearDownClass()
+
+    def setUp(self):
+        self.cleanup = []
+
+    def tearDown(self):
+        super(TestSetSourceNatIp, self).tearDown()
+
+    @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
+    def test_01_create_network_with_specified_source_nat_ip_address(self):
+        """
+        For creation of network witjh a specified address
+        """
+
+
+        self.services["network"]["networkoffering"] = self.network_offering.id
+        network = Network.create(
+            self.apiclient,
+            self.services["network"],
+            self.account.name,
+            self.account.domainid,
+            sourcenatipaddress = self.ip_to_want
+        )
+        self.cleanup.append(network)
+
+        self.validate_source_nat(network=network, ip=self.ip_to_want)
+
+
+    @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
+    def test_02_change_source_nat_ip_address_for_network(self):
+        """
+        Test changing a networks source NAT IP address
+        """
+        network = Network.create(
+            self.apiclient,
+            self.services["network"],
+            self.account.name,
+            self.account.domainid,
+            sourcenatipaddress = self.ip_to_want
+        )
+        self.cleanup.append(network)
+        second_ip = PublicIPAddress.create(
+            self.apiclient,
+            self.account.name,
+            self.zone.id,
+            self.account.domainid,
+            networkid=network.id
+        )
+        self.cleanup.append(second_ip)
+        self.debug(f'==== second ip: {second_ip.ipaddress.ipaddress}')
+
+        self.validate_source_nat(network=network, ip=self.ip_to_want)
+
+        network.update(self.apiclient, sourcenatipaddress=second_ip.ipaddress.ipaddress)
+
+        self.validate_source_nat(network=network, ip=second_ip.ipaddress.ipaddress)
+
+        return
+
+
+    @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
+    def test_03_create_vpc_with_specified_source_nat_ip_address(self):
+        """
+        Test for creation of a VPC with a specified address
+        """
+
+        vpc = VPC.create(
+            self.apiclient,
+            self.services["vpc"],
+            self.vpc_offering.id,
+            self.zone.id,
+            sourcenatipaddress = self.ip_to_want
+        )
+        self.cleanup.append(vpc)
+
+        self.validate_source_nat(vpc=vpc, ip=self.ip_to_want)
+
+    @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
+    def test_04_change_source_nat_ip_address_for_vpc(self):
+        """
+        Test changing a networks source NAT IP address
+        """
+        vpc = VPC.create(
+            self.apiclient,
+            self.services["vpc"],
+            self.vpc_offering.id,
+            self.zone.id,
+            account=self.account.name,
+            domainid = self.account.domainid,
+            sourcenatipaddress = self.ip_to_want
+        )
+        self.cleanup.append(vpc)
+        second_ip = PublicIPAddress.create(
+            self.apiclient,
+            self.account.name,
+            self.zone.id,
+            self.account.domainid,
+            vpcid=vpc.id
+        )
+        self.debug(f'==== second ip: {second_ip.ipaddress.ipaddress}')
+
+        self.validate_source_nat(vpc=vpc, ip=self.ip_to_want)
+
+        vpc.update(self.apiclient, sourcenatipaddress=second_ip.ipaddress.ipaddress)
+
+        self.validate_source_nat(vpc=vpc, ip=second_ip.ipaddress.ipaddress)
+
+        return
+
+
+    def validate_source_nat(self, network=None, vpc=None, ip=None):
+        list_pub_ip_addr_resp = None
+        if network:
+            list_pub_ip_addr_resp = list_publicIP(
+                self.apiclient,
+                associatednetworkid=network.id,
+                listall=True,
+                issourcenat=True
+            )
+        elif vpc:
+            list_pub_ip_addr_resp = list_publicIP(
+                self.apiclient,
+                vpcid=vpc.id,
+                listall=True,
+                issourcenat=True
+            )
+        self.assertEqual(
+            isinstance(list_pub_ip_addr_resp, list),
+            True,
+            "Check list response returns a valid list"
+        )
+        self.assertNotEqual(
+            len(list_pub_ip_addr_resp),
+            0,
+            "Check if new IP Address is associated"
+        )
+        self.debug(f'==== my result {list_pub_ip_addr_resp[0]}')
+        self.assertEqual(
+            list_pub_ip_addr_resp[0].ipaddress,
+            ip,
+            f"Check Correct IP Address is returned in the List, expected {ip} but got {list_pub_ip_addr_resp[0].ipaddress}"
+        )
diff --git a/test/integration/smoke/test_vm_schedule.py b/test/integration/smoke/test_vm_schedule.py
new file mode 100644
index 0000000..e66470d
--- /dev/null
+++ b/test/integration/smoke/test_vm_schedule.py
@@ -0,0 +1,609 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+""" P1 tests for VM Schedule
+"""
+from marvin.cloudstackTestCase import cloudstackTestCase
+from marvin.lib.base import Account, ServiceOffering, VirtualMachine, VMSchedule
+from marvin.lib.common import get_domain, get_zone, get_template
+from marvin.lib.utils import cleanup_resources
+
+# Import Local Modules
+from nose.plugins.attrib import attr
+
+import datetime
+import time
+
+
+class Services:
+    """Test Snapshots Services"""
+
+    def __init__(self):
+        self.services = {
+            "account": {
+                "email": "test@test.com",
+                "firstname": "Test",
+                "lastname": "User",
+                "username": "test",
+                # Random characters are appended for unique
+                # username
+                "password": "password",
+            },
+            "service_offering": {
+                "name": "Tiny Instance",
+                "displaytext": "Tiny Instance",
+                "cpunumber": 1,
+                "cpuspeed": 200,  # in MHz
+                "memory": 256,  # In MBs
+            },
+            "disk_offering": {
+                "displaytext": "Small Disk",
+                "name": "Small Disk",
+                "disksize": 1,
+            },
+            "server": {
+                "displayname": "TestVM",
+                "username": "root",
+                "password": "password",
+                "ssh_port": 22,
+                "privateport": 22,
+                "publicport": 22,
+                "protocol": "TCP",
+            },
+            "mgmt_server": {
+                "ipaddress": "192.168.100.21",
+                "username": "root",
+                "password": "password",
+                "port": 22,
+            },
+            "templates": {
+                "displaytext": "Template",
+                "name": "Template",
+                "ostype": "CentOS 5.3 (64-bit)",
+                "templatefilter": "self",
+            },
+            "ostype": "CentOS 5.3 (64-bit)",
+        }
+
+
+class TestVMSchedule(cloudstackTestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.testClient = super(TestVMSchedule, cls).getClsTestClient()
+        cls.api_client = cls.testClient.getApiClient()
+
+        cls._cleanup = []
+
+        cls.hypervisor = cls.testClient.getHypervisorInfo()
+
+        cls.services = Services().services
+        # Get Zone, Domain and templates
+        cls.domain = get_domain(cls.api_client)
+        cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
+        cls.services["mode"] = cls.zone.networktype
+        template = get_template(cls.api_client, cls.zone.id, cls.services["ostype"])
+
+        cls.services["domainid"] = cls.domain.id
+        cls.services["server"]["zoneid"] = cls.zone.id
+
+        cls.services["templates"]["ostypeid"] = template.ostypeid
+        cls.services["zoneid"] = cls.zone.id
+
+        # Create VMs, NAT Rules etc
+        cls.account = Account.create(
+            cls.api_client, cls.services["account"], domainid=cls.domain.id
+        )
+        cls._cleanup.append(cls.account)
+
+        cls.services["account"] = cls.account.name
+
+        cls.service_offering = ServiceOffering.create(
+            cls.api_client, cls.services["service_offering"]
+        )
+        cls._cleanup.append(cls.service_offering)
+        cls.virtual_machine = VirtualMachine.create(
+            cls.api_client,
+            cls.services["server"],
+            templateid=template.id,
+            accountid=cls.account.name,
+            domainid=cls.account.domainid,
+            serviceofferingid=cls.service_offering.id,
+        )
+        return
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestVMSchedule, cls).tearDownClass()
+
+    def setUp(self):
+        self.apiclient = self.testClient.getApiClient()
+        self.dbclient = self.testClient.getDbConnection()
+        self.cleanup = []
+        return
+
+    def tearDown(self):
+        super(TestVMSchedule, self).tearDown()
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_01_vmschedule_create(self):
+        """Test VM Schedule Creation in cron format and validate responses"""
+
+        # Validate the following
+        # 1. Create VM Schedule in cron format
+        # 2. List VM Schedule and verify the response
+        # 3. Delete VM Schedule and verify the response
+
+        # Create VM Schedule
+        schedule = "0 0 1 * *"
+        vmschedule = VMSchedule.create(
+            self.apiclient,
+            self.virtual_machine.id,
+            "start",
+            schedule,
+            datetime.datetime.now().astimezone().tzinfo,
+            # Current date minutes in format "2014-01-01 00:00:00"
+            (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+            enabled=True,
+        )
+
+        self.cleanup.append(vmschedule)
+
+        self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
+
+        # List VM Schedule
+        vmschedules = VMSchedule.list(
+            self.apiclient, self.virtual_machine.id, id=vmschedule.id
+        )
+
+        self.assertEqual(
+            isinstance(vmschedules, list),
+            True,
+            "Check list response returns a valid list",
+        )
+
+        self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule list")
+
+        self.debug("List VM Schedule response: %s" % vmschedules[0].__dict__)
+
+        self.assertEqual(
+            vmschedules[0].id,
+            vmschedule.id,
+            "Check VM Schedule ID in list resources call",
+        )
+
+        self.assertEqual(
+            vmschedules[0].virtualmachineid,
+            self.virtual_machine.id,
+            "Check VM ID in list resources call",
+        )
+
+        self.assertEqual(
+            vmschedules[0].schedule,
+            schedule,
+            "Check VM Schedule in list resources call",
+        )
+
+        self.assertEqual(
+            vmschedules[0].timezone,
+            str(datetime.datetime.now().astimezone().tzinfo),
+            "Check VM Schedule timezone in list resources call",
+        )
+
+        # Check for entry in vm_scheduled_job in db
+        vmscheduled_job = self.dbclient.execute(
+            "select * from vm_scheduled_job where vm_schedule_id IN (SELECT id FROM vm_schedule WHERE uuid = '%s')"
+            % vmschedule.id,
+            db="cloud",
+        )
+
+        self.assertIsInstance(
+            vmscheduled_job,
+            list,
+            "Check if VM Schedule exists in vm_scheduled_job table",
+        )
+
+        self.assertGreater(
+            len(vmscheduled_job),
+            0,
+            "Check if VM Schedule exists in vm_scheduled_job table",
+        )
+        return
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_02_vmschedule_create_parameter_exceptions(self):
+        """Test VM Schedule Creation exceptions with invalid parameters"""
+
+        # Validate the following
+        # 1. Create VM Schedule with invalid virtual machine ID
+        # 2. Create VM Schedule with invalid schedule
+        # 3. Create VM Schedule with invalid start date
+        # 5. Create VM Schedule with invalid action
+        # 6. Create VM Schedule with invalid end date
+
+        # Create VM Schedule with invalid virtual machine ID
+        with self.assertRaises(Exception):
+            VMSchedule.create(
+                self.apiclient,
+                "invalid",
+                "start",
+                "0 0 1 * *",
+                datetime.datetime.now().astimezone().tzinfo,
+                # Current date minutes in format "2014-01-01 00:00:00"
+                (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                    "%Y-%m-%d %H:%M:%S"
+                ),
+            )
+
+        # Create VM Schedule with invalid schedule
+        with self.assertRaises(Exception):
+            VMSchedule.create(
+                self.apiclient,
+                self.virtual_machine.id,
+                "start",
+                "invalid",
+                datetime.datetime.now().astimezone().tzinfo,
+                # Current date minutes in format "2014-01-01 00:00:00"
+                (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                    "%Y-%m-%d %H:%M:%S"
+                ),
+            )
+
+        # Create VM Schedule with invalid start date
+        with self.assertRaises(Exception):
+            VMSchedule.create(
+                self.apiclient,
+                self.virtual_machine.id,
+                "start",
+                "0 0 1 * *",
+                datetime.datetime.now().astimezone().tzinfo,
+                # Current date minutes in format "2014-01-01 00:00:00"
+                "invalid",
+            )
+
+        # Create VM Schedule with invalid action
+        with self.assertRaises(Exception):
+            VMSchedule.create(
+                self.apiclient,
+                self.virtual_machine.id,
+                "invalid",
+                "0 0 1 * *",
+                datetime.datetime.now().astimezone().tzinfo,
+                # Current date minutes in format "2014-01-01 00:00:00"
+                (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                    "%Y-%m-%d %H:%M:%S"
+                ),
+            )
+
+        # test invalid end date
+        with self.assertRaises(Exception):
+            VMSchedule.create(
+                self.apiclient,
+                self.virtual_machine.id,
+                "start",
+                "0 0 1 * *",
+                datetime.datetime.now().astimezone().tzinfo,
+                # Current date minutes in format "2014-01-01 00:00:00"
+                (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                    "%Y-%m-%d %H:%M:%S"
+                ),
+                enddate="invalid",
+            )
+        return
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_03_vmschedule_update(self):
+        """Test VM Schedule Update in cron format and validate responses"""
+
+        # Validate the following
+        # 1. Create VM Schedule in cron format
+        # 2. Update VM Schedule and verify the response
+
+        # Create VM Schedule
+        schedule = "0 0 1 * *"
+        vmschedule = VMSchedule.create(
+            self.apiclient,
+            self.virtual_machine.id,
+            "start",
+            schedule,
+            datetime.datetime.now().astimezone().tzinfo,
+            # Current date minutes in format "2014-01-01 00:00:00"
+            (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+        )
+
+        self.cleanup.append(vmschedule)
+
+        self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
+
+        # Update VM Schedule
+        new_schedule = "0 0 2 * *"
+        vmschedule.update(
+            self.apiclient,
+            id=vmschedule.id,
+            virtualmachineid=self.virtual_machine.id,
+            description="TestVM",
+            schedule=new_schedule,
+            timezone=datetime.datetime.now().astimezone().tzinfo,
+            startdate=(
+                datetime.datetime.now() + datetime.timedelta(minutes=10)
+            ).strftime("%Y-%m-%d %H:%M:%S"),
+            enddate=(datetime.datetime.now() + datetime.timedelta(hours=10)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+        )
+
+        self.debug("Updated VM Schedule with ID: %s" % vmschedule.id)
+
+        # List VM Schedule
+        vmschedules = VMSchedule.list(
+            self.apiclient, self.virtual_machine.id, id=vmschedule.id
+        )
+        self.assertEqual(
+            isinstance(vmschedules, list),
+            True,
+            "Check list response returns a valid list",
+        )
+
+        self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule list")
+
+        self.debug("List VM Schedule response: %s" % vmschedules[0].__dict__)
+
+        self.assertEqual(
+            vmschedules[0].id,
+            vmschedule.id,
+            "Check VM Schedule ID in list resources call",
+        )
+
+        self.assertEqual(
+            vmschedules[0].virtualmachineid,
+            self.virtual_machine.id,
+            "Check VM ID in list resources call",
+        )
+
+        self.assertEqual(
+            vmschedules[0].schedule,
+            new_schedule,
+            "Check VM Schedule in list resources call",
+        )
+        return
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_04_vmschedule_update_parameter_exceptions(self):
+        """Test VM Schedule Update exceptions with invalid parameters"""
+
+        # Validate the following
+        # 1. Update VM Schedule with invalid schedule
+        # 2. Update VM Schedule with invalid start date
+        # 3. Update VM Schedule with invalid ID
+        # 4. Update VM Schedule with invalid end date
+
+        # Create VM Schedule
+        schedule = "0 0 1 * *"
+        vmschedule = VMSchedule.create(
+            self.apiclient,
+            self.virtual_machine.id,
+            "start",
+            schedule,
+            datetime.datetime.now().astimezone().tzinfo,
+            # Current date minutes in format "2014-01-01 00:00:00"
+            (datetime.datetime.now() + datetime.timedelta(minutes=5)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+        )
+
+        self.cleanup.append(vmschedule)
+
+        self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
+
+        # Update VM Schedule with invalid schedule
+        with self.assertRaises(Exception):
+            vmschedule.update(
+                self.apiclient,
+                id=vmschedule.id,
+                virtualmachineid=self.virtual_machine.id,
+                description="TestVM",
+                schedule="invalid",
+                timezone=datetime.datetime.now().astimezone().tzinfo,
+                startdate=(
+                    datetime.datetime.now() + datetime.timedelta(minutes=5)
+                ).strftime("%Y-%m-%d %H:%M:%S"),
+            )
+
+        # Update VM Schedule with invalid start date
+        with self.assertRaises(Exception):
+            vmschedule.update(
+                self.apiclient,
+                id=vmschedule.id,
+                virtualmachineid=self.virtual_machine.id,
+                description="TestVM",
+                schedule=schedule,
+                timezone=datetime.datetime.now().astimezone().tzinfo,
+                startdate=(
+                    datetime.datetime.now() - datetime.timedelta(days=1)
+                ).strftime("%Y-%m-%d %H:%M:%S"),
+            )
+
+        # Update VM Schedule with invalid ID
+        with self.assertRaises(Exception):
+            vmschedule.update(
+                self.apiclient,
+                id="invalid",
+                virtualmachineid=self.virtual_machine.id,
+                description="TestVM",
+                schedule=schedule,
+                timezone=datetime.datetime.now().astimezone().tzinfo,
+                startdate=(
+                    datetime.datetime.now() + datetime.timedelta(minutes=5)
+                ).strftime("%Y-%m-%d %H:%M:%S"),
+            )
+
+        # Update VM Schedule with invalid end date
+        with self.assertRaises(Exception):
+            vmschedule.update(
+                self.apiclient,
+                id=vmschedule.id,
+                virtualmachineid=self.virtual_machine.id,
+                description="TestVM",
+                schedule=schedule,
+                timezone=datetime.datetime.now().astimezone().tzinfo,
+                startdate=(
+                    datetime.datetime.now() + datetime.timedelta(minutes=5)
+                ).strftime("%Y-%m-%d %H:%M:%S"),
+                enddate=(
+                    datetime.datetime.now() - datetime.timedelta(minutes=5)
+                ).strftime("%Y-%m-%d %H:%M:%S"),
+            )
+
+        return
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_05_vmschedule_test_e2e(self):
+        # Validate the following
+        # 1. Create 2 VM Schedules - start and stop
+        # 2. Verify VM Schedule is created
+        # 3. Verify VM is stopped after schedule time
+        # 4. Verify VM is started after schedule time
+        # 5. Delete VM Schedule
+        # 6. Verify VM Schedule is deleted
+        # 7. Verify VM is not stopped after schedule time
+        # 8. Verify VM is not started after schedule time
+
+        # Create VM Schedule - start
+        start_schedule = "*/2 * * * *"
+        start_vmschedule = VMSchedule.create(
+            self.apiclient,
+            self.virtual_machine.id,
+            "start",
+            start_schedule,
+            datetime.datetime.now().astimezone().tzinfo,
+            # Current date minutes in format "2014-01-01 00:00:00"
+            (datetime.datetime.now() + datetime.timedelta(seconds=5)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+            enabled=True,
+        )
+
+        self.debug("Created VM Schedule with ID: %s" % start_vmschedule.id)
+
+        # Create VM Schedule - stop
+        stop_schedule = "*/1 * * * *"
+        stop_vmschedule = VMSchedule.create(
+            self.apiclient,
+            self.virtual_machine.id,
+            "stop",
+            stop_schedule,
+            datetime.datetime.now().astimezone().tzinfo,
+            # Current date minutes in format "2014-01-01 00:00:00"
+            (datetime.datetime.now() + datetime.timedelta(seconds=5)).strftime(
+                "%Y-%m-%d %H:%M:%S"
+            ),
+            enabled=True,
+        )
+
+        self.debug("Created VM Schedule with ID: %s" % stop_vmschedule.id)
+
+        # Verify VM Schedule is created
+        vmschedules = VMSchedule.list(
+            self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
+        )
+
+        self.assertEqual(
+            isinstance(vmschedules, list),
+            True,
+            "Check list response returns a valid list",
+        )
+        self.assertNotEqual(len(vmschedules), 0, "Check VM Schedule is created")
+
+        # poll every 10 seconds (max waiting time is 6 minutes) and check VM's state for changes
+        previous_state = self.virtual_machine.state
+        self.debug("VM state: %s" % self.virtual_machine.state)
+        is_stop_schedule_working = False
+        is_start_schedule_working = False
+        for i in range(0, 36):
+            time.sleep(10)
+            current_state = self.virtual_machine.update(self.apiclient).state
+            self.debug("Polling VM state: %s" % current_state)
+            if previous_state in ("Running", "Starting") and current_state in (
+                "Stopped",
+                "Stopping",
+            ):
+                is_stop_schedule_working = True
+            elif previous_state in ("Stopped", "Stopping") and current_state in (
+                "Running",
+                "Starting",
+            ):
+                is_start_schedule_working = True
+            if is_start_schedule_working and is_stop_schedule_working:
+                break
+            previous_state = current_state
+
+        self.debug("Is stop schedule working: %s" % is_stop_schedule_working)
+        self.debug("Is start schedule working: %s" % is_start_schedule_working)
+
+        self.assertTrue(
+            is_stop_schedule_working,
+            "VM switched states from Running to Stopped at least once",
+        )
+
+        self.assertTrue(
+            is_start_schedule_working,
+            "VM switched states from Stopped to Running at least once",
+        )
+
+        # Delete VM Schedule
+        start_vmschedule.delete(self.apiclient)
+        stop_vmschedule.delete(self.apiclient)
+
+        # To ensure that all vm schedules have been deleted and all of their jobs have been completed
+        time.sleep(15)
+
+        # Verify VM Schedule is deleted
+        self.assertEqual(
+            VMSchedule.list(
+                self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
+            ),
+            None,
+            "Check VM Schedule is deleted",
+        )
+        self.assertEqual(
+            VMSchedule.list(
+                self.apiclient, self.virtual_machine.id, id=stop_vmschedule.id
+            ),
+            None,
+            "Check VM Schedule is deleted",
+        )
+
+        # Verify VM does not switch states after deleting schedules at least for 2 minutes
+        previous_state = self.virtual_machine.update(self.apiclient).state
+        state_changed = False
+        for i in range(0, 4):
+            time.sleep(30)
+            current_state = self.virtual_machine.update(self.apiclient).state
+            if previous_state != current_state:
+                self.debug(
+                    "VM changed state from %s to %s" % (previous_state, current_state)
+                )
+                state_changed = True
+                break
+
+        self.assertFalse(
+            state_changed,
+            "VM did not switch states after schedule time",
+        )
+        return
diff --git a/test/pom.xml b/test/pom.xml
index 2106fa7..1692a13 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index 1352800..bd102e9 100644
--- a/tools/apidoc/gen_toc.py
+++ b/tools/apidoc/gen_toc.py
@@ -248,7 +248,8 @@
     'Rolling': 'Rolling Maintenance',
     'importVsphereStoragePolicies' : 'vSphere storage policies',
     'listVsphereStoragePolicies' : 'vSphere storage policies',
-    'ConsoleEndpoint': 'Console Endpoint'
+    'ConsoleEndpoint': 'Console Endpoint',
+    'Shutdown': 'Shutdown'
 }
 
 
diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml
index a86de3d..43f4bc8 100644
--- a/tools/apidoc/pom.xml
+++ b/tools/apidoc/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-tools</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <properties>
diff --git a/tools/checkstyle/pom.xml b/tools/checkstyle/pom.xml
index d5ea235..bef394c 100644
--- a/tools/checkstyle/pom.xml
+++ b/tools/checkstyle/pom.xml
@@ -22,7 +22,7 @@
     <name>Apache CloudStack Developer Tools - Checkstyle Configuration</name>
     <groupId>org.apache.cloudstack</groupId>
     <artifactId>checkstyle</artifactId>
-    <version>4.18.1.0-SNAPSHOT</version>
+    <version>4.19.0.0-SNAPSHOT</version>
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml
index 299480e..d91e9f2 100644
--- a/tools/devcloud-kvm/pom.xml
+++ b/tools/devcloud-kvm/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-tools</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/tools/devcloud4/pom.xml b/tools/devcloud4/pom.xml
index adf5933..6e31176 100644
--- a/tools/devcloud4/pom.xml
+++ b/tools/devcloud4/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-tools</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index 5b132f2..9c135b0 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -20,7 +20,7 @@
 FROM ubuntu:22.04
 
 MAINTAINER "Apache CloudStack" <dev@cloudstack.apache.org>
-LABEL Vendor="Apache.org" License="ApacheV2" Version="4.18.1.0"
+LABEL Vendor="Apache.org" License="ApacheV2" Version="4.19.0.0"
 
 ARG DEBIAN_FRONTEND=noninteractive
 
diff --git a/tools/docker/Dockerfile.marvin b/tools/docker/Dockerfile.marvin
index f0329fa..3faf721 100644
--- a/tools/docker/Dockerfile.marvin
+++ b/tools/docker/Dockerfile.marvin
@@ -20,11 +20,11 @@
 FROM python:2
 
 MAINTAINER "Apache CloudStack" <dev@cloudstack.apache.org>
-LABEL Vendor="Apache.org" License="ApacheV2" Version="4.18.1.0"
+LABEL Vendor="Apache.org" License="ApacheV2" Version="4.19.0.0"
 
 ENV WORK_DIR=/marvin
 
-ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.18.1.0-SNAPSHOT.tar.gz
+ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.19.0.0-SNAPSHOT.tar.gz
 
 RUN apt-get update && apt-get install -y vim
 RUN pip install --upgrade paramiko nose requests
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 9d28d5a..5906d2f 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -108,6 +108,8 @@
             cmd.roleid = services["roleid"]
         if "description" in services:
             cmd.description = services["description"]
+        if "ispublic" in services:
+            cmd.ispublic = services["ispublic"]
 
         return Role(apiclient.createRole(cmd).__dict__)
 
@@ -122,6 +124,8 @@
             cmd.description = services["description"]
         if "forced" in services:
             cmd.type = services["forced"]
+        if "ispublic" in services:
+            cmd.ispublic = services["ispublic"]
 
         return Role(apiclient.importRole(cmd).__dict__)
 
@@ -3481,7 +3485,8 @@
                networkofferingid=None, projectid=None,
                subdomainaccess=None, zoneid=None,
                gateway=None, netmask=None, vpcid=None, aclid=None, vlan=None,
-               externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None, publicmtu=None, privatemtu=None):
+               externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None, publicmtu=None, privatemtu=None,
+               sourcenatipaddress=None):
         """Create Network for account"""
         cmd = createNetwork.createNetworkCmd()
         cmd.name = services["name"]
@@ -3563,6 +3568,8 @@
             cmd.publicmtu = publicmtu
         if privatemtu:
             cmd.privatemtu = privatemtu
+        if sourcenatipaddress:
+            cmd.sourcenatipaddress = sourcenatipaddress
         return Network(apiclient.createNetwork(cmd).__dict__)
 
     def delete(self, apiclient):
@@ -6514,3 +6521,182 @@
         cmd.policyuuid = policyuuid
         cmd.ruleuuid = ruleuuid
         return apiclient.removeTungstenFabricPolicyRule(cmd)
+
+class GuestOSCategory:
+    """Manage Guest OS Categories"""
+
+    def __init__(self, items, services):
+        self.__dict__.update(items)
+
+    @classmethod
+    def list(cls, apiclient, id=None, name=None, **kwargs):
+        """List all Guest OS categories"""
+        cmd = listOsCategories.listOsCategoriesCmd()
+        [setattr(cmd, k, v) for k, v in list(kwargs.items())]
+        if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
+            cmd.listall = True
+        if id is not None:
+            cmd.id = id
+        if name is not None:
+            cmd.name = name
+
+        return (apiclient.listOsCategories(cmd))
+
+class GuestOS:
+    """Manage Guest OS"""
+
+    def __init__(self, items, services):
+        self.__dict__.update(items)
+
+    @classmethod
+    def add(cls, apiclient, osdisplayname=None,
+                  oscategoryid=None, name=None, details=None):
+        """Add Guest OS"""
+        cmd = addGuestOs.addGuestOsCmd()
+        cmd.osdisplayname = osdisplayname
+        cmd.oscategoryid = oscategoryid
+        if name is not None:
+            cmd.name = name
+        if details is not None:
+            cmd.details = details
+
+        return (apiclient.addGuestOs(cmd))
+
+    @classmethod
+    def remove(cls, apiclient, id):
+        """Remove Guest OS"""
+        cmd = removeGuestOs.removeGuestOsCmd()
+        cmd.id = id
+
+        return apiclient.removeGuestOs(cmd)
+
+    @classmethod
+    def update(cls, apiclient, id, osdisplayname=None, details=None):
+        """Update Guest OS"""
+        cmd = updateGuestOs.updateGuestOsCmd()
+        cmd.id = id
+        cmd.osdisplayname = osdisplayname
+        if details is not None:
+            cmd.details = details
+
+        return apiclient.updateGuestOs(cmd)
+
+    @classmethod
+    def list(cls, apiclient, id=None, oscategoryid=None, description=None, **kwargs):
+        """List all Guest OS"""
+        cmd = listOsTypes.listOsTypesCmd()
+        [setattr(cmd, k, v) for k, v in list(kwargs.items())]
+        if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
+            cmd.listall = True
+        if id is not None:
+            cmd.id = id
+        if oscategoryid is not None:
+            cmd.oscategoryid = oscategoryid
+        if description is not None:
+            cmd.description = description
+
+        return (apiclient.listOsTypes(cmd))
+
+class GuestOsMapping:
+    """Manage Guest OS Mappings"""
+
+    def __init__(self, items, services):
+        self.__dict__.update(items)
+
+    @classmethod
+    def add(cls, apiclient, ostypeid=None,
+                  hypervisor=None, hypervisorversion=None,
+                  osnameforhypervisor=None, osmappingcheckenabled=None, forced=None):
+        """Add Guest OS mapping"""
+        cmd = addGuestOsMapping.addGuestOsMappingCmd()
+        cmd.ostypeid = ostypeid
+        cmd.hypervisor = hypervisor
+        cmd.hypervisorversion = hypervisorversion
+        cmd.osnameforhypervisor = osnameforhypervisor
+        if osmappingcheckenabled is not None:
+            cmd.osmappingcheckenabled = osmappingcheckenabled
+        if forced is not None:
+            cmd.forced = forced
+
+        return (apiclient.addGuestOsMapping(cmd))
+
+    @classmethod
+    def remove(cls, apiclient, id):
+        """Remove Guest OS mapping"""
+        cmd = removeGuestOsMapping.removeGuestOsMappingCmd()
+        cmd.id = id
+
+        return apiclient.removeGuestOsMapping(cmd)
+
+    @classmethod
+    def update(cls, apiclient, id, osnameforhypervisor=None, osmappingcheckenabled=None):
+        """Update Guest OS mapping"""
+        cmd = updateGuestOsMapping.updateGuestOsMappingCmd()
+        cmd.id = id
+        cmd.osnameforhypervisor = osnameforhypervisor
+        if osmappingcheckenabled is not None:
+            cmd.osmappingcheckenabled = osmappingcheckenabled
+
+        return apiclient.updateGuestOsMapping(cmd)
+
+    @classmethod
+    def list(cls, apiclient, id=None, ostypeid=None, osdisplayname=None,
+             osnameforhypervisor=None, hypervisor=None, hypervisorversion=None, **kwargs):
+        """List all Guest OS mappings"""
+        cmd = listGuestOsMapping.listGuestOsMappingCmd()
+        [setattr(cmd, k, v) for k, v in list(kwargs.items())]
+        if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
+            cmd.listall = True
+        if id is not None:
+            cmd.id = id
+        if ostypeid is not None:
+            cmd.ostypeid = ostypeid
+        if osdisplayname is not None:
+            cmd.osdisplayname = osdisplayname
+        if osnameforhypervisor is not None:
+            cmd.osnameforhypervisor = osnameforhypervisor
+        if hypervisor is not None:
+            cmd.hypervisor = hypervisor
+        if hypervisorversion is not None:
+            cmd.hypervisorversion = hypervisorversion
+
+        return (apiclient.listGuestOsMapping(cmd))
+
+class VMSchedule:
+
+    def __init__(self, items):
+        self.__dict__.update(items)
+
+    @classmethod
+    def create(cls, apiclient, virtualmachineid, action, schedule, timezone, startdate, enabled=False, description=None, enddate=None):
+        cmd = createVMSchedule.createVMScheduleCmd()
+        cmd.virtualmachineid = virtualmachineid
+        cmd.description = description
+        cmd.action = action
+        cmd.schedule = schedule
+        cmd.timezone = timezone
+        cmd.startdate = startdate
+        cmd.enddate = enddate
+        cmd.enabled = enabled
+        return VMSchedule(apiclient.createVMSchedule(cmd).__dict__)
+
+    @classmethod
+    def list(cls, apiclient, virtualmachineid, id=None, enabled=None, action=None):
+        cmd = listVMSchedule.listVMScheduleCmd()
+        cmd.virtualmachineid = virtualmachineid
+        cmd.id = id
+        cmd.enabled = enabled
+        cmd.action = action
+        return apiclient.listVMSchedule(cmd)
+
+    def update(self, apiclient, **kwargs):
+        cmd = updateVMSchedule.updateVMScheduleCmd()
+        cmd.id = self.id
+        [setattr(cmd, k, v) for k, v in list(kwargs.items())]
+        return apiclient.updateVMSchedule(cmd)
+
+    def delete(self, apiclient):
+        cmd = deleteVMSchedule.deleteVMScheduleCmd()
+        cmd.id = self.id
+        cmd.virtualmachineid = self.virtualmachineid
+        return (apiclient.deleteVMSchedule(cmd))
diff --git a/tools/marvin/marvin/lib/common.py b/tools/marvin/marvin/lib/common.py
index f1f09bb..cc77365 100644
--- a/tools/marvin/marvin/lib/common.py
+++ b/tools/marvin/marvin/lib/common.py
@@ -58,6 +58,7 @@
                                   listNetworkOfferings,
                                   listResourceLimits,
                                   listVPCOfferings,
+                                  listManagementServers,
                                   migrateSystemVm)
 from marvin.sshClient import SshClient
 from marvin.codes import (PASS, FAILED, ISOLATED_NETWORK, VPC_NETWORK,
@@ -1056,6 +1057,14 @@
         cmd.listall=True
     return(apiclient.listVPCOfferings(cmd))
 
+def list_mgmt_servers(apiclient, **kwargs):
+    """ Lists Management Servers """
+
+    cmd = listManagementServers.listManagementServersCmd()
+    [setattr(cmd, k, v) for k, v in list(kwargs.items())]
+    if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
+        cmd.listall=True
+    return(apiclient.listManagementServers(cmd))
 
 def update_resource_count(apiclient, domainid, accountid=None,
                           projectid=None, rtype=None):
diff --git a/tools/marvin/marvin/lib/utils.py b/tools/marvin/marvin/lib/utils.py
index bc4d15c..d3cbd42 100644
--- a/tools/marvin/marvin/lib/utils.py
+++ b/tools/marvin/marvin/lib/utils.py
@@ -288,6 +288,18 @@
     assert hosts_list_validation_result[0] == PASS, "host list validation failed"
     return hosts_list_validation_result[1].hypervisor
 
+def get_hypervisor_version(apiclient):
+
+    """Return the hypervisor type of the hosts in setup"""
+
+    cmd = listHosts.listHostsCmd()
+    cmd.type = 'Routing'
+    cmd.listall = True
+    hosts = apiclient.listHosts(cmd)
+    hosts_list_validation_result = validateList(hosts)
+    assert hosts_list_validation_result[0] == PASS, "host list validation failed"
+    return hosts_list_validation_result[1].hypervisorversion
+
 def is_snapshot_on_nfs(apiclient, dbconn, config, zoneid, snapshotid):
     """
     Checks whether a snapshot with id (not UUID) `snapshotid` is present on the nfs storage
diff --git a/tools/marvin/pom.xml b/tools/marvin/pom.xml
index 84da98a..334d0b3 100644
--- a/tools/marvin/pom.xml
+++ b/tools/marvin/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloud-tools</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py
index 3ceb275..e96065f 100644
--- a/tools/marvin/setup.py
+++ b/tools/marvin/setup.py
@@ -27,7 +27,7 @@
         raise RuntimeError("python setuptools is required to build Marvin")
 
 
-VERSION = "4.18.1.0"
+VERSION = "4.19.0.0"
 
 setup(name="Marvin",
       version=VERSION,
diff --git a/tools/pom.xml b/tools/pom.xml
index 81bfe9b..a39a504 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -25,7 +25,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <build>
diff --git a/ui/jest.config.js b/ui/jest.config.js
index b49888a..7659c94 100644
--- a/ui/jest.config.js
+++ b/ui/jest.config.js
@@ -42,7 +42,7 @@
     '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
   ],
   transformIgnorePatterns: [
-    '<rootDir>/node_modules/(?!ant-design-vue|vue|@babel/runtime|lodash-es|@ant-design)'
+    '<rootDir>/node_modules/(?!ant-design-vue|vue|@babel/runtime|lodash-es|@ant-design|@vue-js-cron)'
   ],
   collectCoverage: true,
   collectCoverageFrom: [
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 97cebc3..ed091b3 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -25,12 +25,13 @@
       }
     },
     "@ampproject/remapping": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz",
-      "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+      "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
       "dev": true,
       "requires": {
-        "@jridgewell/trace-mapping": "^0.3.0"
+        "@jridgewell/gen-mapping": "^0.1.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
       }
     },
     "@ant-design/colors": {
@@ -41,6 +42,18 @@
         "@ctrl/tinycolor": "^3.4.0"
       }
     },
+    "@ant-design/icons": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.7.0.tgz",
+      "integrity": "sha512-aoB4Z7JA431rt6d4u+8xcNPPCrdufSRMUOpxa1ab6mz1JCQZOEVolj2WVs/tDFmN62zzK30mNelEsprLYsSF3g==",
+      "requires": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-svg": "^4.2.1",
+        "@babel/runtime": "^7.11.2",
+        "classnames": "^2.2.6",
+        "rc-util": "^5.9.4"
+      }
+    },
     "@ant-design/icons-svg": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz",
@@ -55,6 +68,18 @@
         "@ant-design/icons-svg": "^4.2.1"
       }
     },
+    "@ant-design/react-slick": {
+      "version": "0.29.2",
+      "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-0.29.2.tgz",
+      "integrity": "sha512-kgjtKmkGHa19FW21lHnAfyyH9AAoh35pBdcJ53rHmQ3O+cfFHGHnUbj/HFrRNJ5vIts09FKJVAD8RpaC+RaWfA==",
+      "requires": {
+        "@babel/runtime": "^7.10.4",
+        "classnames": "^2.2.5",
+        "json2mq": "^0.2.0",
+        "lodash": "^4.17.21",
+        "resize-observer-polyfill": "^1.5.1"
+      }
+    },
     "@apollo/protobufjs": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.2.tgz",
@@ -85,9 +110,9 @@
       }
     },
     "@apollographql/apollo-tools": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.2.tgz",
-      "integrity": "sha512-KxZiw0Us3k1d0YkJDhOpVH5rJ+mBfjXcgoRoCcslbgirjgLotKMzOcx4PZ7YTEvvEROmvG7X3Aon41GvMmyGsw==",
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz",
+      "integrity": "sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==",
       "dev": true
     },
     "@apollographql/graphql-playground-html": {
@@ -115,40 +140,40 @@
       }
     },
     "@babel/code-frame": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
-      "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+      "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
       "dev": true,
       "requires": {
-        "@babel/highlight": "^7.16.7"
+        "@babel/highlight": "^7.18.6"
       }
     },
     "@babel/compat-data": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
-      "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.6.tgz",
+      "integrity": "sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ==",
       "dev": true
     },
     "@babel/core": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
-      "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.6.tgz",
+      "integrity": "sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.1.0",
-        "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.7",
-        "@babel/helper-compilation-targets": "^7.17.7",
-        "@babel/helper-module-transforms": "^7.17.7",
-        "@babel/helpers": "^7.17.8",
-        "@babel/parser": "^7.17.8",
-        "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
-        "@babel/types": "^7.17.0",
+        "@babel/code-frame": "^7.18.6",
+        "@babel/generator": "^7.18.6",
+        "@babel/helper-compilation-targets": "^7.18.6",
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helpers": "^7.18.6",
+        "@babel/parser": "^7.18.6",
+        "@babel/template": "^7.18.6",
+        "@babel/traverse": "^7.18.6",
+        "@babel/types": "^7.18.6",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
-        "json5": "^2.1.2",
+        "json5": "^2.2.1",
         "semver": "^6.3.0"
       },
       "dependencies": {
@@ -161,52 +186,57 @@
       }
     },
     "@babel/generator": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
-      "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+      "version": "7.18.7",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.7.tgz",
+      "integrity": "sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.17.0",
-        "jsesc": "^2.5.1",
-        "source-map": "^0.5.0"
+        "@babel/types": "^7.18.7",
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "jsesc": "^2.5.1"
       },
       "dependencies": {
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
+        "@jridgewell/gen-mapping": {
+          "version": "0.3.2",
+          "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+          "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+          "dev": true,
+          "requires": {
+            "@jridgewell/set-array": "^1.0.1",
+            "@jridgewell/sourcemap-codec": "^1.4.10",
+            "@jridgewell/trace-mapping": "^0.3.9"
+          }
         }
       }
     },
     "@babel/helper-annotate-as-pure": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
-      "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz",
+      "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-builder-binary-assignment-operator-visitor": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz",
-      "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.6.tgz",
+      "integrity": "sha512-KT10c1oWEpmrIRYnthbzHgoOf6B+Xd6a5yhdbNtdhtG7aO1or5HViuf1TQR36xY/QprXA5nvxO6nAjhJ4y38jw==",
       "dev": true,
       "requires": {
-        "@babel/helper-explode-assignable-expression": "^7.16.7",
-        "@babel/types": "^7.16.7"
+        "@babel/helper-explode-assignable-expression": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-compilation-targets": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
-      "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz",
+      "integrity": "sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.17.7",
-        "@babel/helper-validator-option": "^7.16.7",
-        "browserslist": "^4.17.5",
+        "@babel/compat-data": "^7.18.6",
+        "@babel/helper-validator-option": "^7.18.6",
+        "browserslist": "^4.20.2",
         "semver": "^6.3.0"
       },
       "dependencies": {
@@ -219,28 +249,28 @@
       }
     },
     "@babel/helper-create-class-features-plugin": {
-      "version": "7.17.6",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz",
-      "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz",
+      "integrity": "sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.7",
-        "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
-        "@babel/helper-member-expression-to-functions": "^7.16.7",
-        "@babel/helper-optimise-call-expression": "^7.16.7",
-        "@babel/helper-replace-supers": "^7.16.7",
-        "@babel/helper-split-export-declaration": "^7.16.7"
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-function-name": "^7.18.6",
+        "@babel/helper-member-expression-to-functions": "^7.18.6",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6"
       }
     },
     "@babel/helper-create-regexp-features-plugin": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz",
-      "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz",
+      "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.7",
-        "regexpu-core": "^5.0.1"
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "regexpu-core": "^5.1.0"
       }
     },
     "@babel/helper-define-polyfill-provider": {
@@ -260,12 +290,12 @@
       },
       "dependencies": {
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -279,194 +309,182 @@
       }
     },
     "@babel/helper-environment-visitor": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
-      "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.16.7"
-      }
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz",
+      "integrity": "sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==",
+      "dev": true
     },
     "@babel/helper-explode-assignable-expression": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz",
-      "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz",
+      "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
-      "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz",
+      "integrity": "sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==",
       "dev": true,
       "requires": {
-        "@babel/helper-get-function-arity": "^7.16.7",
-        "@babel/template": "^7.16.7",
-        "@babel/types": "^7.16.7"
-      }
-    },
-    "@babel/helper-get-function-arity": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
-      "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/template": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-hoist-variables": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
-      "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+      "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-member-expression-to-functions": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz",
-      "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz",
+      "integrity": "sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.17.0"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
-      "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+      "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-module-transforms": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
-      "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz",
+      "integrity": "sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw==",
       "dev": true,
       "requires": {
-        "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-module-imports": "^7.16.7",
-        "@babel/helper-simple-access": "^7.17.7",
-        "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/helper-validator-identifier": "^7.16.7",
-        "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
-        "@babel/types": "^7.17.0"
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-simple-access": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "@babel/template": "^7.18.6",
+        "@babel/traverse": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-optimise-call-expression": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz",
-      "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz",
+      "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-plugin-utils": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
-      "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz",
+      "integrity": "sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==",
       "dev": true
     },
     "@babel/helper-remap-async-to-generator": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz",
-      "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.6.tgz",
+      "integrity": "sha512-z5wbmV55TveUPZlCLZvxWHtrjuJd+8inFhk7DG0WW87/oJuGDcjDiu7HIvGcpf5464L6xKCg3vNkmlVVz9hwyQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.7",
-        "@babel/helper-wrap-function": "^7.16.8",
-        "@babel/types": "^7.16.8"
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-wrap-function": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-replace-supers": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz",
-      "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz",
+      "integrity": "sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g==",
       "dev": true,
       "requires": {
-        "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-member-expression-to-functions": "^7.16.7",
-        "@babel/helper-optimise-call-expression": "^7.16.7",
-        "@babel/traverse": "^7.16.7",
-        "@babel/types": "^7.16.7"
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-member-expression-to-functions": "^7.18.6",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/traverse": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-simple-access": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
-      "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz",
+      "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.17.0"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-skip-transparent-expression-wrappers": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz",
-      "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.6.tgz",
+      "integrity": "sha512-4KoLhwGS9vGethZpAhYnMejWkX64wsnHPDwvOsKWU6Fg4+AlK2Jz3TyjQLMEPvz+1zemi/WBdkYxCD0bAfIkiw==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-split-export-declaration": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
-      "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+      "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-validator-identifier": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
-      "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
+      "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
       "dev": true
     },
     "@babel/helper-validator-option": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz",
-      "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+      "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
       "dev": true
     },
     "@babel/helper-wrap-function": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz",
-      "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.6.tgz",
+      "integrity": "sha512-I5/LZfozwMNbwr/b1vhhuYD+J/mU+gfGAj5td7l5Rv9WYmH6i3Om69WGKNmlIpsVW/mF6O5bvTKbvDQZVgjqOw==",
       "dev": true,
       "requires": {
-        "@babel/helper-function-name": "^7.16.7",
-        "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.16.8",
-        "@babel/types": "^7.16.8"
+        "@babel/helper-function-name": "^7.18.6",
+        "@babel/template": "^7.18.6",
+        "@babel/traverse": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helpers": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
-      "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.6.tgz",
+      "integrity": "sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.16.7",
-        "@babel/traverse": "^7.17.3",
-        "@babel/types": "^7.17.0"
+        "@babel/template": "^7.18.6",
+        "@babel/traverse": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/highlight": {
-      "version": "7.16.10",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
-      "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+      "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.16.7",
+        "@babel/helper-validator-identifier": "^7.18.6",
         "chalk": "^2.0.0",
         "js-tokens": "^4.0.0"
       },
@@ -503,13 +521,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -524,199 +542,200 @@
       }
     },
     "@babel/parser": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
-      "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ=="
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.6.tgz",
+      "integrity": "sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw=="
     },
     "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz",
-      "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz",
+      "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz",
-      "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.6.tgz",
+      "integrity": "sha512-Udgu8ZRgrBrttVz6A0EVL0SJ1z+RLbIeqsu632SA1hf0awEppD6TvdznoH+orIF8wtFFAV/Enmw9Y+9oV8TQcw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
-        "@babel/plugin-proposal-optional-chaining": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.18.6",
+        "@babel/plugin-proposal-optional-chaining": "^7.18.6"
       }
     },
     "@babel/plugin-proposal-async-generator-functions": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz",
-      "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz",
+      "integrity": "sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-remap-async-to-generator": "^7.16.8",
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-remap-async-to-generator": "^7.18.6",
         "@babel/plugin-syntax-async-generators": "^7.8.4"
       }
     },
     "@babel/plugin-proposal-class-properties": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz",
-      "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz",
+      "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-proposal-class-static-block": {
-      "version": "7.17.6",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz",
-      "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz",
+      "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.17.6",
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-class-static-block": "^7.14.5"
       }
     },
     "@babel/plugin-proposal-decorators": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.8.tgz",
-      "integrity": "sha512-U69odN4Umyyx1xO1rTII0IDkAEC+RNlcKXtqOblfpzqy1C+aOplb76BQNq0+XdpVkOaPlpEDwd++joY8FNFJKA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.6.tgz",
+      "integrity": "sha512-gAdhsjaYmiZVxx5vTMiRfj31nB7LhwBJFMSLzeDxc7X4tKLixup0+k9ughn0RcpBrv9E3PBaXJW7jF5TCihAOg==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.17.6",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-replace-supers": "^7.16.7",
-        "@babel/plugin-syntax-decorators": "^7.17.0",
-        "charcodes": "^0.2.0"
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/plugin-syntax-decorators": "^7.18.6"
       }
     },
     "@babel/plugin-proposal-dynamic-import": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz",
-      "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz",
+      "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-export-namespace-from": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz",
-      "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.6.tgz",
+      "integrity": "sha512-zr/QcUlUo7GPo6+X1wC98NJADqmy5QTFWWhqeQWiki4XHafJtLl/YMGkmRB2szDD2IYJCCdBTd4ElwhId9T7Xw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-json-strings": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz",
-      "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz",
+      "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-json-strings": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-logical-assignment-operators": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz",
-      "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.6.tgz",
+      "integrity": "sha512-zMo66azZth/0tVd7gmkxOkOjs2rpHyhpcFo565PUP37hSp6hSd9uUKIfTDFMz58BwqgQKhJ9YxtM5XddjXVn+Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
       }
     },
     "@babel/plugin-proposal-nullish-coalescing-operator": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz",
-      "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
+      "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-numeric-separator": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz",
-      "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz",
+      "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-numeric-separator": "^7.10.4"
       }
     },
     "@babel/plugin-proposal-object-rest-spread": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz",
-      "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.6.tgz",
+      "integrity": "sha512-9yuM6wr4rIsKa1wlUAbZEazkCrgw2sMPEXCr4Rnwetu7cEW1NydkCWytLuYletbf8vFxdJxFhwEZqMpOx2eZyw==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.17.0",
-        "@babel/helper-compilation-targets": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/compat-data": "^7.18.6",
+        "@babel/helper-compilation-targets": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-        "@babel/plugin-transform-parameters": "^7.16.7"
+        "@babel/plugin-transform-parameters": "^7.18.6"
       }
     },
     "@babel/plugin-proposal-optional-catch-binding": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz",
-      "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz",
+      "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-optional-chaining": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz",
-      "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.6.tgz",
+      "integrity": "sha512-PatI6elL5eMzoypFAiYDpYQyMtXTn+iMhuxxQt5mAXD4fEmKorpSI3PHd+i3JXBJN3xyA6MvJv7at23HffFHwA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.18.6",
         "@babel/plugin-syntax-optional-chaining": "^7.8.3"
       }
     },
     "@babel/plugin-proposal-private-methods": {
-      "version": "7.16.11",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz",
-      "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+      "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.10",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-proposal-private-property-in-object": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz",
-      "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz",
+      "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.7",
-        "@babel/helper-create-class-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
       }
     },
     "@babel/plugin-proposal-unicode-property-regex": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz",
-      "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
+      "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-async-generators": {
@@ -756,12 +775,12 @@
       }
     },
     "@babel/plugin-syntax-decorators": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.0.tgz",
-      "integrity": "sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz",
+      "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-dynamic-import": {
@@ -783,12 +802,21 @@
       }
     },
     "@babel/plugin-syntax-flow": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz",
-      "integrity": "sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz",
+      "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
+      }
+    },
+    "@babel/plugin-syntax-import-assertions": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz",
+      "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-import-meta": {
@@ -810,12 +838,12 @@
       }
     },
     "@babel/plugin-syntax-jsx": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
-      "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+      "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-logical-assignment-operators": {
@@ -891,284 +919,286 @@
       }
     },
     "@babel/plugin-syntax-typescript": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz",
-      "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz",
+      "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-arrow-functions": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz",
-      "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz",
+      "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-async-to-generator": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz",
-      "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz",
+      "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-imports": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-remap-async-to-generator": "^7.16.8"
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-remap-async-to-generator": "^7.18.6"
       }
     },
     "@babel/plugin-transform-block-scoped-functions": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz",
-      "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz",
+      "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-block-scoping": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz",
-      "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.6.tgz",
+      "integrity": "sha512-pRqwb91C42vs1ahSAWJkxOxU1RHWDn16XAa6ggQ72wjLlWyYeAcLvTtE0aM8ph3KNydy9CQF2nLYcjq1WysgxQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-classes": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz",
-      "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.6.tgz",
+      "integrity": "sha512-XTg8XW/mKpzAF3actL554Jl/dOYoJtv3l8fxaEczpgz84IeeVf+T1u2CSvPHuZbt0w3JkIx4rdn/MRQI7mo0HQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-annotate-as-pure": "^7.16.7",
-        "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
-        "@babel/helper-optimise-call-expression": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-replace-supers": "^7.16.7",
-        "@babel/helper-split-export-declaration": "^7.16.7",
+        "@babel/helper-annotate-as-pure": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-function-name": "^7.18.6",
+        "@babel/helper-optimise-call-expression": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
         "globals": "^11.1.0"
       }
     },
     "@babel/plugin-transform-computed-properties": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz",
-      "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.6.tgz",
+      "integrity": "sha512-9repI4BhNrR0KenoR9vm3/cIc1tSBIo+u1WVjKCAynahj25O8zfbiE6JtAtHPGQSs4yZ+bA8mRasRP+qc+2R5A==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-destructuring": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz",
-      "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.6.tgz",
+      "integrity": "sha512-tgy3u6lRp17ilY8r1kP4i2+HDUwxlVqq3RTc943eAWSzGgpU1qhiKpqZ5CMyHReIYPHdo3Kg8v8edKtDqSVEyQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-dotall-regex": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz",
-      "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz",
+      "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-duplicate-keys": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz",
-      "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.6.tgz",
+      "integrity": "sha512-NJU26U/208+sxYszf82nmGYqVF9QN8py2HFTblPT9hbawi8+1C5a9JubODLTGFuT0qlkqVinmkwOD13s0sZktg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-exponentiation-operator": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz",
-      "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz",
+      "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==",
       "dev": true,
       "requires": {
-        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-flow-strip-types": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz",
-      "integrity": "sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.6.tgz",
+      "integrity": "sha512-wE0xtA7csz+hw4fKPwxmu5jnzAsXPIO57XnRwzXP3T19jWh1BODnPGoG9xKYwvAwusP7iUktHayRFbMPGtODaQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/plugin-syntax-flow": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-flow": "^7.18.6"
       }
     },
     "@babel/plugin-transform-for-of": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz",
-      "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.6.tgz",
+      "integrity": "sha512-WAjoMf4wIiSsy88KmG7tgj2nFdEK7E46tArVtcgED7Bkj6Fg/tG5SbvNIOKxbFS2VFgNh6+iaPswBeQZm4ox8w==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz",
-      "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.6.tgz",
+      "integrity": "sha512-kJha/Gbs5RjzIu0CxZwf5e3aTTSlhZnHMT8zPWnJMjNpLOUgqevg+PN5oMH68nMCXnfiMo4Bhgxqj59KHTlAnA==",
       "dev": true,
       "requires": {
-        "@babel/helper-compilation-targets": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-compilation-targets": "^7.18.6",
+        "@babel/helper-function-name": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-literals": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz",
-      "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.6.tgz",
+      "integrity": "sha512-x3HEw0cJZVDoENXOp20HlypIHfl0zMIhMVZEBVTfmqbObIpsMxMbmU5nOEO8R7LYT+z5RORKPlTI5Hj4OsO9/Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-member-expression-literals": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz",
-      "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz",
+      "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-modules-amd": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz",
-      "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz",
+      "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-commonjs": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz",
-      "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz",
+      "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.17.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-simple-access": "^7.17.7",
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-simple-access": "^7.18.6",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-systemjs": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz",
-      "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.6.tgz",
+      "integrity": "sha512-UbPYpXxLjTw6w6yXX2BYNxF3p6QY225wcTkfQCy3OMnSlS/C3xGtwUjEzGkldb/sy6PWLiCQ3NbYfjWUTI3t4g==",
       "dev": true,
       "requires": {
-        "@babel/helper-hoist-variables": "^7.16.7",
-        "@babel/helper-module-transforms": "^7.17.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-validator-identifier": "^7.16.7",
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-validator-identifier": "^7.18.6",
         "babel-plugin-dynamic-import-node": "^2.3.3"
       }
     },
     "@babel/plugin-transform-modules-umd": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz",
-      "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz",
+      "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-transforms": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-module-transforms": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-named-capturing-groups-regex": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz",
-      "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz",
+      "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.7"
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-new-target": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz",
-      "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz",
+      "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-object-super": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz",
-      "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz",
+      "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-replace-supers": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-replace-supers": "^7.18.6"
       }
     },
     "@babel/plugin-transform-parameters": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz",
-      "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.6.tgz",
+      "integrity": "sha512-FjdqgMv37yVl/gwvzkcB+wfjRI8HQmc5EgOG9iGNvUY1ok+TjsoaMP7IqCDZBhkFcM5f3OPVMs6Dmp03C5k4/A==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-property-literals": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz",
-      "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz",
+      "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-regenerator": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz",
-      "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz",
+      "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==",
       "dev": true,
       "requires": {
-        "regenerator-transform": "^0.14.2"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "regenerator-transform": "^0.15.0"
       }
     },
     "@babel/plugin-transform-reserved-words": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz",
-      "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz",
+      "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-runtime": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz",
-      "integrity": "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.6.tgz",
+      "integrity": "sha512-8uRHk9ZmRSnWqUgyae249EJZ94b0yAGLBIqzZzl+0iEdbno55Pmlt/32JZsHwXD9k/uZj18Aqqk35wBX4CBTXA==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-imports": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "babel-plugin-polyfill-corejs2": "^0.3.0",
-        "babel-plugin-polyfill-corejs3": "^0.5.0",
-        "babel-plugin-polyfill-regenerator": "^0.3.0",
+        "@babel/helper-module-imports": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "babel-plugin-polyfill-corejs2": "^0.3.1",
+        "babel-plugin-polyfill-corejs3": "^0.5.2",
+        "babel-plugin-polyfill-regenerator": "^0.3.1",
         "semver": "^6.3.0"
       },
       "dependencies": {
@@ -1181,113 +1211,114 @@
       }
     },
     "@babel/plugin-transform-shorthand-properties": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz",
-      "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz",
+      "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-spread": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz",
-      "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.6.tgz",
+      "integrity": "sha512-ayT53rT/ENF8WWexIRg9AiV9h0aIteyWn5ptfZTZQrjk/+f3WdrJGCY4c9wcgl2+MKkKPhzbYp97FTsquZpDCw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.18.6"
       }
     },
     "@babel/plugin-transform-sticky-regex": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz",
-      "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz",
+      "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-template-literals": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz",
-      "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.6.tgz",
+      "integrity": "sha512-UuqlRrQmT2SWRvahW46cGSany0uTlcj8NYOS5sRGYi8FxPYPoLd5DDmMd32ZXEj2Jq+06uGVQKHxa/hJx2EzKw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-typeof-symbol": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz",
-      "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.6.tgz",
+      "integrity": "sha512-7m71iS/QhsPk85xSjFPovHPcH3H9qeyzsujhTc+vcdnsXavoWYJ74zx0lP5RhpC5+iDnVLO+PPMHzC11qels1g==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-typescript": {
-      "version": "7.16.8",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz",
-      "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.6.tgz",
+      "integrity": "sha512-ijHNhzIrLj5lQCnI6aaNVRtGVuUZhOXFLRVFs7lLrkXTHip4FKty5oAuQdk4tywG0/WjXmjTfQCWmuzrvFer1w==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-class-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/plugin-syntax-typescript": "^7.16.7"
+        "@babel/helper-create-class-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/plugin-syntax-typescript": "^7.18.6"
       }
     },
     "@babel/plugin-transform-unicode-escapes": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz",
-      "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz",
+      "integrity": "sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-transform-unicode-regex": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz",
-      "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz",
+      "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==",
       "dev": true,
       "requires": {
-        "@babel/helper-create-regexp-features-plugin": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7"
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/preset-env": {
-      "version": "7.16.11",
-      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz",
-      "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.6.tgz",
+      "integrity": "sha512-WrthhuIIYKrEFAwttYzgRNQ5hULGmwTj+D6l7Zdfsv5M7IWV/OZbUfbeL++Qrzx1nVJwWROIFhCHRYQV4xbPNw==",
       "dev": true,
       "requires": {
-        "@babel/compat-data": "^7.16.8",
-        "@babel/helper-compilation-targets": "^7.16.7",
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-validator-option": "^7.16.7",
-        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7",
-        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7",
-        "@babel/plugin-proposal-async-generator-functions": "^7.16.8",
-        "@babel/plugin-proposal-class-properties": "^7.16.7",
-        "@babel/plugin-proposal-class-static-block": "^7.16.7",
-        "@babel/plugin-proposal-dynamic-import": "^7.16.7",
-        "@babel/plugin-proposal-export-namespace-from": "^7.16.7",
-        "@babel/plugin-proposal-json-strings": "^7.16.7",
-        "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
-        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
-        "@babel/plugin-proposal-numeric-separator": "^7.16.7",
-        "@babel/plugin-proposal-object-rest-spread": "^7.16.7",
-        "@babel/plugin-proposal-optional-catch-binding": "^7.16.7",
-        "@babel/plugin-proposal-optional-chaining": "^7.16.7",
-        "@babel/plugin-proposal-private-methods": "^7.16.11",
-        "@babel/plugin-proposal-private-property-in-object": "^7.16.7",
-        "@babel/plugin-proposal-unicode-property-regex": "^7.16.7",
+        "@babel/compat-data": "^7.18.6",
+        "@babel/helper-compilation-targets": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-validator-option": "^7.18.6",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.6",
+        "@babel/plugin-proposal-async-generator-functions": "^7.18.6",
+        "@babel/plugin-proposal-class-properties": "^7.18.6",
+        "@babel/plugin-proposal-class-static-block": "^7.18.6",
+        "@babel/plugin-proposal-dynamic-import": "^7.18.6",
+        "@babel/plugin-proposal-export-namespace-from": "^7.18.6",
+        "@babel/plugin-proposal-json-strings": "^7.18.6",
+        "@babel/plugin-proposal-logical-assignment-operators": "^7.18.6",
+        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+        "@babel/plugin-proposal-numeric-separator": "^7.18.6",
+        "@babel/plugin-proposal-object-rest-spread": "^7.18.6",
+        "@babel/plugin-proposal-optional-catch-binding": "^7.18.6",
+        "@babel/plugin-proposal-optional-chaining": "^7.18.6",
+        "@babel/plugin-proposal-private-methods": "^7.18.6",
+        "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
+        "@babel/plugin-proposal-unicode-property-regex": "^7.18.6",
         "@babel/plugin-syntax-async-generators": "^7.8.4",
         "@babel/plugin-syntax-class-properties": "^7.12.13",
         "@babel/plugin-syntax-class-static-block": "^7.14.5",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3",
         "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+        "@babel/plugin-syntax-import-assertions": "^7.18.6",
         "@babel/plugin-syntax-json-strings": "^7.8.3",
         "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
         "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
@@ -1297,44 +1328,44 @@
         "@babel/plugin-syntax-optional-chaining": "^7.8.3",
         "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
         "@babel/plugin-syntax-top-level-await": "^7.14.5",
-        "@babel/plugin-transform-arrow-functions": "^7.16.7",
-        "@babel/plugin-transform-async-to-generator": "^7.16.8",
-        "@babel/plugin-transform-block-scoped-functions": "^7.16.7",
-        "@babel/plugin-transform-block-scoping": "^7.16.7",
-        "@babel/plugin-transform-classes": "^7.16.7",
-        "@babel/plugin-transform-computed-properties": "^7.16.7",
-        "@babel/plugin-transform-destructuring": "^7.16.7",
-        "@babel/plugin-transform-dotall-regex": "^7.16.7",
-        "@babel/plugin-transform-duplicate-keys": "^7.16.7",
-        "@babel/plugin-transform-exponentiation-operator": "^7.16.7",
-        "@babel/plugin-transform-for-of": "^7.16.7",
-        "@babel/plugin-transform-function-name": "^7.16.7",
-        "@babel/plugin-transform-literals": "^7.16.7",
-        "@babel/plugin-transform-member-expression-literals": "^7.16.7",
-        "@babel/plugin-transform-modules-amd": "^7.16.7",
-        "@babel/plugin-transform-modules-commonjs": "^7.16.8",
-        "@babel/plugin-transform-modules-systemjs": "^7.16.7",
-        "@babel/plugin-transform-modules-umd": "^7.16.7",
-        "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8",
-        "@babel/plugin-transform-new-target": "^7.16.7",
-        "@babel/plugin-transform-object-super": "^7.16.7",
-        "@babel/plugin-transform-parameters": "^7.16.7",
-        "@babel/plugin-transform-property-literals": "^7.16.7",
-        "@babel/plugin-transform-regenerator": "^7.16.7",
-        "@babel/plugin-transform-reserved-words": "^7.16.7",
-        "@babel/plugin-transform-shorthand-properties": "^7.16.7",
-        "@babel/plugin-transform-spread": "^7.16.7",
-        "@babel/plugin-transform-sticky-regex": "^7.16.7",
-        "@babel/plugin-transform-template-literals": "^7.16.7",
-        "@babel/plugin-transform-typeof-symbol": "^7.16.7",
-        "@babel/plugin-transform-unicode-escapes": "^7.16.7",
-        "@babel/plugin-transform-unicode-regex": "^7.16.7",
+        "@babel/plugin-transform-arrow-functions": "^7.18.6",
+        "@babel/plugin-transform-async-to-generator": "^7.18.6",
+        "@babel/plugin-transform-block-scoped-functions": "^7.18.6",
+        "@babel/plugin-transform-block-scoping": "^7.18.6",
+        "@babel/plugin-transform-classes": "^7.18.6",
+        "@babel/plugin-transform-computed-properties": "^7.18.6",
+        "@babel/plugin-transform-destructuring": "^7.18.6",
+        "@babel/plugin-transform-dotall-regex": "^7.18.6",
+        "@babel/plugin-transform-duplicate-keys": "^7.18.6",
+        "@babel/plugin-transform-exponentiation-operator": "^7.18.6",
+        "@babel/plugin-transform-for-of": "^7.18.6",
+        "@babel/plugin-transform-function-name": "^7.18.6",
+        "@babel/plugin-transform-literals": "^7.18.6",
+        "@babel/plugin-transform-member-expression-literals": "^7.18.6",
+        "@babel/plugin-transform-modules-amd": "^7.18.6",
+        "@babel/plugin-transform-modules-commonjs": "^7.18.6",
+        "@babel/plugin-transform-modules-systemjs": "^7.18.6",
+        "@babel/plugin-transform-modules-umd": "^7.18.6",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6",
+        "@babel/plugin-transform-new-target": "^7.18.6",
+        "@babel/plugin-transform-object-super": "^7.18.6",
+        "@babel/plugin-transform-parameters": "^7.18.6",
+        "@babel/plugin-transform-property-literals": "^7.18.6",
+        "@babel/plugin-transform-regenerator": "^7.18.6",
+        "@babel/plugin-transform-reserved-words": "^7.18.6",
+        "@babel/plugin-transform-shorthand-properties": "^7.18.6",
+        "@babel/plugin-transform-spread": "^7.18.6",
+        "@babel/plugin-transform-sticky-regex": "^7.18.6",
+        "@babel/plugin-transform-template-literals": "^7.18.6",
+        "@babel/plugin-transform-typeof-symbol": "^7.18.6",
+        "@babel/plugin-transform-unicode-escapes": "^7.18.6",
+        "@babel/plugin-transform-unicode-regex": "^7.18.6",
         "@babel/preset-modules": "^0.1.5",
-        "@babel/types": "^7.16.8",
-        "babel-plugin-polyfill-corejs2": "^0.3.0",
-        "babel-plugin-polyfill-corejs3": "^0.5.0",
-        "babel-plugin-polyfill-regenerator": "^0.3.0",
-        "core-js-compat": "^3.20.2",
+        "@babel/types": "^7.18.6",
+        "babel-plugin-polyfill-corejs2": "^0.3.1",
+        "babel-plugin-polyfill-corejs3": "^0.5.2",
+        "babel-plugin-polyfill-regenerator": "^0.3.1",
+        "core-js-compat": "^3.22.1",
         "semver": "^6.3.0"
       },
       "dependencies": {
@@ -1347,14 +1378,14 @@
       }
     },
     "@babel/preset-flow": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.16.7.tgz",
-      "integrity": "sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.18.6.tgz",
+      "integrity": "sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-validator-option": "^7.16.7",
-        "@babel/plugin-transform-flow-strip-types": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-validator-option": "^7.18.6",
+        "@babel/plugin-transform-flow-strip-types": "^7.18.6"
       }
     },
     "@babel/preset-modules": {
@@ -1371,20 +1402,20 @@
       }
     },
     "@babel/preset-typescript": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz",
-      "integrity": "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz",
+      "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.16.7",
-        "@babel/helper-validator-option": "^7.16.7",
-        "@babel/plugin-transform-typescript": "^7.16.7"
+        "@babel/helper-plugin-utils": "^7.18.6",
+        "@babel/helper-validator-option": "^7.18.6",
+        "@babel/plugin-transform-typescript": "^7.18.6"
       }
     },
     "@babel/register": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz",
-      "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.6.tgz",
+      "integrity": "sha512-tkYtONzaO8rQubZzpBnvZPFcHgh8D9F55IjOsYton4X2IBoyRn2ZSWQqySTZnUn2guZbxbQiAB27hJEbvXamhQ==",
       "dev": true,
       "requires": {
         "clone-deep": "^4.0.1",
@@ -1419,56 +1450,56 @@
       }
     },
     "@babel/runtime": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
-      "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz",
+      "integrity": "sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==",
       "requires": {
         "regenerator-runtime": "^0.13.4"
       }
     },
     "@babel/template": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
-      "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz",
+      "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.16.7",
-        "@babel/parser": "^7.16.7",
-        "@babel/types": "^7.16.7"
+        "@babel/code-frame": "^7.18.6",
+        "@babel/parser": "^7.18.6",
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/traverse": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
-      "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.6.tgz",
+      "integrity": "sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.3",
-        "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
-        "@babel/helper-hoist-variables": "^7.16.7",
-        "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/parser": "^7.17.3",
-        "@babel/types": "^7.17.0",
+        "@babel/code-frame": "^7.18.6",
+        "@babel/generator": "^7.18.6",
+        "@babel/helper-environment-visitor": "^7.18.6",
+        "@babel/helper-function-name": "^7.18.6",
+        "@babel/helper-hoist-variables": "^7.18.6",
+        "@babel/helper-split-export-declaration": "^7.18.6",
+        "@babel/parser": "^7.18.6",
+        "@babel/types": "^7.18.6",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
       }
     },
     "@babel/types": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
-      "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+      "version": "7.18.7",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.7.tgz",
+      "integrity": "sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.16.7",
+        "@babel/helper-validator-identifier": "^7.18.6",
         "to-fast-properties": "^2.0.0"
       }
     },
     "@ctrl/tinycolor": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
-      "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
+      "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw=="
     },
     "@fortawesome/fontawesome-common-types": {
       "version": "0.3.0",
@@ -1514,9 +1545,9 @@
       }
     },
     "@fortawesome/vue-fontawesome": {
-      "version": "3.0.0-5",
-      "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.0-5.tgz",
-      "integrity": "sha512-aNmBT4bOecrFsZTog1l6AJDQHPP3ocXV+WQ3Ogy8WZCqstB/ahfhH4CPu5i4N9Hw0MBKXqE+LX+NbUxcj8cVTw=="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.1.tgz",
+      "integrity": "sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A=="
     },
     "@gar/promisify": {
       "version": "1.1.3",
@@ -1573,67 +1604,6 @@
         "postcss": "^7.0.0"
       }
     },
-    "@intlify/core-base": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.1.9.tgz",
-      "integrity": "sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==",
-      "requires": {
-        "@intlify/devtools-if": "9.1.9",
-        "@intlify/message-compiler": "9.1.9",
-        "@intlify/message-resolver": "9.1.9",
-        "@intlify/runtime": "9.1.9",
-        "@intlify/shared": "9.1.9",
-        "@intlify/vue-devtools": "9.1.9"
-      }
-    },
-    "@intlify/devtools-if": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.1.9.tgz",
-      "integrity": "sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==",
-      "requires": {
-        "@intlify/shared": "9.1.9"
-      }
-    },
-    "@intlify/message-compiler": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.1.9.tgz",
-      "integrity": "sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==",
-      "requires": {
-        "@intlify/message-resolver": "9.1.9",
-        "@intlify/shared": "9.1.9",
-        "source-map": "0.6.1"
-      }
-    },
-    "@intlify/message-resolver": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/message-resolver/-/message-resolver-9.1.9.tgz",
-      "integrity": "sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA=="
-    },
-    "@intlify/runtime": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/runtime/-/runtime-9.1.9.tgz",
-      "integrity": "sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==",
-      "requires": {
-        "@intlify/message-compiler": "9.1.9",
-        "@intlify/message-resolver": "9.1.9",
-        "@intlify/shared": "9.1.9"
-      }
-    },
-    "@intlify/shared": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.1.9.tgz",
-      "integrity": "sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw=="
-    },
-    "@intlify/vue-devtools": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz",
-      "integrity": "sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==",
-      "requires": {
-        "@intlify/message-resolver": "9.1.9",
-        "@intlify/runtime": "9.1.9",
-        "@intlify/shared": "9.1.9"
-      }
-    },
     "@istanbuljs/load-nyc-config": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -1704,13 +1674,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "slash": {
@@ -1810,13 +1780,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "rimraf": {
@@ -1938,13 +1908,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "node-notifier": {
@@ -2082,13 +2052,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "slash": {
@@ -2136,22 +2106,38 @@
       "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==",
       "dev": true
     },
+    "@jridgewell/gen-mapping": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+      "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/set-array": "^1.0.0",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
     "@jridgewell/resolve-uri": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
-      "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz",
+      "integrity": "sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==",
+      "dev": true
+    },
+    "@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
       "dev": true
     },
     "@jridgewell/sourcemap-codec": {
-      "version": "1.4.11",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
-      "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==",
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
       "dev": true
     },
     "@jridgewell/trace-mapping": {
-      "version": "0.3.4",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz",
-      "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==",
+      "version": "0.3.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+      "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
       "dev": true,
       "requires": {
         "@jridgewell/resolve-uri": "^3.0.3",
@@ -2280,7 +2266,7 @@
     "@protobufjs/aspromise": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
-      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
+      "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
       "dev": true
     },
     "@protobufjs/base64": {
@@ -2298,13 +2284,13 @@
     "@protobufjs/eventemitter": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
-      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
+      "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
       "dev": true
     },
     "@protobufjs/fetch": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
-      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
       "dev": true,
       "requires": {
         "@protobufjs/aspromise": "^1.1.1",
@@ -2314,31 +2300,31 @@
     "@protobufjs/float": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
-      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
+      "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
       "dev": true
     },
     "@protobufjs/inquire": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
-      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
+      "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
       "dev": true
     },
     "@protobufjs/path": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
-      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
+      "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
       "dev": true
     },
     "@protobufjs/pool": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
-      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
+      "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
       "dev": true
     },
     "@protobufjs/utf8": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
-      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
+      "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
       "dev": true
     },
     "@simonwep/pickr": {
@@ -2472,9 +2458,9 @@
       }
     },
     "@types/babel__traverse": {
-      "version": "7.14.2",
-      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz",
-      "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==",
+      "version": "7.17.1",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz",
+      "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==",
       "dev": true,
       "requires": {
         "@babel/types": "^7.3.0"
@@ -2510,9 +2496,9 @@
       }
     },
     "@types/content-disposition": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.4.tgz",
-      "integrity": "sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ==",
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz",
+      "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==",
       "dev": true
     },
     "@types/cookies": {
@@ -2552,9 +2538,9 @@
       }
     },
     "@types/express-serve-static-core": {
-      "version": "4.17.28",
-      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
-      "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+      "version": "4.17.29",
+      "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz",
+      "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==",
       "dev": true,
       "requires": {
         "@types/node": "*",
@@ -2603,9 +2589,9 @@
       "dev": true
     },
     "@types/http-proxy": {
-      "version": "1.17.8",
-      "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
-      "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==",
+      "version": "1.17.9",
+      "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
+      "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
       "dev": true,
       "requires": {
         "@types/node": "*"
@@ -2686,15 +2672,15 @@
       }
     },
     "@types/json-schema": {
-      "version": "7.0.10",
-      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.10.tgz",
-      "integrity": "sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A==",
+      "version": "7.0.11",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+      "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
     "@types/json5": {
       "version": "0.0.29",
       "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
-      "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
       "dev": true
     },
     "@types/keygrip": {
@@ -2729,9 +2715,9 @@
       }
     },
     "@types/long": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
-      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+      "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
       "dev": true
     },
     "@types/mime": {
@@ -2753,9 +2739,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "17.0.23",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
-      "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+      "version": "18.0.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
+      "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==",
       "dev": true
     },
     "@types/normalize-package-data": {
@@ -2807,7 +2793,7 @@
     "@types/strip-bom": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
+      "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
       "dev": true
     },
     "@types/strip-json-comments": {
@@ -2832,9 +2818,9 @@
       }
     },
     "@types/uglify-js": {
-      "version": "3.13.1",
-      "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
-      "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==",
+      "version": "3.16.0",
+      "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz",
+      "integrity": "sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g==",
       "dev": true,
       "requires": {
         "source-map": "^0.6.1"
@@ -2884,9 +2870,9 @@
       },
       "dependencies": {
         "source-map": {
-          "version": "0.7.3",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+          "version": "0.7.4",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+          "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
           "dev": true
         }
       }
@@ -2915,6 +2901,49 @@
       "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
       "dev": true
     },
+    "@vue-js-cron/ant": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@vue-js-cron/ant/-/ant-1.1.3.tgz",
+      "integrity": "sha512-E7PbZX/Fwb4w4GYYllnhDx8ywTce3OQ3WS20BPS8EbPlmShmkdvtU9dyNC+C+bGyzXfUwPIdLbtQ1vKzLrMm0A==",
+      "requires": {
+        "@vue-js-cron/core": "3.7.1",
+        "ant-design-vue": "^3.2.12"
+      },
+      "dependencies": {
+        "ant-design-vue": {
+          "version": "3.2.20",
+          "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-3.2.20.tgz",
+          "integrity": "sha512-YWpMfGaGoRastIXEYfCoJiaRiDHk4chqtYhlKQM5GqPt6NfvrM1Vg2e60yHtjxlZjed91wCMm0rAmyUr7Hwzdg==",
+          "requires": {
+            "@ant-design/colors": "^6.0.0",
+            "@ant-design/icons-vue": "^6.1.0",
+            "@babel/runtime": "^7.10.5",
+            "@ctrl/tinycolor": "^3.4.0",
+            "@simonwep/pickr": "~1.8.0",
+            "array-tree-filter": "^2.1.0",
+            "async-validator": "^4.0.0",
+            "dayjs": "^1.10.5",
+            "dom-align": "^1.12.1",
+            "dom-scroll-into-view": "^2.0.0",
+            "lodash": "^4.17.21",
+            "lodash-es": "^4.17.15",
+            "resize-observer-polyfill": "^1.5.1",
+            "scroll-into-view-if-needed": "^2.2.25",
+            "shallow-equal": "^1.0.0",
+            "vue-types": "^3.0.0",
+            "warning": "^4.0.0"
+          }
+        }
+      }
+    },
+    "@vue-js-cron/core": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/@vue-js-cron/core/-/core-3.7.1.tgz",
+      "integrity": "sha512-aWCkbfCbwpEJwmptY0tRFlxH4ZaVtnmR2Q3f0L8SzSC58cn45VYMe2/eSK53221cBmk/w+18Hq563u+W7Jp9Eg==",
+      "requires": {
+        "mustache": "^4.2.0"
+      }
+    },
     "@vue/babel-helper-vue-jsx-merge-props": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz",
@@ -2969,15 +2998,15 @@
         "html-tags": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz",
-          "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
+          "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==",
           "dev": true
         }
       }
     },
     "@vue/babel-preset-app": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.17.tgz",
-      "integrity": "sha512-iFv9J3F5VKUPcbx+TqW5qhGmAVyXQxPRpKpPOuTLFIVTzg+iwJnrqVbL4kJU5ECGDxPESW2oCVgxv9bTlDPu7w==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.5.19.tgz",
+      "integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.11.0",
@@ -3075,7 +3104,7 @@
         "html-tags": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz",
-          "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
+          "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==",
           "dev": true
         }
       }
@@ -3092,17 +3121,17 @@
       }
     },
     "@vue/cli": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli/-/cli-4.5.17.tgz",
-      "integrity": "sha512-73nK2o/o7Wk9myPySdjxpMzABLynGmnQbJ5wz3CJ9SFrss8Y2LwLAnzlO77mr3uA2nFPdTJbkbUcZlvBWCVSrA==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli/-/cli-4.5.19.tgz",
+      "integrity": "sha512-y+rPxbhwMNf9ZL3K1XuRDHaggnX4fVVDsm3oFzbZXKxK674Hi+cM+dc17TfKWNH8LbXiKH6R5KEWFoqIM/AbOA==",
       "dev": true,
       "requires": {
         "@types/ejs": "^2.7.0",
         "@types/inquirer": "^6.5.0",
-        "@vue/cli-shared-utils": "^4.5.17",
-        "@vue/cli-ui": "^4.5.17",
-        "@vue/cli-ui-addon-webpack": "^4.5.17",
-        "@vue/cli-ui-addon-widgets": "^4.5.17",
+        "@vue/cli-shared-utils": "^4.5.19",
+        "@vue/cli-ui": "^4.5.19",
+        "@vue/cli-ui-addon-webpack": "^4.5.19",
+        "@vue/cli-ui-addon-widgets": "^4.5.19",
         "boxen": "^4.1.0",
         "cmd-shim": "^3.0.3",
         "commander": "^2.20.0",
@@ -3156,12 +3185,12 @@
           }
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -3190,20 +3219,20 @@
       }
     },
     "@vue/cli-overlay": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.5.17.tgz",
-      "integrity": "sha512-QKKp66VbMg+X8Qh0wgXSwgxLfxY7EIkZkV6bZ6nFqBx8xtaJQVDbTL+4zcUPPA6nygbIcQ6gvTinNEqIqX6FUQ==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-4.5.19.tgz",
+      "integrity": "sha512-GdxvNSmOw7NHIazCO8gTK+xZbaOmScTtxj6eHVeMbYpDYVPJ+th3VMLWNpw/b6uOjwzzcyKlA5dRQ1DAb+gF/g==",
       "dev": true
     },
     "@vue/cli-plugin-babel": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.17.tgz",
-      "integrity": "sha512-6kZuc3PdoUvGAnndUq6+GqjIXn3bqdTR8lOcAb1BH2b4N7IKGlmzcipALGS23HLVMAvDgNuUS7vf0unin9j2cg==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.19.tgz",
+      "integrity": "sha512-8ebXzaMW9KNTMAN6+DzkhFsjty1ieqT7hIW5Lbk4v30Qhfjkms7lBWyXPGkoq+wAikXFa1Gnam2xmWOBqDDvWg==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.11.0",
-        "@vue/babel-preset-app": "^4.5.17",
-        "@vue/cli-shared-utils": "^4.5.17",
+        "@vue/babel-preset-app": "^4.5.19",
+        "@vue/cli-shared-utils": "^4.5.19",
         "babel-loader": "^8.1.0",
         "cache-loader": "^4.1.0",
         "thread-loader": "^2.1.3",
@@ -3211,12 +3240,12 @@
       }
     },
     "@vue/cli-plugin-eslint": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.17.tgz",
-      "integrity": "sha512-bVNDP+SuWcuJrBMc+JLaKvlxx25XKIlZBa+zzFnxhHZlwPZ7CeBD3e2wnsygJyPoKgDZcZwDgmEz1BZzMEjsNw==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.19.tgz",
+      "integrity": "sha512-53sa4Pu9j5KajesFlj494CcO8vVo3e3nnZ1CCKjGGnrF90id1rUeepcFfz5XjwfEtbJZp2x/NoX/EZE6zCzSFQ==",
       "dev": true,
       "requires": {
-        "@vue/cli-shared-utils": "^4.5.17",
+        "@vue/cli-shared-utils": "^4.5.19",
         "eslint-loader": "^2.2.1",
         "globby": "^9.2.0",
         "inquirer": "^7.1.0",
@@ -3225,24 +3254,24 @@
       }
     },
     "@vue/cli-plugin-router": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.17.tgz",
-      "integrity": "sha512-9r9CSwqv2+39XHQPDZJ0uaTtTP7oe0Gx17m7kBhHG3FA7R7AOSk2aVzhHZmDRhzlOxjx9kQSvrOSMfUG0kV4dQ==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-4.5.19.tgz",
+      "integrity": "sha512-3icGzH1IbVYmMMsOwYa0lal/gtvZLebFXdE5hcQJo2mnTwngXGMTyYAzL56EgHBPjbMmRpyj6Iw9k4aVInVX6A==",
       "dev": true,
       "requires": {
-        "@vue/cli-shared-utils": "^4.5.17"
+        "@vue/cli-shared-utils": "^4.5.19"
       }
     },
     "@vue/cli-plugin-unit-jest": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-4.5.17.tgz",
-      "integrity": "sha512-Ta8hx68Y252umik1yD50dyyrwhzj4KzTEpngY9YKB6bY6pga9GK2olqyrb3+2X8o6tnSQkTFz+UcobfRQJDs5A==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-unit-jest/-/cli-plugin-unit-jest-4.5.19.tgz",
+      "integrity": "sha512-yX61mpeU7DnjOv+Lxtjmr3pzESqBLIXeTK4MJpa/UdzrhnylHP4r6mCYETNLEYtxp8WZUXPjZFIzrKn5poZPJg==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.11.0",
         "@babel/plugin-transform-modules-commonjs": "^7.9.6",
         "@types/jest": "^24.0.19",
-        "@vue/cli-shared-utils": "^4.5.17",
+        "@vue/cli-shared-utils": "^4.5.19",
         "babel-core": "^7.0.0-bridge.0",
         "babel-jest": "^24.9.0",
         "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
@@ -3303,13 +3332,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "slash": {
@@ -3321,7 +3350,7 @@
         "source-map": {
           "version": "0.5.7",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
           "dev": true
         },
         "supports-color": {
@@ -3355,15 +3384,15 @@
       }
     },
     "@vue/cli-plugin-vuex": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.17.tgz",
-      "integrity": "sha512-ck/ju2T2dmPKLWK/5QctNJs9SCb+eSZbbmr8neFkMc7GlbXw6qLWw5v3Vpd4KevdQA8QuQOA1pjUmzpCiU/mYQ==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.19.tgz",
+      "integrity": "sha512-DUmfdkG3pCdkP7Iznd87RfE9Qm42mgp2hcrNcYQYSru1W1gX2dG/JcW8bxmeGSa06lsxi9LEIc/QD1yPajSCZw==",
       "dev": true
     },
     "@vue/cli-service": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.5.17.tgz",
-      "integrity": "sha512-MqfkRYIcIUACe3nYlzNrYstJTWRXHlIqh6JCkbWbdnXWN+IfaVdlG8zw5Q0DVcSdGvkevUW7zB4UhtZB4uyAcA==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-4.5.19.tgz",
+      "integrity": "sha512-+Wpvj8fMTCt9ZPOLu5YaLkFCQmB4MrZ26aRmhhKiCQ/4PMoL6mLezfqdt6c+m2htM+1WV5RunRo+0WHl2DfwZA==",
       "dev": true,
       "requires": {
         "@intervolga/optimize-cssnano-plugin": "^1.0.5",
@@ -3372,10 +3401,10 @@
         "@types/minimist": "^1.2.0",
         "@types/webpack": "^4.0.0",
         "@types/webpack-dev-server": "^3.11.0",
-        "@vue/cli-overlay": "^4.5.17",
-        "@vue/cli-plugin-router": "^4.5.17",
-        "@vue/cli-plugin-vuex": "^4.5.17",
-        "@vue/cli-shared-utils": "^4.5.17",
+        "@vue/cli-overlay": "^4.5.19",
+        "@vue/cli-plugin-router": "^4.5.19",
+        "@vue/cli-plugin-vuex": "^4.5.19",
+        "@vue/cli-shared-utils": "^4.5.19",
         "@vue/component-compiler-utils": "^3.1.2",
         "@vue/preload-webpack-plugin": "^1.1.0",
         "@vue/web-component-wrapper": "^1.2.0",
@@ -3515,7 +3544,7 @@
             "hash-sum": {
               "version": "1.0.2",
               "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
-              "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
+              "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
               "dev": true
             }
           }
@@ -3533,16 +3562,16 @@
           },
           "dependencies": {
             "json5": {
-              "version": "2.2.1",
-              "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
-              "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+              "version": "2.2.3",
+              "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+              "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
               "dev": true,
               "optional": true
             },
             "loader-utils": {
-              "version": "2.0.2",
-              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
-              "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+              "version": "2.0.4",
+              "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+              "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
               "dev": true,
               "optional": true,
               "requires": {
@@ -3567,9 +3596,9 @@
       }
     },
     "@vue/cli-shared-utils": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.17.tgz",
-      "integrity": "sha512-VoFNdxvTW4vZu3ne+j1Mf7mU99J2SAoRVn9XPrsouTUUJablglM8DASk7Ixhsh6ymyL/W9EADQFR6Pgj8Ujjuw==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.19.tgz",
+      "integrity": "sha512-JYpdsrC/d9elerKxbEUtmSSU6QRM60rirVubOewECHkBHj+tLNznWq/EhCjswywtePyLaMUK25eTqnTSZlEE+g==",
       "dev": true,
       "requires": {
         "@achrinza/node-ipc": "9.2.2",
@@ -3624,13 +3653,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "lru-cache": {
@@ -3675,14 +3704,14 @@
       }
     },
     "@vue/cli-ui": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-ui/-/cli-ui-4.5.17.tgz",
-      "integrity": "sha512-x5o+RUNkPLO6wDNY5/Sec9ZuZExzARU8KkP2SwM2SWLirkjKgLVvyL4/snAIWwRLxsxXVuyZ8YJdqnWidixzCg==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-ui/-/cli-ui-4.5.19.tgz",
+      "integrity": "sha512-wiZ63+uqB1b2+AH9rUkubvQEzDCwEBLJZ4xaTkRlI+eSPUj1nZM2SzvwbWo2aL0ObFAQwWaXwqjh2VW3zQEvJw==",
       "dev": true,
       "requires": {
         "@achrinza/node-ipc": "9.2.2",
         "@akryum/winattr": "^3.0.0",
-        "@vue/cli-shared-utils": "^4.5.17",
+        "@vue/cli-shared-utils": "^4.5.19",
         "apollo-server-express": "^2.13.1",
         "clone": "^2.1.1",
         "deepmerge": "^4.2.2",
@@ -3713,7 +3742,7 @@
         "clone": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-          "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+          "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
           "dev": true
         },
         "lru-cache": {
@@ -3734,48 +3763,48 @@
       }
     },
     "@vue/cli-ui-addon-webpack": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-ui-addon-webpack/-/cli-ui-addon-webpack-4.5.17.tgz",
-      "integrity": "sha512-AZMnDzToM2uxW/73mfjJweea3atC5sxOC3Nel5UEFnQK0hWN0/8NW6nPAiKDc+kJpZWFb7Y6ReP2hwYizUJK2w==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-ui-addon-webpack/-/cli-ui-addon-webpack-4.5.19.tgz",
+      "integrity": "sha512-ESkNIVbEMGwe42OcGbfF4fQD/JRHiN4notSGkQ5fnjVnEkNlH4UJotoNgea4lmnnCIMjMEN4wz3f+ADXKjzOlA==",
       "dev": true
     },
     "@vue/cli-ui-addon-widgets": {
-      "version": "4.5.17",
-      "resolved": "https://registry.npmjs.org/@vue/cli-ui-addon-widgets/-/cli-ui-addon-widgets-4.5.17.tgz",
-      "integrity": "sha512-K49weNsBggUL54Etdqml0hR3PpNzQSXUxC0G52qGNuZwPPxpZfZSQIH8GQ4jBTS8ySXmQYDT99DyxKSedE7McQ==",
+      "version": "4.5.19",
+      "resolved": "https://registry.npmjs.org/@vue/cli-ui-addon-widgets/-/cli-ui-addon-widgets-4.5.19.tgz",
+      "integrity": "sha512-MuujsH7WHSe4UJVok2EbmiTW/Ig3dlUMYqr1cvRY+G5YDpzychPetiQsHzoJsBc5yfEdRRzmJ04KAuzfAzIA1g==",
       "dev": true
     },
     "@vue/compiler-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
-      "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz",
+      "integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==",
       "requires": {
         "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.31",
+        "@vue/shared": "3.2.37",
         "estree-walker": "^2.0.2",
         "source-map": "^0.6.1"
       }
     },
     "@vue/compiler-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
-      "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz",
+      "integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==",
       "requires": {
-        "@vue/compiler-core": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-core": "3.2.37",
+        "@vue/shared": "3.2.37"
       }
     },
     "@vue/compiler-sfc": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
-      "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz",
+      "integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==",
       "requires": {
         "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.31",
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/compiler-ssr": "3.2.31",
-        "@vue/reactivity-transform": "3.2.31",
-        "@vue/shared": "3.2.31",
+        "@vue/compiler-core": "3.2.37",
+        "@vue/compiler-dom": "3.2.37",
+        "@vue/compiler-ssr": "3.2.37",
+        "@vue/reactivity-transform": "3.2.37",
+        "@vue/shared": "3.2.37",
         "estree-walker": "^2.0.2",
         "magic-string": "^0.25.7",
         "postcss": "^8.1.10",
@@ -3788,11 +3817,11 @@
           "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
         },
         "postcss": {
-          "version": "8.4.12",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz",
-          "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==",
+          "version": "8.4.14",
+          "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
+          "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
           "requires": {
-            "nanoid": "^3.3.1",
+            "nanoid": "^3.3.4",
             "picocolors": "^1.0.0",
             "source-map-js": "^1.0.2"
           }
@@ -3800,12 +3829,12 @@
       }
     },
     "@vue/compiler-ssr": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
-      "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz",
+      "integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==",
       "requires": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-dom": "3.2.37",
+        "@vue/shared": "3.2.37"
       }
     },
     "@vue/component-compiler-utils": {
@@ -3828,7 +3857,7 @@
         "hash-sum": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
-          "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
+          "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
           "dev": true
         },
         "lru-cache": {
@@ -3844,15 +3873,15 @@
         "yallist": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
           "dev": true
         }
       }
     },
     "@vue/devtools-api": {
-      "version": "6.1.3",
-      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.3.tgz",
-      "integrity": "sha512-79InfO2xHv+WHIrH1bHXQUiQD/wMls9qBk6WVwGCbdwP7/3zINtvqPNMtmSHXsIKjvUAHc8L0ouOj6ZQQRmcXg=="
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.0.tgz",
+      "integrity": "sha512-pF1G4wky+hkifDiZSWn8xfuLOJI1ZXtuambpBEYaf7Xaf6zC/pM29rvAGpd3qaGXnr4BAXU1Pxz/VfvBGwexGA=="
     },
     "@vue/eslint-config-standard": {
       "version": "5.1.2",
@@ -3872,62 +3901,62 @@
       "dev": true
     },
     "@vue/reactivity": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz",
-      "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
+      "integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==",
       "requires": {
-        "@vue/shared": "3.2.31"
+        "@vue/shared": "3.2.37"
       }
     },
     "@vue/reactivity-transform": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
-      "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz",
+      "integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==",
       "requires": {
         "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.31",
-        "@vue/shared": "3.2.31",
+        "@vue/compiler-core": "3.2.37",
+        "@vue/shared": "3.2.37",
         "estree-walker": "^2.0.2",
         "magic-string": "^0.25.7"
       }
     },
     "@vue/runtime-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
-      "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz",
+      "integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==",
       "requires": {
-        "@vue/reactivity": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/reactivity": "3.2.37",
+        "@vue/shared": "3.2.37"
       }
     },
     "@vue/runtime-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
-      "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz",
+      "integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==",
       "requires": {
-        "@vue/runtime-core": "3.2.31",
-        "@vue/shared": "3.2.31",
+        "@vue/runtime-core": "3.2.37",
+        "@vue/shared": "3.2.37",
         "csstype": "^2.6.8"
       }
     },
     "@vue/server-renderer": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
-      "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz",
+      "integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==",
       "requires": {
-        "@vue/compiler-ssr": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-ssr": "3.2.37",
+        "@vue/shared": "3.2.37"
       }
     },
     "@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz",
+      "integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw=="
     },
     "@vue/test-utils": {
-      "version": "2.0.0-rc.17",
-      "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.0-rc.17.tgz",
-      "integrity": "sha512-7LHZKsFRV/HqDoMVY+cJamFzgHgsrmQFalROHC5FMWrzPzd+utG5e11krj1tVsnxYufGA2ABShX4nlcHXED+zQ==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.0.tgz",
+      "integrity": "sha512-zL5kygNq7hONrO1CzaUGprEAklAX+pH8J1MPMCU3Rd2xtSYkZ+PmKU3oEDRg8VAGdL5lNJHzDgrud5amFPtirw==",
       "dev": true
     },
     "@vue/web-component-wrapper": {
@@ -4133,9 +4162,9 @@
       "dev": true
     },
     "abab": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
-      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+      "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
       "dev": true
     },
     "abbrev": {
@@ -4182,9 +4211,9 @@
       "dev": true
     },
     "address": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz",
-      "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz",
+      "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==",
       "dev": true
     },
     "agent-base": {
@@ -4240,13 +4269,7 @@
     "alphanum-sort": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
-      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
-      "dev": true
-    },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+      "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==",
       "dev": true
     },
     "ansi-align": {
@@ -4319,7 +4342,7 @@
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+      "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
     },
     "ansi-styles": {
       "version": "4.3.0",
@@ -4330,21 +4353,22 @@
       }
     },
     "ant-design-vue": {
-      "version": "2.2.8",
-      "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.2.8.tgz",
-      "integrity": "sha512-3graq9/gCfJQs6hznrHV6sa9oDmk/D1H3Oo0vLdVpPS/I61fZPk8NEyNKCHpNA6fT2cx6xx9U3QS63uuyikg/Q==",
+      "version": "3.2.9",
+      "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-3.2.9.tgz",
+      "integrity": "sha512-fnZJpAf4tYAPGBALD9dv8VBmeqfJ+xPt87DfgY1/JI6x3hn6Gfge7voF1GkS0yVT0zLyzc2aTWkDVFWKB5iupA==",
       "requires": {
-        "@ant-design/icons-vue": "^6.0.0",
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-vue": "^6.1.0",
         "@babel/runtime": "^7.10.5",
+        "@ctrl/tinycolor": "^3.4.0",
         "@simonwep/pickr": "~1.8.0",
         "array-tree-filter": "^2.1.0",
-        "async-validator": "^3.3.0",
+        "async-validator": "^4.0.0",
+        "dayjs": "^1.10.5",
         "dom-align": "^1.12.1",
         "dom-scroll-into-view": "^2.0.0",
         "lodash": "^4.17.21",
         "lodash-es": "^4.17.15",
-        "moment": "^2.27.0",
-        "omit.js": "^2.0.0",
         "resize-observer-polyfill": "^1.5.1",
         "scroll-into-view-if-needed": "^2.2.25",
         "shallow-equal": "^1.0.0",
@@ -4352,32 +4376,72 @@
         "warning": "^4.0.0"
       }
     },
+    "antd": {
+      "version": "4.21.4",
+      "resolved": "https://registry.npmjs.org/antd/-/antd-4.21.4.tgz",
+      "integrity": "sha512-seSXyzVwfOI0a9/HIxUQ41nVJpR8hHn+DbBlJTCOM92Q56Fvw0FlbtZuNMA2gaOkDzyPTnp4W7adlbaqkGsvog==",
+      "requires": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons": "^4.7.0",
+        "@ant-design/react-slick": "~0.29.1",
+        "@babel/runtime": "^7.18.3",
+        "@ctrl/tinycolor": "^3.4.0",
+        "classnames": "^2.2.6",
+        "copy-to-clipboard": "^3.2.0",
+        "lodash": "^4.17.21",
+        "memoize-one": "^6.0.0",
+        "moment": "^2.29.2",
+        "rc-cascader": "~3.6.0",
+        "rc-checkbox": "~2.3.0",
+        "rc-collapse": "~3.3.0",
+        "rc-dialog": "~8.9.0",
+        "rc-drawer": "~4.4.2",
+        "rc-dropdown": "~4.0.0",
+        "rc-field-form": "~1.26.1",
+        "rc-image": "~5.7.0",
+        "rc-input": "~0.0.1-alpha.5",
+        "rc-input-number": "~7.3.0",
+        "rc-mentions": "~1.8.0",
+        "rc-menu": "~9.6.0",
+        "rc-motion": "^2.5.1",
+        "rc-notification": "~4.6.0",
+        "rc-pagination": "~3.1.16",
+        "rc-picker": "~2.6.8",
+        "rc-progress": "~3.3.2",
+        "rc-rate": "~2.9.0",
+        "rc-resize-observer": "^1.2.0",
+        "rc-segmented": "~2.1.0",
+        "rc-select": "~14.1.1",
+        "rc-slider": "~10.0.0",
+        "rc-steps": "~4.1.0",
+        "rc-switch": "~3.2.0",
+        "rc-table": "~7.24.0",
+        "rc-tabs": "~11.16.0",
+        "rc-textarea": "~0.3.0",
+        "rc-tooltip": "~5.1.1",
+        "rc-tree": "~5.6.5",
+        "rc-tree-select": "~5.4.0",
+        "rc-trigger": "^5.2.10",
+        "rc-upload": "~4.3.0",
+        "rc-util": "^5.22.5",
+        "scroll-into-view-if-needed": "^2.2.25"
+      }
+    },
     "antd-theme-generator": {
-      "version": "1.2.10",
-      "resolved": "https://registry.npmjs.org/antd-theme-generator/-/antd-theme-generator-1.2.10.tgz",
-      "integrity": "sha512-Eh/5bJP7SejZCTOg1Rq4WB9PkfGXEKo3XWRCFj6Cf1Zsp76Jc9eiXUOijQ2YMtkhLMN2RJOnaNR+OOSpfAonDw==",
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/antd-theme-generator/-/antd-theme-generator-1.2.11.tgz",
+      "integrity": "sha512-7A3lXyLb7eD7MXK7aSgZZ4DxQEdhZwyKhzIm70orUZPQJ8N8TWhZphyOWSGCe8yUqGQhi8PcpM2pLmTriZyKBw==",
       "requires": {
         "glob": "^7.1.3",
         "hash.js": "^1.1.5",
         "less": "^3.9.0",
-        "less-bundle-promise": "^1.0.7",
+        "less-bundle-promise": "^1.0.11",
         "less-plugin-npm-import": "^2.1.0",
         "postcss": "^6.0.21",
         "postcss-less": "^3.1.4",
         "strip-css-comments": "^4.1.0"
       },
       "dependencies": {
-        "ajv": {
-          "version": "6.6.2",
-          "bundled": true,
-          "optional": true,
-          "requires": {
-            "fast-deep-equal": "^2.0.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
-          }
-        },
         "ansi-styles": {
           "version": "3.2.1",
           "bundled": true,
@@ -4527,11 +4591,6 @@
           "bundled": true,
           "optional": true
         },
-        "fast-deep-equal": {
-          "version": "2.0.1",
-          "bundled": true,
-          "optional": true
-        },
         "fast-json-stable-stringify": {
           "version": "2.0.0",
           "bundled": true,
@@ -4593,6 +4652,24 @@
           "requires": {
             "ajv": "^6.5.5",
             "har-schema": "^2.0.0"
+          },
+          "dependencies": {
+            "ajv": {
+              "version": "6.6.2",
+              "bundled": true,
+              "optional": true,
+              "requires": {
+                "fast-deep-equal": "^2.0.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+              }
+            },
+            "fast-deep-equal": {
+              "version": "2.0.1",
+              "bundled": true,
+              "optional": true
+            }
           }
         },
         "has-flag": {
@@ -4708,6 +4785,10 @@
               "requires": {
                 "asap": "~2.0.3"
               }
+            },
+            "resolve": {
+              "version": "1.1.7",
+              "bundled": true
             }
           }
         },
@@ -4781,12 +4862,6 @@
             "chalk": "^2.4.1",
             "source-map": "^0.6.1",
             "supports-color": "^5.4.0"
-          },
-          "dependencies": {
-            "source-map": {
-              "version": "0.6.1",
-              "bundled": true
-            }
           }
         },
         "promise": {
@@ -4844,10 +4919,6 @@
             "uuid": "^3.3.2"
           }
         },
-        "resolve": {
-          "version": "1.1.7",
-          "bundled": true
-        },
         "safe-buffer": {
           "version": "5.1.2",
           "bundled": true,
@@ -4860,8 +4931,7 @@
         },
         "source-map": {
           "version": "0.6.1",
-          "bundled": true,
-          "optional": true
+          "bundled": true
         },
         "sshpk": {
           "version": "1.15.2",
@@ -4963,7 +5033,7 @@
     "any-promise": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
       "dev": true
     },
     "anymatch": {
@@ -4997,9 +5067,9 @@
       }
     },
     "apollo-graphql": {
-      "version": "0.9.5",
-      "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.9.5.tgz",
-      "integrity": "sha512-RGt5k2JeBqrmnwRM0VOgWFiGKlGJMfmiif/4JvdaEqhMJ+xqe/9cfDYzXfn33ke2eWixsAbjEbRfy8XbaN9nTw==",
+      "version": "0.9.7",
+      "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.9.7.tgz",
+      "integrity": "sha512-bezL9ItUWUGHTm1bI/XzIgiiZbhXpsC7uxk4UxFPmcVJwJsDc3ayZ99oXxAaK+3Rbg/IoqrHckA6CwmkCsbaSA==",
       "dev": true,
       "requires": {
         "core-js-pure": "^3.10.2",
@@ -5038,9 +5108,9 @@
       }
     },
     "apollo-server-core": {
-      "version": "2.25.3",
-      "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.25.3.tgz",
-      "integrity": "sha512-Midow3uZoJ9TjFNeCNSiWElTVZlvmB7G7tG6PPoxIR9Px90/v16Q6EzunDIO0rTJHRC3+yCwZkwtf8w2AcP0sA==",
+      "version": "2.25.4",
+      "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.25.4.tgz",
+      "integrity": "sha512-1u3BnFKbCt6F9SPM7ZoWmtHK6ubme56H8hV5Mjv3KbfSairU76SU79IhO05BEJE57S6N+ddb1rm3Uk93X6YeGw==",
       "dev": true,
       "requires": {
         "@apollographql/apollo-tools": "^0.5.0",
@@ -5095,9 +5165,9 @@
       "dev": true
     },
     "apollo-server-express": {
-      "version": "2.25.3",
-      "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.25.3.tgz",
-      "integrity": "sha512-tTFYn0oKH2qqLwVj7Ez2+MiKleXACODiGh5IxsB7VuYCPMAi9Yl8iUSlwTjQUvgCWfReZjnf0vFL2k5YhDlrtQ==",
+      "version": "2.25.4",
+      "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.25.4.tgz",
+      "integrity": "sha512-1Yd9DscLlCP5BhfAkNxg+aGcaTKnL36FyezdL7Iqc+KelON5PAyX8qpAChKL8Z3L2YHJzIk/Haf4dFJLKUjx9w==",
       "dev": true,
       "requires": {
         "@apollographql/graphql-playground-html": "1.6.27",
@@ -5107,7 +5177,7 @@
         "@types/express": "^4.17.12",
         "@types/express-serve-static-core": "^4.17.21",
         "accepts": "^1.3.5",
-        "apollo-server-core": "^2.25.3",
+        "apollo-server-core": "^2.25.4",
         "apollo-server-types": "^0.9.0",
         "body-parser": "^1.18.3",
         "cors": "^2.8.5",
@@ -5175,7 +5245,7 @@
     "archive-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz",
-      "integrity": "sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA=",
+      "integrity": "sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==",
       "dev": true,
       "requires": {
         "file-type": "^4.2.0"
@@ -5184,7 +5254,7 @@
         "file-type": {
           "version": "4.4.0",
           "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz",
-          "integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU=",
+          "integrity": "sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==",
           "dev": true
         }
       }
@@ -5209,7 +5279,7 @@
     "arr-diff": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
       "dev": true
     },
     "arr-flatten": {
@@ -5221,42 +5291,36 @@
     "arr-union": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
       "dev": true
     },
     "array-equal": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
-      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+      "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==",
       "dev": true
     },
     "array-find": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz",
-      "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=",
-      "dev": true
-    },
-    "array-find-index": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+      "integrity": "sha512-kO/vVCacW9mnpn3WPWbTVlEnOabK2L7LWi2HViURtCM46y1zb6I8UMjx4LgbiqadTgHnLInUronwn3ampNTJtQ==",
       "dev": true
     },
     "array-flatten": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
       "dev": true
     },
     "array-includes": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz",
-      "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==",
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
+      "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.1",
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5",
         "get-intrinsic": "^1.1.1",
         "is-string": "^1.0.7"
       }
@@ -5269,7 +5333,7 @@
     "array-union": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+      "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
       "dev": true,
       "requires": {
         "array-uniq": "^1.0.1"
@@ -5278,24 +5342,38 @@
     "array-uniq": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+      "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
       "dev": true
     },
     "array-unique": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
       "dev": true
     },
     "array.prototype.flat": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz",
-      "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
+      "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.0"
+        "es-abstract": "^1.19.2",
+        "es-shim-unscopables": "^1.0.0"
+      }
+    },
+    "array.prototype.reduce": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
+      "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.2",
+        "es-array-method-boxes-properly": "^1.0.0",
+        "is-string": "^1.0.7"
       }
     },
     "arrify": {
@@ -5345,13 +5423,13 @@
         "inherits": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
-          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+          "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==",
           "dev": true
         },
         "util": {
           "version": "0.10.3",
           "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
-          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+          "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==",
           "dev": true,
           "requires": {
             "inherits": "2.0.1"
@@ -5362,12 +5440,12 @@
     "assert-plus": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+      "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
     },
     "assign-symbols": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
       "dev": true
     },
     "ast-types": {
@@ -5383,9 +5461,9 @@
       "dev": true
     },
     "async": {
-      "version": "2.6.3",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
-      "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+      "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
       "dev": true,
       "requires": {
         "lodash": "^4.17.14"
@@ -5397,12 +5475,6 @@
       "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
       "dev": true
     },
-    "async-foreach": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
-      "dev": true
-    },
     "async-limiter": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
@@ -5427,14 +5499,14 @@
       }
     },
     "async-validator": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.5.2.tgz",
-      "integrity": "sha512-8eLCg00W9pIRZSB781UUX/H6Oskmm8xloZfr09lz5bikRpBVDlJ3hRVuxxP1SxcwsEYfJ4IU8Q19Y8/893r3rQ=="
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
     },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
     },
     "atob": {
       "version": "2.1.2",
@@ -5460,7 +5532,7 @@
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+      "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
     },
     "aws4": {
       "version": "1.11.0",
@@ -5478,7 +5550,7 @@
     "babel-code-frame": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+      "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
       "dev": true,
       "requires": {
         "chalk": "^1.1.3",
@@ -5489,13 +5561,13 @@
         "ansi-styles": {
           "version": "2.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+          "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
           "dev": true
         },
         "chalk": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
           "dev": true,
           "requires": {
             "ansi-styles": "^2.2.1",
@@ -5508,13 +5580,13 @@
         "js-tokens": {
           "version": "3.0.2",
           "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+          "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
           "dev": true
         },
         "supports-color": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+          "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
           "dev": true
         }
       }
@@ -5540,12 +5612,12 @@
       },
       "dependencies": {
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -5688,9 +5760,9 @@
           "dev": true
         },
         "istanbul-lib-instrument": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz",
-          "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==",
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz",
+          "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==",
           "dev": true,
           "requires": {
             "@babel/core": "^7.12.3",
@@ -5764,13 +5836,13 @@
           }
         },
         "micromatch": {
-          "version": "4.0.4",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-          "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+          "version": "4.0.5",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+          "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
           "dev": true,
           "requires": {
-            "braces": "^3.0.1",
-            "picomatch": "^2.2.3"
+            "braces": "^3.0.2",
+            "picomatch": "^2.3.1"
           }
         },
         "semver": {
@@ -5802,9 +5874,9 @@
       }
     },
     "babel-loader": {
-      "version": "8.2.4",
-      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz",
-      "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==",
+      "version": "8.2.5",
+      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz",
+      "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==",
       "dev": true,
       "requires": {
         "find-cache-dir": "^3.3.1",
@@ -5838,7 +5910,7 @@
     "babel-messages": {
       "version": "6.23.0",
       "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
-      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+      "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==",
       "dev": true,
       "requires": {
         "babel-runtime": "^6.22.0"
@@ -5854,13 +5926,12 @@
       }
     },
     "babel-plugin-import": {
-      "version": "1.13.3",
-      "resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.13.3.tgz",
-      "integrity": "sha512-1qCWdljJOrDRH/ybaCZuDgySii4yYrtQ8OJQwrcDqdt0y67N30ng3X3nABg6j7gR7qUJgcMa9OMhc4AGViDwWw==",
+      "version": "1.13.5",
+      "resolved": "https://registry.npmjs.org/babel-plugin-import/-/babel-plugin-import-1.13.5.tgz",
+      "integrity": "sha512-IkqnoV+ov1hdJVofly9pXRJmeDm9EtROfrc5i6eII0Hix2xMs5FEm8FG3ExMvazbnZBbgHIt6qdO8And6lCloQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-module-imports": "^7.0.0",
-        "@babel/runtime": "^7.0.0"
+        "@babel/helper-module-imports": "^7.0.0"
       }
     },
     "babel-plugin-istanbul": {
@@ -5906,7 +5977,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         }
       }
@@ -5978,7 +6049,7 @@
     "babel-plugin-transform-strict-mode": {
       "version": "6.24.1",
       "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
-      "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+      "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==",
       "dev": true,
       "requires": {
         "babel-runtime": "^6.22.0",
@@ -6018,7 +6089,7 @@
     "babel-runtime": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
       "dev": true,
       "requires": {
         "core-js": "^2.4.0",
@@ -6042,7 +6113,7 @@
     "babel-template": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
-      "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+      "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==",
       "dev": true,
       "requires": {
         "babel-runtime": "^6.26.0",
@@ -6055,7 +6126,7 @@
     "babel-traverse": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
-      "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+      "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==",
       "dev": true,
       "requires": {
         "babel-code-frame": "^6.26.0",
@@ -6087,7 +6158,7 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         }
       }
@@ -6095,7 +6166,7 @@
     "babel-types": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
-      "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+      "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
       "dev": true,
       "requires": {
         "babel-runtime": "^6.26.0",
@@ -6107,7 +6178,7 @@
         "to-fast-properties": {
           "version": "1.0.3",
           "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
-          "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+          "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==",
           "dev": true
         }
       }
@@ -6121,7 +6192,7 @@
     "backo2": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+      "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==",
       "dev": true
     },
     "balanced-match": {
@@ -6147,7 +6218,7 @@
         "define-property": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^1.0.0"
@@ -6193,13 +6264,13 @@
     "batch": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
-      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+      "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
       "dev": true
     },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
       "requires": {
         "tweetnacl": "^0.14.3"
       }
@@ -6246,15 +6317,6 @@
         "safe-buffer": "^5.1.1"
       }
     },
-    "block-stream": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
-      "dev": true,
-      "requires": {
-        "inherits": "~2.0.0"
-      }
-    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -6262,27 +6324,29 @@
       "dev": true
     },
     "bn.js": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
-      "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+      "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
       "dev": true
     },
     "body-parser": {
-      "version": "1.19.2",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
-      "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
+      "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
       "dev": true,
       "requires": {
         "bytes": "3.1.2",
         "content-type": "~1.0.4",
         "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "http-errors": "1.8.1",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
         "iconv-lite": "0.4.24",
-        "on-finished": "~2.3.0",
-        "qs": "6.9.7",
-        "raw-body": "2.4.3",
-        "type-is": "~1.6.18"
+        "on-finished": "2.4.1",
+        "qs": "6.10.3",
+        "raw-body": "2.5.1",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
       },
       "dependencies": {
         "debug": {
@@ -6294,6 +6358,25 @@
             "ms": "2.0.0"
           }
         },
+        "depd": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+          "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+          "dev": true
+        },
+        "http-errors": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+          "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+          "dev": true,
+          "requires": {
+            "depd": "2.0.0",
+            "inherits": "2.0.4",
+            "setprototypeof": "1.2.0",
+            "statuses": "2.0.1",
+            "toidentifier": "1.0.1"
+          }
+        },
         "iconv-lite": {
           "version": "0.4.24",
           "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -6306,13 +6389,22 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "qs": {
-          "version": "6.9.7",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
-          "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
+          "version": "6.10.3",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+          "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+          "dev": true,
+          "requires": {
+            "side-channel": "^1.0.4"
+          }
+        },
+        "statuses": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+          "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
           "dev": true
         }
       }
@@ -6320,7 +6412,7 @@
     "bonjour": {
       "version": "3.5.0",
       "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
-      "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+      "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
       "dev": true,
       "requires": {
         "array-flatten": "^2.1.0",
@@ -6342,7 +6434,7 @@
     "boolbase": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
       "dev": true
     },
     "boxen": {
@@ -6429,7 +6521,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -6440,7 +6532,7 @@
     "brorand": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
-      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+      "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
       "dev": true
     },
     "browser-process-hrtime": {
@@ -6461,7 +6553,7 @@
         "resolve": {
           "version": "1.1.7",
           "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
-          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+          "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==",
           "dev": true
         }
       }
@@ -6559,24 +6651,15 @@
       }
     },
     "browserslist": {
-      "version": "4.20.2",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
-      "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
+      "version": "4.21.1",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.1.tgz",
+      "integrity": "sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30001317",
-        "electron-to-chromium": "^1.4.84",
-        "escalade": "^3.1.1",
-        "node-releases": "^2.0.2",
-        "picocolors": "^1.0.0"
-      },
-      "dependencies": {
-        "picocolors": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-          "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-          "dev": true
-        }
+        "caniuse-lite": "^1.0.30001359",
+        "electron-to-chromium": "^1.4.172",
+        "node-releases": "^2.0.5",
+        "update-browserslist-db": "^1.0.4"
       }
     },
     "bs-logger": {
@@ -6626,13 +6709,13 @@
     "buffer-crc32": {
       "version": "0.2.13",
       "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-      "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
+      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
       "dev": true
     },
     "buffer-fill": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
-      "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
+      "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
       "dev": true
     },
     "buffer-from": {
@@ -6656,19 +6739,19 @@
     "buffer-xor": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
-      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+      "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
       "dev": true
     },
     "builtin-status-codes": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
-      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+      "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
       "dev": true
     },
     "builtins": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
-      "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
+      "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ=="
     },
     "busboy": {
       "version": "0.3.1",
@@ -6834,13 +6917,13 @@
     "call-me-maybe": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
-      "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+      "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==",
       "dev": true
     },
     "caller-callsite": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
-      "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+      "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
       "dev": true,
       "requires": {
         "callsites": "^2.0.0"
@@ -6849,7 +6932,7 @@
         "callsites": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-          "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+          "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==",
           "dev": true
         }
       }
@@ -6857,7 +6940,7 @@
     "caller-path": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
-      "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+      "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
       "dev": true,
       "requires": {
         "caller-callsite": "^2.0.0"
@@ -6872,7 +6955,7 @@
     "camel-case": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
-      "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+      "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==",
       "dev": true,
       "requires": {
         "no-case": "^2.2.0",
@@ -6884,24 +6967,6 @@
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
     },
-    "camelcase-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
-      "dev": true,
-      "requires": {
-        "camelcase": "^2.0.0",
-        "map-obj": "^1.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-          "dev": true
-        }
-      }
-    },
     "caniuse-api": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -6915,9 +6980,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001320",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz",
-      "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==",
+      "version": "1.0.30001361",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz",
+      "integrity": "sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ==",
       "dev": true
     },
     "capture-exit": {
@@ -6938,7 +7003,7 @@
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+      "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
     },
     "caw": {
       "version": "2.0.1",
@@ -6961,12 +7026,6 @@
         "supports-color": "^7.1.0"
       }
     },
-    "charcodes": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/charcodes/-/charcodes-0.2.0.tgz",
-      "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==",
-      "dev": true
-    },
     "chardet": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
@@ -6976,7 +7035,7 @@
     "charenc": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
-      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
+      "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
     },
     "chart.js": {
       "version": "3.9.1",
@@ -7073,7 +7132,7 @@
     "cint": {
       "version": "8.2.1",
       "resolved": "https://registry.npmjs.org/cint/-/cint-8.2.1.tgz",
-      "integrity": "sha1-cDhrG0jidz0NYxZqVa/5TvRFahI="
+      "integrity": "sha512-gyWqJHXgDFPNx7PEyFJotutav+al92TTC3dWlMFyTETlOyKBQMZb7Cetqmj3GlrnSILHwSJRwf4mIGzc7C5lXw=="
     },
     "cipher-base": {
       "version": "1.0.4",
@@ -7100,7 +7159,7 @@
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^0.1.0"
@@ -7108,6 +7167,11 @@
         }
       }
     },
+    "classnames": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+      "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+    },
     "clean-css": {
       "version": "4.2.4",
       "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
@@ -7130,7 +7194,7 @@
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
-      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==",
       "dev": true,
       "requires": {
         "restore-cursor": "^2.0.0"
@@ -7179,9 +7243,9 @@
       "dev": true
     },
     "clipboard": {
-      "version": "2.0.10",
-      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.10.tgz",
-      "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==",
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
       "requires": {
         "good-listener": "^1.2.2",
         "select": "^1.1.2",
@@ -7258,7 +7322,7 @@
     "clone": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
       "dev": true
     },
     "clone-deep": {
@@ -7286,7 +7350,7 @@
     "clone-response": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
-      "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+      "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==",
       "requires": {
         "mimic-response": "^1.0.0"
       }
@@ -7315,7 +7379,7 @@
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
       "dev": true
     },
     "coa": {
@@ -7361,13 +7425,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -7384,12 +7448,12 @@
     "code-point-at": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+      "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="
     },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
       "dev": true,
       "requires": {
         "map-visit": "^1.0.0",
@@ -7418,7 +7482,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         }
       }
@@ -7437,9 +7501,9 @@
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
     },
     "color-string": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
-      "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+      "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
       "dev": true,
       "requires": {
         "color-name": "^1.0.0",
@@ -7449,7 +7513,7 @@
     "colors": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
-      "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+      "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw=="
     },
     "combined-stream": {
       "version": "1.0.8",
@@ -7467,7 +7531,7 @@
     "commondir": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+      "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
       "dev": true
     },
     "component-emitter": {
@@ -7503,7 +7567,7 @@
         "bytes": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-          "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+          "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
           "dev": true
         },
         "debug": {
@@ -7518,7 +7582,7 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         }
       }
@@ -7531,7 +7595,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
     "concat-stream": {
       "version": "1.6.2",
@@ -7548,7 +7612,7 @@
     "condense-newlines": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz",
-      "integrity": "sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=",
+      "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==",
       "dev": true,
       "requires": {
         "extend-shallow": "^2.0.1",
@@ -7559,7 +7623,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -7568,7 +7632,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -7614,7 +7678,7 @@
     "console-control-strings": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+      "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
     },
     "consolidate": {
       "version": "0.15.1",
@@ -7628,7 +7692,7 @@
     "constants-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
-      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+      "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==",
       "dev": true
     },
     "content-disposition": {
@@ -7664,15 +7728,15 @@
       }
     },
     "cookie": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-      "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
       "dev": true
     },
     "cookie-signature": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
       "dev": true
     },
     "copy-anything": {
@@ -7699,12 +7763,12 @@
       },
       "dependencies": {
         "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "minimist": "^1.2.6"
           }
         },
         "rimraf": {
@@ -7721,9 +7785,17 @@
     "copy-descriptor": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
       "dev": true
     },
+    "copy-to-clipboard": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
+      "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
+      "requires": {
+        "toggle-selection": "^1.0.6"
+      }
+    },
     "copy-webpack-plugin": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz",
@@ -7776,7 +7848,7 @@
         "globby": {
           "version": "7.1.1",
           "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
-          "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+          "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==",
           "dev": true,
           "requires": {
             "array-union": "^1.0.1",
@@ -7854,7 +7926,7 @@
         "slash": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
-          "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+          "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
           "dev": true
         },
         "ssri": {
@@ -7881,17 +7953,17 @@
       }
     },
     "core-js": {
-      "version": "3.21.1",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
-      "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="
+      "version": "3.23.3",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.3.tgz",
+      "integrity": "sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q=="
     },
     "core-js-compat": {
-      "version": "3.21.1",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz",
-      "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==",
+      "version": "3.23.3",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.3.tgz",
+      "integrity": "sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.19.1",
+        "browserslist": "^4.21.0",
         "semver": "7.0.0"
       },
       "dependencies": {
@@ -7904,9 +7976,9 @@
       }
     },
     "core-js-pure": {
-      "version": "3.21.1",
-      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
-      "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==",
+      "version": "3.23.3",
+      "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.23.3.tgz",
+      "integrity": "sha512-XpoouuqIj4P+GWtdyV8ZO3/u4KftkeDVMfvp+308eGMhCrA3lVDSmAxO0c6GGOcmgVlaKDrgWVMo49h2ab/TDA==",
       "dev": true
     },
     "core-util-is": {
@@ -7939,7 +8011,7 @@
         "parse-json": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
           "dev": true,
           "requires": {
             "error-ex": "^1.3.1",
@@ -7993,6 +8065,11 @@
         "sha.js": "^2.4.8"
       }
     },
+    "cronstrue": {
+      "version": "2.26.0",
+      "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.26.0.tgz",
+      "integrity": "sha512-M1VdV3hpBAsd1Zzvqcvf63wgDpcwCuS4WiNEVFpJ0s33MGO2sVDTfswYq0EPypCmESrCzmgL8h68DTzJuSDbVA=="
+    },
     "cross-spawn": {
       "version": "6.0.5",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -8026,7 +8103,7 @@
     "crypt": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
-      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
+      "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
     },
     "crypto-browserify": {
       "version": "3.12.0",
@@ -8067,7 +8144,7 @@
     "css-color-names": {
       "version": "0.0.4",
       "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
-      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
+      "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==",
       "dev": true
     },
     "css-declaration-sorter": {
@@ -8172,7 +8249,7 @@
     "cssfilter": {
       "version": "0.0.10",
       "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
-      "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=",
+      "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
       "dev": true
     },
     "cssnano": {
@@ -8228,13 +8305,13 @@
     "cssnano-util-get-arguments": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
-      "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=",
+      "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==",
       "dev": true
     },
     "cssnano-util-get-match": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
-      "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=",
+      "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==",
       "dev": true
     },
     "cssnano-util-raw-cache": {
@@ -8314,25 +8391,16 @@
         "ndjson": "^1.4.0"
       }
     },
-    "currently-unhandled": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
-      "dev": true,
-      "requires": {
-        "array-find-index": "^1.0.1"
-      }
-    },
     "cyclist": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
-      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+      "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==",
       "dev": true
     },
     "dashdash": {
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -8351,7 +8419,7 @@
         "tr46": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
           "dev": true,
           "requires": {
             "punycode": "^2.1.0"
@@ -8376,10 +8444,20 @@
         }
       }
     },
+    "date-fns": {
+      "version": "2.28.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
+      "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
+    },
+    "dayjs": {
+      "version": "1.11.3",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz",
+      "integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A=="
+    },
     "deasync": {
-      "version": "0.1.24",
-      "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz",
-      "integrity": "sha512-i98vg42xNfRZCymummMAN0rIcQ1gZFinSe3btvPIvy6JFTaeHcumeKybRo2HTv86nasfmT0nEgAn2ggLZhOCVA==",
+      "version": "0.1.26",
+      "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.26.tgz",
+      "integrity": "sha512-YKw0BmJSWxkjtQsbgn6Q9CHSWB7DKMen8vKrgyC006zy0UZ6nWyGidB0IzZgqkVRkOglAeUaFtiRTeLyel72bg==",
       "dev": true,
       "requires": {
         "bindings": "^1.5.0",
@@ -8397,13 +8475,13 @@
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
       "dev": true
     },
     "decode-uri-component": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
       "dev": true
     },
     "decompress": {
@@ -8434,7 +8512,7 @@
             "pify": {
               "version": "3.0.0",
               "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-              "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+              "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
               "dev": true
             }
           }
@@ -8442,7 +8520,7 @@
         "pify": {
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
           "dev": true
         }
       }
@@ -8450,7 +8528,7 @@
     "decompress-response": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
-      "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+      "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==",
       "requires": {
         "mimic-response": "^1.0.0"
       }
@@ -8469,7 +8547,7 @@
         "file-type": {
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
-          "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=",
+          "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==",
           "dev": true
         }
       }
@@ -8509,7 +8587,7 @@
         "file-type": {
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
-          "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=",
+          "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==",
           "dev": true
         }
       }
@@ -8517,7 +8595,7 @@
     "decompress-unzip": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
-      "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=",
+      "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==",
       "dev": true,
       "requires": {
         "file-type": "^3.8.0",
@@ -8529,13 +8607,13 @@
         "file-type": {
           "version": "3.9.0",
           "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
-          "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=",
+          "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
           "dev": true
         },
         "get-stream": {
           "version": "2.3.1",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
-          "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
+          "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==",
           "dev": true,
           "requires": {
             "object-assign": "^4.0.1",
@@ -8545,7 +8623,7 @@
         "pify": {
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
           "dev": true
         }
       }
@@ -8690,7 +8768,7 @@
     "defaults": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
-      "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+      "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==",
       "dev": true,
       "requires": {
         "clone": "^1.0.2"
@@ -8702,12 +8780,13 @@
       "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
     },
     "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+      "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
       "dev": true,
       "requires": {
-        "object-keys": "^1.0.12"
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
       }
     },
     "define-property": {
@@ -8769,7 +8848,7 @@
         "globby": {
           "version": "6.1.0",
           "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
-          "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+          "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
           "dev": true,
           "requires": {
             "array-union": "^1.0.1",
@@ -8782,7 +8861,7 @@
             "pify": {
               "version": "2.3.0",
               "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-              "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+              "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
               "dev": true
             }
           }
@@ -8813,7 +8892,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
     },
     "delegate": {
       "version": "3.2.0",
@@ -8823,17 +8902,17 @@
     "delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
     },
     "depd": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
     },
     "deprecated-decorator": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz",
-      "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=",
+      "integrity": "sha512-MHidOOnCHGlZDKsI21+mbIIhf4Fff+hhCTB7gtVg4uoIqjcrTZc5v6M+GS2zVI0sV7PqK415rb8XaOSQsQkHOw==",
       "dev": true
     },
     "des.js": {
@@ -8847,15 +8926,15 @@
       }
     },
     "destroy": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
       "dev": true
     },
     "detect-newline": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
-      "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
+      "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==",
       "dev": true
     },
     "detect-node": {
@@ -8910,7 +8989,7 @@
     "dns-equal": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
-      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+      "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
       "dev": true
     },
     "dns-packet": {
@@ -8926,7 +9005,7 @@
     "dns-txt": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
-      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+      "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
       "dev": true,
       "requires": {
         "buffer-indexof": "^1.0.0"
@@ -8942,9 +9021,9 @@
       }
     },
     "dom-align": {
-      "version": "1.12.2",
-      "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz",
-      "integrity": "sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg=="
+      "version": "1.12.3",
+      "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.3.tgz",
+      "integrity": "sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA=="
     },
     "dom-converter": {
       "version": "0.2.0",
@@ -8971,9 +9050,9 @@
       },
       "dependencies": {
         "domelementtype": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-          "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+          "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
           "dev": true
         }
       }
@@ -9017,9 +9096,9 @@
       },
       "dependencies": {
         "domelementtype": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-          "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+          "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
           "dev": true
         }
       }
@@ -9083,7 +9162,7 @@
         "cacheable-request": {
           "version": "2.1.4",
           "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz",
-          "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=",
+          "integrity": "sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ==",
           "dev": true,
           "requires": {
             "clone-response": "1.0.2",
@@ -9098,7 +9177,7 @@
             "lowercase-keys": {
               "version": "1.0.0",
               "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
-              "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=",
+              "integrity": "sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A==",
               "dev": true
             }
           }
@@ -9106,7 +9185,7 @@
         "get-stream": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-          "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+          "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==",
           "dev": true
         },
         "got": {
@@ -9143,7 +9222,7 @@
         "into-stream": {
           "version": "3.1.0",
           "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
-          "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
+          "integrity": "sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==",
           "dev": true,
           "requires": {
             "from2": "^2.1.1",
@@ -9188,7 +9267,7 @@
         "sort-keys": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
-          "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=",
+          "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==",
           "dev": true,
           "requires": {
             "is-plain-obj": "^1.0.0"
@@ -9216,7 +9295,7 @@
     "duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-      "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+      "integrity": "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA=="
     },
     "duplexify": {
       "version": "3.7.1",
@@ -9239,7 +9318,7 @@
     "ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
       "requires": {
         "jsbn": "~0.1.0",
         "safer-buffer": "^2.1.0"
@@ -9282,7 +9361,7 @@
         "yallist": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
           "dev": true
         }
       }
@@ -9290,7 +9369,7 @@
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
       "dev": true
     },
     "ejs": {
@@ -9300,9 +9379,9 @@
       "dev": true
     },
     "electron-to-chromium": {
-      "version": "1.4.92",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.92.tgz",
-      "integrity": "sha512-YAVbvQIcDE/IJ/vzDMjD484/hsRbFPW2qXJPaYTfOhtligmfYEYOep+5QojpaEU9kq6bMvNeC2aG7arYvTHYsA==",
+      "version": "1.4.176",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.176.tgz",
+      "integrity": "sha512-92JdgyRlcNDwuy75MjuFSb3clt6DGJ2IXSpg0MCjKd3JV9eSmuUAIyWiGAp/EtT0z2D4rqbYqThQLV90maH3Zw==",
       "dev": true
     },
     "elliptic": {
@@ -9341,7 +9420,7 @@
     "encodeurl": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
       "dev": true
     },
     "encoding": {
@@ -9387,7 +9466,7 @@
     "enquire.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
-      "integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ="
+      "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
     },
     "entities": {
       "version": "2.2.0",
@@ -9430,40 +9509,58 @@
       }
     },
     "error-stack-parser": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz",
-      "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
+      "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
       "dev": true,
       "requires": {
-        "stackframe": "^1.1.1"
+        "stackframe": "^1.3.4"
       }
     },
     "es-abstract": {
-      "version": "1.19.1",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
-      "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
+      "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
         "es-to-primitive": "^1.2.1",
         "function-bind": "^1.1.1",
+        "function.prototype.name": "^1.1.5",
         "get-intrinsic": "^1.1.1",
         "get-symbol-description": "^1.0.0",
         "has": "^1.0.3",
-        "has-symbols": "^1.0.2",
+        "has-property-descriptors": "^1.0.0",
+        "has-symbols": "^1.0.3",
         "internal-slot": "^1.0.3",
         "is-callable": "^1.2.4",
-        "is-negative-zero": "^2.0.1",
+        "is-negative-zero": "^2.0.2",
         "is-regex": "^1.1.4",
-        "is-shared-array-buffer": "^1.0.1",
+        "is-shared-array-buffer": "^1.0.2",
         "is-string": "^1.0.7",
-        "is-weakref": "^1.0.1",
-        "object-inspect": "^1.11.0",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.0",
         "object-keys": "^1.1.1",
         "object.assign": "^4.1.2",
-        "string.prototype.trimend": "^1.0.4",
-        "string.prototype.trimstart": "^1.0.4",
-        "unbox-primitive": "^1.0.1"
+        "regexp.prototype.flags": "^1.4.3",
+        "string.prototype.trimend": "^1.0.5",
+        "string.prototype.trimstart": "^1.0.5",
+        "unbox-primitive": "^1.0.2"
+      }
+    },
+    "es-array-method-boxes-properly": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+      "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+      "dev": true
+    },
+    "es-shim-unscopables": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+      "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
       }
     },
     "es-to-primitive": {
@@ -9491,13 +9588,13 @@
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
       "dev": true
     },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
     },
     "escodegen": {
       "version": "1.14.3",
@@ -9595,7 +9692,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "eslint-scope": {
@@ -9629,7 +9726,7 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "import-fresh": {
@@ -9715,12 +9812,12 @@
           }
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -9757,7 +9854,7 @@
         "enhanced-resolve": {
           "version": "0.9.1",
           "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz",
-          "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=",
+          "integrity": "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==",
           "dev": true,
           "requires": {
             "graceful-fs": "^4.1.2",
@@ -9768,22 +9865,22 @@
         "memory-fs": {
           "version": "0.2.0",
           "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz",
-          "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=",
+          "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==",
           "dev": true
         },
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -9797,7 +9894,7 @@
         "tapable": {
           "version": "0.1.10",
           "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
-          "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=",
+          "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==",
           "dev": true
         }
       }
@@ -9868,7 +9965,7 @@
         "find-up": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-          "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+          "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
           "dev": true,
           "requires": {
             "locate-path": "^2.0.0"
@@ -9877,7 +9974,7 @@
         "locate-path": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-          "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+          "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
           "dev": true,
           "requires": {
             "p-locate": "^2.0.0",
@@ -9896,7 +9993,7 @@
         "p-locate": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-          "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+          "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
           "dev": true,
           "requires": {
             "p-limit": "^1.1.0"
@@ -9905,13 +10002,13 @@
         "p-try": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-          "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+          "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
           "dev": true
         },
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         }
       }
@@ -9944,9 +10041,9 @@
       }
     },
     "eslint-plugin-import": {
-      "version": "2.25.4",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz",
-      "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==",
+      "version": "2.26.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+      "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
       "dev": true,
       "requires": {
         "array-includes": "^3.1.4",
@@ -9954,14 +10051,14 @@
         "debug": "^2.6.9",
         "doctrine": "^2.1.0",
         "eslint-import-resolver-node": "^0.3.6",
-        "eslint-module-utils": "^2.7.2",
+        "eslint-module-utils": "^2.7.3",
         "has": "^1.0.3",
-        "is-core-module": "^2.8.0",
+        "is-core-module": "^2.8.1",
         "is-glob": "^4.0.3",
-        "minimatch": "^3.0.4",
+        "minimatch": "^3.1.2",
         "object.values": "^1.1.5",
-        "resolve": "^1.20.0",
-        "tsconfig-paths": "^3.12.0"
+        "resolve": "^1.22.0",
+        "tsconfig-paths": "^3.14.1"
       },
       "dependencies": {
         "debug": {
@@ -9985,16 +10082,16 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -10031,12 +10128,12 @@
           "dev": true
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -10193,7 +10290,7 @@
     "etag": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
       "dev": true
     },
     "event-pubsub": {
@@ -10215,13 +10312,10 @@
       "dev": true
     },
     "eventsource": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
-      "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
-      "dev": true,
-      "requires": {
-        "original": "^1.0.0"
-      }
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
+      "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
+      "dev": true
     },
     "evp_bytestokey": {
       "version": "1.0.3",
@@ -10260,13 +10354,13 @@
     "exit": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+      "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
       "dev": true
     },
     "expand-brackets": {
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
       "dev": true,
       "requires": {
         "debug": "^2.3.3",
@@ -10290,7 +10384,7 @@
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^0.1.0"
@@ -10299,7 +10393,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -10308,7 +10402,7 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         }
       }
@@ -10316,7 +10410,7 @@
     "expand-tilde": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
-      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+      "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
       "dev": true,
       "requires": {
         "homedir-polyfill": "^1.0.1"
@@ -10357,44 +10451,45 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         }
       }
     },
     "express": {
-      "version": "4.17.3",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
-      "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
+      "version": "4.18.1",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
+      "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
       "dev": true,
       "requires": {
         "accepts": "~1.3.8",
         "array-flatten": "1.1.1",
-        "body-parser": "1.19.2",
+        "body-parser": "1.20.0",
         "content-disposition": "0.5.4",
         "content-type": "~1.0.4",
-        "cookie": "0.4.2",
+        "cookie": "0.5.0",
         "cookie-signature": "1.0.6",
         "debug": "2.6.9",
-        "depd": "~1.1.2",
+        "depd": "2.0.0",
         "encodeurl": "~1.0.2",
         "escape-html": "~1.0.3",
         "etag": "~1.8.1",
-        "finalhandler": "~1.1.2",
+        "finalhandler": "1.2.0",
         "fresh": "0.5.2",
+        "http-errors": "2.0.0",
         "merge-descriptors": "1.0.1",
         "methods": "~1.1.2",
-        "on-finished": "~2.3.0",
+        "on-finished": "2.4.1",
         "parseurl": "~1.3.3",
         "path-to-regexp": "0.1.7",
         "proxy-addr": "~2.0.7",
-        "qs": "6.9.7",
+        "qs": "6.10.3",
         "range-parser": "~1.2.1",
         "safe-buffer": "5.2.1",
-        "send": "0.17.2",
-        "serve-static": "1.14.2",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
         "setprototypeof": "1.2.0",
-        "statuses": "~1.5.0",
+        "statuses": "2.0.1",
         "type-is": "~1.6.18",
         "utils-merge": "1.0.1",
         "vary": "~1.1.2"
@@ -10409,30 +10504,58 @@
             "ms": "2.0.0"
           }
         },
+        "depd": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+          "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+          "dev": true
+        },
+        "http-errors": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+          "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+          "dev": true,
+          "requires": {
+            "depd": "2.0.0",
+            "inherits": "2.0.4",
+            "setprototypeof": "1.2.0",
+            "statuses": "2.0.1",
+            "toidentifier": "1.0.1"
+          }
+        },
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "qs": {
-          "version": "6.9.7",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
-          "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
-          "dev": true
+          "version": "6.10.3",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+          "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+          "dev": true,
+          "requires": {
+            "side-channel": "^1.0.4"
+          }
         },
         "safe-buffer": {
           "version": "5.2.1",
           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
           "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
           "dev": true
+        },
+        "statuses": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+          "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+          "dev": true
         }
       }
     },
     "express-history-api-fallback": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/express-history-api-fallback/-/express-history-api-fallback-2.2.1.tgz",
-      "integrity": "sha1-OirSf3vryQ/FM9EQ18bYMJe80Fc=",
+      "integrity": "sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg==",
       "dev": true
     },
     "ext-list": {
@@ -10462,7 +10585,7 @@
     "extend-shallow": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
       "dev": true,
       "requires": {
         "assign-symbols": "^1.0.0",
@@ -10530,7 +10653,7 @@
         "define-property": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^1.0.0"
@@ -10539,7 +10662,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -10579,7 +10702,7 @@
     "extract-from-css": {
       "version": "0.4.4",
       "resolved": "https://registry.npmjs.org/extract-from-css/-/extract-from-css-0.4.4.tgz",
-      "integrity": "sha1-HqffLnx8brmSL6COitrqSG9vj5I=",
+      "integrity": "sha512-41qWGBdtKp9U7sgBxAQ7vonYqSXzgW/SiAYzq4tdWSVhAShvpVCH1nyvPQgjse6EdgbW7Y7ERdT3674/lKr65A==",
       "dev": true,
       "requires": {
         "css": "^2.1.0"
@@ -10588,7 +10711,7 @@
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+      "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
     },
     "fast-deep-equal": {
       "version": "3.1.3",
@@ -10617,7 +10740,7 @@
     "fast-levenshtein": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
       "dev": true
     },
     "fastq": {
@@ -10650,7 +10773,7 @@
     "fd-slicer": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
-      "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
       "dev": true,
       "requires": {
         "pend": "~1.2.0"
@@ -10726,7 +10849,7 @@
     "filename-reserved-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
-      "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
+      "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
       "dev": true
     },
     "filenamify": {
@@ -10749,7 +10872,7 @@
     "fill-range": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
       "dev": true,
       "requires": {
         "extend-shallow": "^2.0.1",
@@ -10761,7 +10884,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -10770,17 +10893,17 @@
       }
     },
     "finalhandler": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
-      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
       "dev": true,
       "requires": {
         "debug": "2.6.9",
         "encodeurl": "~1.0.2",
         "escape-html": "~1.0.3",
-        "on-finished": "~2.3.0",
+        "on-finished": "2.4.1",
         "parseurl": "~1.3.3",
-        "statuses": "~1.5.0",
+        "statuses": "2.0.1",
         "unpipe": "~1.0.0"
       },
       "dependencies": {
@@ -10796,7 +10919,13 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+          "dev": true
+        },
+        "statuses": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+          "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
           "dev": true
         }
       }
@@ -10814,13 +10943,13 @@
         "json5": {
           "version": "0.5.1",
           "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-          "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+          "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
           "dev": true
         },
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         }
       }
@@ -10918,9 +11047,9 @@
       "dev": true
     },
     "flow-parser": {
-      "version": "0.174.1",
-      "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.174.1.tgz",
-      "integrity": "sha512-nDMOvlFR+4doLpB3OJpseHZ7uEr3ENptlF6qMas/kzQmNcLzMwfQeFX0gGJ/+em7UdldB/nGsk55tDTOvjbCuw==",
+      "version": "0.181.2",
+      "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.181.2.tgz",
+      "integrity": "sha512-+QzNZEmhYNF9SHrKI8M2lzT07UGkJW6Zoeg7wP+aGkFxh0Mh/wx8eyS/lcwY9bd3B4azS6K50ZjyIjzMWpowGg==",
       "dev": true
     },
     "flush-write-stream": {
@@ -10934,9 +11063,9 @@
       }
     },
     "follow-redirects": {
-      "version": "1.14.9",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
-      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
+      "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
     },
     "for-each": {
       "version": "0.3.3",
@@ -10950,13 +11079,13 @@
     "for-in": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
       "dev": true
     },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+      "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
     },
     "form-data": {
       "version": "2.3.3",
@@ -10977,7 +11106,7 @@
     "fragment-cache": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
       "dev": true,
       "requires": {
         "map-cache": "^0.2.2"
@@ -10986,13 +11115,13 @@
     "fresh": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
       "dev": true
     },
     "from2": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+      "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
       "dev": true,
       "requires": {
         "inherits": "^2.0.1",
@@ -11014,7 +11143,7 @@
     "fs-exists-sync": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
-      "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
+      "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==",
       "dev": true
     },
     "fs-extra": {
@@ -11039,7 +11168,7 @@
     "fs-write-stream-atomic": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+      "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==",
       "dev": true,
       "requires": {
         "graceful-fs": "^4.1.2",
@@ -11051,7 +11180,7 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
     },
     "fsevents": {
       "version": "2.3.2",
@@ -11060,38 +11189,6 @@
       "dev": true,
       "optional": true
     },
-    "fstream": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
-      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
-        "rimraf": "2"
-      },
-      "dependencies": {
-        "mkdirp": {
-          "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
-          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.6"
-          }
-        },
-        "rimraf": {
-          "version": "2.7.1",
-          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-          "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-          "dev": true,
-          "requires": {
-            "glob": "^7.1.3"
-          }
-        }
-      }
-    },
     "fswin": {
       "version": "2.17.1227",
       "resolved": "https://registry.npmjs.org/fswin/-/fswin-2.17.1227.tgz",
@@ -11104,16 +11201,34 @@
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
+    "function.prototype.name": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+      "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.0",
+        "functions-have-names": "^1.2.2"
+      }
+    },
     "functional-red-black-tree": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+      "dev": true
+    },
+    "functions-have-names": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+      "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
       "dev": true
     },
     "gauge": {
       "version": "2.7.4",
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+      "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
       "requires": {
         "aproba": "^1.0.3",
         "console-control-strings": "^1.0.0",
@@ -11125,25 +11240,16 @@
         "wide-align": "^1.1.0"
       }
     },
-    "gaze": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
-      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
-      "dev": true,
-      "requires": {
-        "globule": "^1.0.0"
-      }
-    },
     "generate-function": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-1.1.0.tgz",
-      "integrity": "sha1-VMIbCAGSsW2Yd3ecW7gWZudyNl8=",
+      "integrity": "sha512-Wv4qgYgt2m9QH7K+jklCX/o4gn1ijnS4nT+nxPYBbhdqZLDLtvNh2o26KP/nxN42Tk6AnrGftCLzjiMuckZeQw==",
       "dev": true
     },
     "generate-object-property": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
-      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+      "integrity": "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==",
       "dev": true,
       "requires": {
         "is-property": "^1.0.0"
@@ -11162,14 +11268,14 @@
       "dev": true
     },
     "get-intrinsic": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
-      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
+      "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
       "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
-        "has-symbols": "^1.0.1"
+        "has-symbols": "^1.0.3"
       }
     },
     "get-package-type": {
@@ -11213,13 +11319,13 @@
     "get-value": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
       "dev": true
     },
     "getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
       "requires": {
         "assert-plus": "^1.0.0"
       }
@@ -11227,13 +11333,13 @@
     "git-clone": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/git-clone/-/git-clone-0.1.0.tgz",
-      "integrity": "sha1-DXYWN3gJOu9/HDAjjyqe8/B6Lrk=",
+      "integrity": "sha512-zs9rlfa7HyaJAKG9o+V7C6qfMzyc+tb1IIXdUFcOBcR1U7siKy/uPdauLlrH1mc0vOgUwIv4BF+QxPiiTYz3Rw==",
       "dev": true
     },
     "git-config-path": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz",
-      "integrity": "sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=",
+      "integrity": "sha512-KcJ2dlrrP5DbBnYIZ2nlikALfRhKzNSX0stvv3ImJ+fvC4hXKoV+U+74SV0upg+jlQZbrtQzc0bu6/Zh+7aQbg==",
       "dev": true,
       "requires": {
         "extend-shallow": "^2.0.1",
@@ -11244,7 +11350,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -11253,14 +11359,14 @@
       }
     },
     "glob": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
-      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
         "inherits": "2",
-        "minimatch": "^3.0.4",
+        "minimatch": "^3.1.1",
         "once": "^1.3.0",
         "path-is-absolute": "^1.0.0"
       }
@@ -11268,7 +11374,7 @@
     "glob-parent": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
       "dev": true,
       "requires": {
         "is-glob": "^3.1.0",
@@ -11278,7 +11384,7 @@
         "is-glob": {
           "version": "3.1.0",
           "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
           "dev": true,
           "requires": {
             "is-extglob": "^2.1.0"
@@ -11289,7 +11395,7 @@
     "glob-to-regexp": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
-      "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
+      "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==",
       "dev": true
     },
     "global-dirs": {
@@ -11343,46 +11449,10 @@
         }
       }
     },
-    "globule": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz",
-      "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==",
-      "dev": true,
-      "requires": {
-        "glob": "~7.1.1",
-        "lodash": "~4.17.10",
-        "minimatch": "~3.0.2"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.1.7",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
-          "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "minimatch": {
-          "version": "3.0.8",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
-          "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "^1.1.7"
-          }
-        }
-      }
-    },
     "good-listener": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
       "requires": {
         "delegate": "^3.1.2"
       }
@@ -11406,9 +11476,9 @@
       }
     },
     "graceful-fs": {
-      "version": "4.2.9",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
-      "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
     },
     "graphql": {
       "version": "14.7.0",
@@ -11449,9 +11519,9 @@
       },
       "dependencies": {
         "tslib": {
-          "version": "2.3.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+          "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
           "dev": true
         }
       }
@@ -11478,7 +11548,7 @@
     "growly": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
-      "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
+      "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
       "dev": true
     },
     "gzip-size": {
@@ -11508,7 +11578,7 @@
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+      "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
     },
     "har-validator": {
       "version": "5.1.5",
@@ -11531,15 +11601,15 @@
     "has-ansi": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+      "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
       "requires": {
         "ansi-regex": "^2.0.0"
       }
     },
     "has-bigints": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
-      "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+      "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
       "dev": true
     },
     "has-flag": {
@@ -11547,6 +11617,15 @@
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
     },
+    "has-property-descriptors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+      "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+      "dev": true,
+      "requires": {
+        "get-intrinsic": "^1.1.1"
+      }
+    },
     "has-symbol-support-x": {
       "version": "1.4.2",
       "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
@@ -11580,12 +11659,12 @@
     "has-unicode": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+      "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
     },
     "has-value": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
       "dev": true,
       "requires": {
         "get-value": "^2.0.6",
@@ -11596,7 +11675,7 @@
     "has-values": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
       "dev": true,
       "requires": {
         "is-number": "^3.0.0",
@@ -11606,7 +11685,7 @@
         "kind-of": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -11685,7 +11764,7 @@
     "hmac-drbg": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
-      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
       "dev": true,
       "requires": {
         "hash.js": "^1.0.3",
@@ -11719,7 +11798,7 @@
     "hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
-      "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+      "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
       "dev": true,
       "requires": {
         "inherits": "^2.0.1",
@@ -11731,13 +11810,13 @@
     "hsl-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
-      "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
+      "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==",
       "dev": true
     },
     "hsla-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
-      "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
+      "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==",
       "dev": true
     },
     "html-encoding-sniffer": {
@@ -11785,15 +11864,15 @@
       }
     },
     "html-tags": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
-      "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz",
+      "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==",
       "dev": true
     },
     "html-webpack-plugin": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
-      "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
+      "integrity": "sha512-Br4ifmjQojUP4EmHnRBoUIYcZ9J7M4bTMcm7u6xoIAIuq2Nte4TzXX0533owvkQKQD1WeMTTTyD4Ni4QKxS0Bg==",
       "dev": true,
       "requires": {
         "html-minifier": "^3.2.3",
@@ -11814,19 +11893,19 @@
         "emojis-list": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
-          "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+          "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==",
           "dev": true
         },
         "json5": {
           "version": "0.5.1",
           "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-          "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+          "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
           "dev": true
         },
         "loader-utils": {
           "version": "0.2.17",
           "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
-          "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
+          "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==",
           "dev": true,
           "requires": {
             "big.js": "^3.1.3",
@@ -11860,9 +11939,9 @@
       },
       "dependencies": {
         "dom-serializer": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
-          "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+          "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
           "dev": true,
           "requires": {
             "domelementtype": "^2.0.1",
@@ -11871,9 +11950,9 @@
           }
         },
         "domelementtype": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-          "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+          "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
           "dev": true
         },
         "domutils": {
@@ -11897,7 +11976,7 @@
     "http-deceiver": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
-      "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+      "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
       "dev": true
     },
     "http-errors": {
@@ -11914,9 +11993,9 @@
       }
     },
     "http-parser-js": {
-      "version": "0.5.6",
-      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz",
-      "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==",
+      "version": "0.5.8",
+      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+      "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
       "dev": true
     },
     "http-proxy": {
@@ -11992,13 +12071,13 @@
           "dev": true
         },
         "micromatch": {
-          "version": "4.0.4",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-          "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+          "version": "4.0.5",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+          "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
           "dev": true,
           "requires": {
-            "braces": "^3.0.1",
-            "picomatch": "^2.2.3"
+            "braces": "^3.0.2",
+            "picomatch": "^2.3.1"
           }
         },
         "to-regex-range": {
@@ -12015,7 +12094,7 @@
     "http-signature": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
       "requires": {
         "assert-plus": "^1.0.0",
         "jsprim": "^1.2.2",
@@ -12025,13 +12104,13 @@
     "https-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+      "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
       "dev": true
     },
     "https-proxy-agent": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
-      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
       "requires": {
         "agent-base": "6",
         "debug": "4"
@@ -12046,7 +12125,7 @@
     "humanize-ms": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
-      "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+      "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
       "requires": {
         "ms": "^2.0.0"
       }
@@ -12078,7 +12157,7 @@
     "iferr": {
       "version": "0.1.5",
       "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+      "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==",
       "dev": true
     },
     "ignore": {
@@ -12098,20 +12177,20 @@
     "image-size": {
       "version": "0.5.5",
       "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
-      "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+      "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
       "dev": true,
       "optional": true
     },
     "immutable": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
-      "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+      "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
       "dev": true
     },
     "import-cwd": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
-      "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+      "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==",
       "dev": true,
       "requires": {
         "import-from": "^2.1.0"
@@ -12120,7 +12199,7 @@
     "import-fresh": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-      "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+      "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
       "dev": true,
       "requires": {
         "caller-path": "^2.0.0",
@@ -12130,7 +12209,7 @@
     "import-from": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
-      "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+      "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==",
       "dev": true,
       "requires": {
         "resolve-from": "^3.0.0"
@@ -12139,7 +12218,7 @@
     "import-global": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/import-global/-/import-global-0.1.0.tgz",
-      "integrity": "sha1-l7OP1EQRTuwWgkqTX42ldbV6oc4=",
+      "integrity": "sha512-8+hPJLML+m1ym9NSeZXTXFkY5+ml0fYFAzO5yhZiaFQvk9kO0NkE7vd7e7kCVjkTmAxsDPbrWwLQACMwGTDgIg==",
       "dev": true,
       "requires": {
         "global-dirs": "^0.1.0"
@@ -12148,7 +12227,7 @@
         "global-dirs": {
           "version": "0.1.1",
           "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-          "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+          "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
           "dev": true,
           "requires": {
             "ini": "^1.3.4"
@@ -12159,7 +12238,7 @@
     "import-lazy": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
-      "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
+      "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A=="
     },
     "import-local": {
       "version": "2.0.0",
@@ -12174,13 +12253,7 @@
     "imurmurhash": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
-    },
-    "in-publish": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
-      "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
-      "dev": true
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
     },
     "indent-string": {
       "version": "4.0.0",
@@ -12190,7 +12263,7 @@
     "indexes-of": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
-      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==",
       "dev": true
     },
     "infer-owner": {
@@ -12201,7 +12274,7 @@
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -12348,7 +12421,7 @@
     "into-stream": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-2.0.1.tgz",
-      "integrity": "sha1-25sANpRFPq4JHYpchMwRUHt4HTE=",
+      "integrity": "sha512-7EHX+bSqaYHfWnrREpeGUKO6ox5tW6NgziFx7cZqxBZ3RNRir9cnPCDvJNjrROLP6guznhxMkyus0sK2qQzhrQ==",
       "dev": true,
       "requires": {
         "from2": "^2.1.1"
@@ -12364,14 +12437,14 @@
       }
     },
     "ip": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+      "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
     },
     "ip-regex": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+      "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==",
       "dev": true
     },
     "ipaddr.js": {
@@ -12383,13 +12456,13 @@
     "is-absolute-url": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
-      "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
+      "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==",
       "dev": true
     },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
@@ -12398,7 +12471,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -12419,7 +12492,7 @@
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
       "dev": true
     },
     "is-bigint": {
@@ -12472,7 +12545,7 @@
     "is-color-stop": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
-      "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
+      "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==",
       "dev": true,
       "requires": {
         "css-color-names": "^0.0.4",
@@ -12484,9 +12557,9 @@
       }
     },
     "is-core-module": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
-      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+      "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
       "dev": true,
       "requires": {
         "has": "^1.0.3"
@@ -12495,7 +12568,7 @@
     "is-data-descriptor": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
@@ -12504,7 +12577,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -12543,7 +12616,7 @@
     "is-directory": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-      "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+      "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==",
       "dev": true
     },
     "is-docker": {
@@ -12555,25 +12628,19 @@
     "is-extendable": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
       "dev": true
     },
     "is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-finite": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
-      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
       "dev": true
     },
     "is-fullwidth-code-point": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
       "requires": {
         "number-is-nan": "^1.0.0"
       }
@@ -12605,12 +12672,12 @@
     "is-lambda": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
-      "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU="
+      "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="
     },
     "is-natural-number": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
-      "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=",
+      "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==",
       "dev": true
     },
     "is-negative-zero": {
@@ -12627,7 +12694,7 @@
     "is-number": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
@@ -12636,7 +12703,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -12645,9 +12712,9 @@
       }
     },
     "is-number-object": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
-      "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+      "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
       "dev": true,
       "requires": {
         "has-tostringtag": "^1.0.0"
@@ -12698,7 +12765,7 @@
     "is-plain-obj": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+      "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
       "dev": true
     },
     "is-plain-object": {
@@ -12715,7 +12782,7 @@
     "is-property": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
-      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+      "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
       "dev": true
     },
     "is-regex": {
@@ -12741,15 +12808,18 @@
       "dev": true
     },
     "is-shared-array-buffer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
-      "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==",
-      "dev": true
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+      "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.2"
+      }
     },
     "is-stream": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+      "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
       "dev": true
     },
     "is-string": {
@@ -12773,13 +12843,7 @@
     "is-typedarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
-    },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
+      "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
     },
     "is-weakref": {
       "version": "1.0.2",
@@ -12799,7 +12863,7 @@
     "is-whitespace": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz",
-      "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=",
+      "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==",
       "dev": true
     },
     "is-windows": {
@@ -12811,7 +12875,7 @@
     "is-wsl": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+      "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
       "dev": true
     },
     "is-yarn-global": {
@@ -12822,29 +12886,29 @@
     "isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
     },
     "isbinaryfile": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz",
-      "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==",
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
       "dev": true
     },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
     },
     "isobject": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
       "dev": true
     },
     "isstream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+      "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
     },
     "istanbul-lib-coverage": {
       "version": "2.0.5",
@@ -12889,7 +12953,7 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "make-dir": {
@@ -12999,7 +13063,7 @@
     "javascript-stringify": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz",
-      "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=",
+      "integrity": "sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ==",
       "dev": true
     },
     "jest": {
@@ -13061,7 +13125,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "emoji-regex": {
@@ -13082,13 +13146,13 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
           "dev": true
         },
         "jest-cli": {
@@ -13134,7 +13198,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         },
         "string-width": {
@@ -13296,13 +13360,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "slash": {
@@ -13366,13 +13430,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -13440,13 +13504,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -13571,7 +13635,7 @@
         "tr46": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
           "dev": true,
           "requires": {
             "punycode": "^2.1.0"
@@ -13659,7 +13723,7 @@
         "normalize-path": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
           "dev": true,
           "requires": {
             "remove-trailing-separator": "^1.0.1"
@@ -13723,13 +13787,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -13797,13 +13861,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -13865,13 +13929,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "slash": {
@@ -13957,13 +14021,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -14047,13 +14111,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -14147,7 +14211,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "emoji-regex": {
@@ -14168,13 +14232,13 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
           "dev": true
         },
         "locate-path": {
@@ -14199,7 +14263,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         },
         "slash": {
@@ -14293,7 +14357,7 @@
     "jest-serializer-vue": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-2.0.2.tgz",
-      "integrity": "sha1-sjjvKGNX7GtIBCG9RxRQUJh9WbM=",
+      "integrity": "sha512-nK/YIFo6qe3i9Ge+hr3h4PpRehuPPGZFt8LDBdTHYldMb7ZWlkanZS8Ls7D8h6qmQP2lBQVDLP0DKn5bJ9QApQ==",
       "dev": true,
       "requires": {
         "pretty": "2.0.0"
@@ -14352,13 +14416,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "mkdirp": {
@@ -14445,13 +14509,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "mkdirp": {
@@ -14526,13 +14590,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -14599,13 +14663,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "string-length": {
@@ -14691,13 +14755,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -14724,7 +14788,7 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -14741,21 +14805,15 @@
     "jju": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
-      "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo="
-    },
-    "js-base64": {
-      "version": "2.6.4",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
-      "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==",
-      "dev": true
+      "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="
     },
     "js-beautify": {
-      "version": "1.14.0",
-      "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.0.tgz",
-      "integrity": "sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==",
+      "version": "1.14.4",
+      "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.4.tgz",
+      "integrity": "sha512-+b4A9c3glceZEmxyIbxDOYB0ZJdReLvyU1077RqKsO4dZx9FUHjTOJn8VHwpg33QoucIykOiYbh7MfqBOghnrA==",
       "dev": true,
       "requires": {
-        "config-chain": "^1.1.12",
+        "config-chain": "^1.1.13",
         "editorconfig": "^0.15.3",
         "glob": "^7.1.3",
         "nopt": "^5.0.0"
@@ -14789,7 +14847,7 @@
     "jsbn": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+      "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
     },
     "jscodeshift": {
       "version": "0.11.0",
@@ -14846,9 +14904,9 @@
           }
         },
         "tslib": {
-          "version": "2.3.1",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-          "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+          "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
           "dev": true
         },
         "write-file-atomic": {
@@ -14907,7 +14965,7 @@
         "tr46": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-          "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+          "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
           "dev": true,
           "requires": {
             "punycode": "^2.1.0"
@@ -14950,7 +15008,7 @@
     "json-buffer": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
-      "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
+      "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ=="
     },
     "json-parse-better-errors": {
       "version": "1.0.2",
@@ -14966,7 +15024,7 @@
     "json-parse-helpfulerror": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz",
-      "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=",
+      "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==",
       "requires": {
         "jju": "^1.1.0"
       }
@@ -14984,13 +15042,21 @@
     "json-stable-stringify-without-jsonify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
       "dev": true
     },
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
+    },
+    "json2mq": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+      "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+      "requires": {
+        "string-convert": "^0.2.0"
+      }
     },
     "json5": {
       "version": "2.2.1",
@@ -15000,7 +15066,7 @@
     "jsonfile": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-      "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
       "dev": true,
       "requires": {
         "graceful-fs": "^4.1.6"
@@ -15009,7 +15075,7 @@
     "jsonparse": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
-      "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="
     },
     "jsprim": {
       "version": "1.4.2",
@@ -15056,9 +15122,9 @@
       }
     },
     "launch-editor": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.3.0.tgz",
-      "integrity": "sha512-3QrsCXejlWYHjBPFXTyGNhPj4rrQdB+5+r5r3wArpLH201aR+nWUgw/zKKkTmilCfY/sv6u8qo98pNvtg8LUTA==",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.4.0.tgz",
+      "integrity": "sha512-mZ0BHeSn/ohL+Ib+b+JnxC59vcNz6v5IR9d0CuM8f0x8ni8oK3IIG6G0vMkpxc0gFsmvINkztGOHiWTaX4BmAg==",
       "dev": true,
       "requires": {
         "picocolors": "^1.0.0",
@@ -15074,12 +15140,12 @@
       }
     },
     "launch-editor-middleware": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.3.0.tgz",
-      "integrity": "sha512-GJR64trLdFFwCoL9DMn/d1SZX0OzTDPixu4mcfWTShQ4tIqCHCGvlg9fOEYQXyBlrSMQwylsJfUWncheShfV2w==",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/launch-editor-middleware/-/launch-editor-middleware-2.4.0.tgz",
+      "integrity": "sha512-/M7AX/6xktZY60KE7j71XLrj9U6H5TBoP+mJzhYB3fcdAq8rcazit/K0qWiu1jvytUPXP4lJRd1VJFwvdMQ/uw==",
       "dev": true,
       "requires": {
-        "launch-editor": "^2.3.0"
+        "launch-editor": "^2.4.0"
       }
     },
     "left-pad": {
@@ -15133,9 +15199,9 @@
       }
     },
     "less-bundle-promise": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/less-bundle-promise/-/less-bundle-promise-1.0.7.tgz",
-      "integrity": "sha512-B4mN+YtkOxAPUHyorhup+ETVNZ9E1PO65sPhgPvDDHDVtR1oYRd87EbYVYOsU0Oev0MW/6MSouS5QYlhe7XrzA=="
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/less-bundle-promise/-/less-bundle-promise-1.0.11.tgz",
+      "integrity": "sha512-LozmEciljdXe0CwEH6uWTlpQDlOVM8d3kkj14P+Jeze/AUhaPZs02x6INJh4TYSeO5xw4RxkpzXTELZSkLKC6Q=="
     },
     "less-loader": {
       "version": "5.0.0",
@@ -15151,7 +15217,7 @@
         "clone": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-          "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+          "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
           "dev": true
         },
         "json5": {
@@ -15191,7 +15257,7 @@
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
       "dev": true,
       "requires": {
         "prelude-ls": "~1.1.2",
@@ -15236,7 +15302,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
         }
       }
     },
@@ -15249,7 +15315,7 @@
     "load-json-file": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
-      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+      "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
       "dev": true,
       "requires": {
         "graceful-fs": "^4.1.2",
@@ -15261,7 +15327,7 @@
         "parse-json": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+          "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
           "dev": true,
           "requires": {
             "error-ex": "^1.3.1",
@@ -15283,7 +15349,7 @@
         "find-cache-dir": {
           "version": "0.1.1",
           "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz",
-          "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
+          "integrity": "sha512-Z9XSBoNE7xQiV6MSgPuCfyMokH2K7JdpRkOYE1+mu3d4BFJtx3GW+f6Bo4q8IX6rlf5MYbLBKW0pjl2cWdkm2A==",
           "dev": true,
           "requires": {
             "commondir": "^1.0.1",
@@ -15294,7 +15360,7 @@
         "find-up": {
           "version": "1.1.2",
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+          "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
           "dev": true,
           "requires": {
             "path-exists": "^2.0.0",
@@ -15313,7 +15379,7 @@
         "path-exists": {
           "version": "2.1.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+          "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
           "dev": true,
           "requires": {
             "pinkie-promise": "^2.0.0"
@@ -15322,7 +15388,7 @@
         "pkg-dir": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
-          "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+          "integrity": "sha512-c6pv3OE78mcZ92ckebVDqg0aWSoKhOTbwCV6qbCWMk546mAL9pZln0+QsN/yQ7fkucd4+yJPLrCBXNt8Ruk+Eg==",
           "dev": true,
           "requires": {
             "find-up": "^1.0.0"
@@ -15367,13 +15433,13 @@
     "lodash.clonedeep": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
-      "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
       "dev": true
     },
     "lodash.debounce": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
       "dev": true
     },
     "lodash.defaultsdeep": {
@@ -15385,19 +15451,19 @@
     "lodash.kebabcase": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
-      "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
       "dev": true
     },
     "lodash.mapvalues": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
-      "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=",
+      "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==",
       "dev": true
     },
     "lodash.memoize": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
+      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
       "dev": true
     },
     "lodash.merge": {
@@ -15409,19 +15475,19 @@
     "lodash.sortby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
-      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+      "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
       "dev": true
     },
     "lodash.transform": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz",
-      "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=",
+      "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==",
       "dev": true
     },
     "lodash.uniq": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
       "dev": true
     },
     "log-symbols": {
@@ -15465,13 +15531,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "supports-color": {
@@ -15505,16 +15571,6 @@
         "js-tokens": "^3.0.0 || ^4.0.0"
       }
     },
-    "loud-rejection": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
-      "dev": true,
-      "requires": {
-        "currently-unhandled": "^0.4.1",
-        "signal-exit": "^3.0.0"
-      }
-    },
     "lowdb": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
@@ -15531,7 +15587,7 @@
     "lower-case": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
-      "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
+      "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==",
       "dev": true
     },
     "lowercase-keys": {
@@ -15611,19 +15667,13 @@
     "map-cache": {
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
-      "dev": true
-    },
-    "map-obj": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+      "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
       "dev": true
     },
     "map-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
       "dev": true,
       "requires": {
         "object-visit": "^1.0.0"
@@ -15659,127 +15709,24 @@
     "media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
       "dev": true
     },
+    "memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
     "memory-fs": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+      "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==",
       "dev": true,
       "requires": {
         "errno": "^0.1.3",
         "readable-stream": "^2.0.1"
       }
     },
-    "meow": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
-      "dev": true,
-      "requires": {
-        "camelcase-keys": "^2.0.0",
-        "decamelize": "^1.1.2",
-        "loud-rejection": "^1.0.0",
-        "map-obj": "^1.0.1",
-        "minimist": "^1.1.3",
-        "normalize-package-data": "^2.3.4",
-        "object-assign": "^4.0.1",
-        "read-pkg-up": "^1.0.1",
-        "redent": "^1.0.0",
-        "trim-newlines": "^1.0.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "load-json-file": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-          "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "parse-json": "^2.2.0",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0",
-            "strip-bom": "^2.0.0"
-          }
-        },
-        "parse-json": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-          "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
-          "dev": true,
-          "requires": {
-            "error-ex": "^1.2.0"
-          }
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "path-type": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-          "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "pify": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        },
-        "read-pkg": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-          "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
-          "dev": true,
-          "requires": {
-            "load-json-file": "^1.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^1.0.0"
-          }
-        },
-        "read-pkg-up": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-          "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
-          "dev": true,
-          "requires": {
-            "find-up": "^1.0.0",
-            "read-pkg": "^1.0.0"
-          }
-        },
-        "strip-bom": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-          "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-          "dev": true,
-          "requires": {
-            "is-utf8": "^0.2.0"
-          }
-        }
-      }
-    },
     "merge": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
@@ -15789,7 +15736,7 @@
     "merge-descriptors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+      "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
       "dev": true
     },
     "merge-source-map": {
@@ -15816,7 +15763,7 @@
     "methods": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
       "dev": true
     },
     "micromatch": {
@@ -15923,7 +15870,7 @@
         "normalize-url": {
           "version": "1.9.1",
           "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
-          "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
+          "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==",
           "dev": true,
           "requires": {
             "object-assign": "^4.0.1",
@@ -15935,13 +15882,13 @@
         "prepend-http": {
           "version": "1.0.4",
           "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-          "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+          "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==",
           "dev": true
         },
         "query-string": {
           "version": "4.3.4",
           "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
-          "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+          "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==",
           "dev": true,
           "requires": {
             "object-assign": "^4.1.0",
@@ -15980,7 +15927,7 @@
     "minimalistic-crypto-utils": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
-      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+      "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
       "dev": true
     },
     "minimatch": {
@@ -15997,9 +15944,9 @@
       "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
     },
     "minipass": {
-      "version": "3.1.6",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
-      "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz",
+      "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==",
       "requires": {
         "yallist": "^4.0.0"
       }
@@ -16124,14 +16071,29 @@
       "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
     },
     "moment": {
-      "version": "2.29.1",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
-      "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+      "version": "2.29.3",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
+      "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
+    },
+    "moment-timezone": {
+      "version": "0.5.43",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz",
+      "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==",
+      "requires": {
+        "moment": "^2.29.4"
+      },
+      "dependencies": {
+        "moment": {
+          "version": "2.29.4",
+          "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+          "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
+        }
+      }
     },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+      "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==",
       "dev": true,
       "requires": {
         "aproba": "^1.1.1",
@@ -16143,12 +16105,12 @@
       },
       "dependencies": {
         "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "minimist": "^1.2.6"
           }
         },
         "rimraf": {
@@ -16180,9 +16142,14 @@
     "multicast-dns-service-types": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
-      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+      "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==",
       "dev": true
     },
+    "mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="
+    },
     "mute-stream": {
       "version": "0.0.8",
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
@@ -16201,15 +16168,16 @@
       }
     },
     "nan": {
-      "version": "2.15.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
-      "dev": true
+      "version": "2.16.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
+      "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
+      "dev": true,
+      "optional": true
     },
     "nanoid": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
-      "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw=="
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
     },
     "nanomatch": {
       "version": "1.2.13",
@@ -16245,13 +16213,13 @@
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
       "dev": true
     },
     "ndjson": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-1.5.0.tgz",
-      "integrity": "sha1-rmA7NrE0vOw0e0UkIrC/mNWDLsg=",
+      "integrity": "sha512-hUPLuaziboGjNF7wHngkgVc0FOclR8dDk/HfEvTtDr/iUrqBWiRcRSTK3/nLOqKH33th714BrMmTPtObI9gZxQ==",
       "dev": true,
       "requires": {
         "json-stringify-safe": "^5.0.1",
@@ -16263,7 +16231,7 @@
     "neat-csv": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/neat-csv/-/neat-csv-2.1.0.tgz",
-      "integrity": "sha1-BvWDYMTDuVW9Rn3cha5FEaOQekw=",
+      "integrity": "sha512-SRzLDeOV/RKF5Em08QWEEbfceBMTl9f+zkqupMqDbEa19ieeQ7UKz42mQBj6zNQaCC4u7uauG4dF8aUOWTnZ8w==",
       "dev": true,
       "requires": {
         "csv-parser": "^1.6.0",
@@ -16274,7 +16242,7 @@
         "get-stream": {
           "version": "2.3.1",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
-          "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
+          "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==",
           "dev": true,
           "requires": {
             "object-assign": "^4.0.1",
@@ -16323,7 +16291,7 @@
     "node-alias": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/node-alias/-/node-alias-1.0.4.tgz",
-      "integrity": "sha1-HxuRa1a56iQcATX5fO1pQPVW8pI=",
+      "integrity": "sha512-9uG48bfkbG9BlKe8QrlxuiPNaKl3wpQn6tJbrojVqgkJuWIO28ifRKrRDrrK+ee72rJ25EaE//PhSIo8E29lLw==",
       "requires": {
         "chalk": "^1.1.1",
         "lodash": "^4.2.0"
@@ -16332,12 +16300,12 @@
         "ansi-styles": {
           "version": "2.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+          "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
         },
         "chalk": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
           "requires": {
             "ansi-styles": "^2.2.1",
             "escape-string-regexp": "^1.0.2",
@@ -16349,7 +16317,7 @@
         "supports-color": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+          "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
         }
       }
     },
@@ -16366,7 +16334,7 @@
         "clone": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-          "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+          "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
           "dev": true
         }
       }
@@ -16374,7 +16342,7 @@
     "node-dir": {
       "version": "0.1.17",
       "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz",
-      "integrity": "sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=",
+      "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==",
       "dev": true,
       "requires": {
         "minimatch": "^3.0.2"
@@ -16415,7 +16383,7 @@
     "node-int64": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
-      "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
       "dev": true
     },
     "node-libs-browser": {
@@ -16463,7 +16431,7 @@
         "punycode": {
           "version": "1.4.1",
           "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
           "dev": true
         }
       }
@@ -16500,9 +16468,9 @@
       }
     },
     "node-releases": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
-      "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
+      "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==",
       "dev": true
     },
     "nopt": {
@@ -16532,12 +16500,12 @@
           "dev": true
         },
         "resolve": {
-          "version": "1.22.0",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-          "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+          "version": "1.22.1",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+          "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
           "dev": true,
           "requires": {
-            "is-core-module": "^2.8.1",
+            "is-core-module": "^2.9.0",
             "path-parse": "^1.0.7",
             "supports-preserve-symlinks-flag": "^1.0.0"
           }
@@ -16559,7 +16527,7 @@
     "normalize-range": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
-      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+      "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
       "dev": true
     },
     "normalize-url": {
@@ -16673,7 +16641,7 @@
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+      "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
       "dev": true,
       "requires": {
         "path-key": "^2.0.0"
@@ -16693,7 +16661,7 @@
     "nprogress": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
-      "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E="
+      "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA=="
     },
     "nth-check": {
       "version": "1.0.2",
@@ -16707,18 +16675,18 @@
     "num2fraction": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
-      "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+      "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==",
       "dev": true
     },
     "number-is-nan": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+      "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="
     },
     "nwsapi": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
-      "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz",
+      "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==",
       "dev": true
     },
     "oauth-sign": {
@@ -16729,12 +16697,12 @@
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
     },
     "object-copy": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
       "dev": true,
       "requires": {
         "copy-descriptor": "^0.1.0",
@@ -16745,7 +16713,7 @@
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^0.1.0"
@@ -16754,7 +16722,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -16769,9 +16737,9 @@
       "dev": true
     },
     "object-inspect": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
-      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+      "version": "1.12.2",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+      "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
       "dev": true
     },
     "object-is": {
@@ -16799,7 +16767,7 @@
     "object-visit": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
       "dev": true,
       "requires": {
         "isobject": "^3.0.0"
@@ -16818,20 +16786,21 @@
       }
     },
     "object.getownpropertydescriptors": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz",
-      "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
+      "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
       "dev": true,
       "requires": {
+        "array.prototype.reduce": "^1.0.4",
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.19.1"
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.20.1"
       }
     },
     "object.pick": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
       "dev": true,
       "requires": {
         "isobject": "^3.0.1"
@@ -16854,15 +16823,10 @@
       "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
       "dev": true
     },
-    "omit.js": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-2.0.2.tgz",
-      "integrity": "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg=="
-    },
     "on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
       "dev": true,
       "requires": {
         "ee-first": "1.1.1"
@@ -16877,7 +16841,7 @@
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
       "requires": {
         "wrappy": "1"
       }
@@ -16885,7 +16849,7 @@
     "onetime": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
-      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==",
       "dev": true,
       "requires": {
         "mimic-fn": "^1.0.0"
@@ -16981,13 +16945,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "strip-ansi": {
@@ -17010,43 +16974,18 @@
         }
       }
     },
-    "original": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
-      "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
-      "dev": true,
-      "requires": {
-        "url-parse": "^1.4.3"
-      }
-    },
     "os-browserify": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
-      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
-      "dev": true
-    },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+      "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
       "dev": true
     },
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+      "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
       "dev": true
     },
-    "osenv": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
-      "dev": true,
-      "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
-      }
-    },
     "p-cancelable": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
@@ -17055,7 +16994,7 @@
     "p-each-series": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
-      "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+      "integrity": "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA==",
       "dev": true,
       "requires": {
         "p-reduce": "^1.0.0"
@@ -17073,13 +17012,13 @@
     "p-finally": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+      "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
       "dev": true
     },
     "p-is-promise": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
-      "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=",
+      "integrity": "sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==",
       "dev": true
     },
     "p-limit": {
@@ -17109,7 +17048,7 @@
     "p-reduce": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
-      "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=",
+      "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==",
       "dev": true
     },
     "p-retry": {
@@ -17199,7 +17138,7 @@
     "param-case": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
-      "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+      "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==",
       "dev": true,
       "requires": {
         "no-case": "^2.2.0"
@@ -17253,7 +17192,7 @@
     "parse-passwd": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+      "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
       "dev": true
     },
     "parse5": {
@@ -17288,7 +17227,7 @@
     "pascalcase": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
       "dev": true
     },
     "path-browserify": {
@@ -17300,7 +17239,7 @@
     "path-dirname": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+      "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
       "dev": true
     },
     "path-exists": {
@@ -17311,18 +17250,18 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
     },
     "path-is-inside": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
       "dev": true
     },
     "path-key": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
       "dev": true
     },
     "path-parse": {
@@ -17333,7 +17272,7 @@
     "path-to-regexp": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+      "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
       "dev": true
     },
     "path-type": {
@@ -17361,13 +17300,13 @@
     "pend": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
-      "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
       "dev": true
     },
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
     },
     "picocolors": {
       "version": "0.2.1",
@@ -17392,7 +17331,7 @@
         "cross-spawn": {
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
           "dev": true,
           "requires": {
             "lru-cache": "^4.0.1",
@@ -17418,7 +17357,7 @@
         "get-stream": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-          "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+          "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==",
           "dev": true
         },
         "lru-cache": {
@@ -17443,7 +17382,7 @@
         "yallist": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
           "dev": true
         }
       }
@@ -17451,19 +17390,19 @@
     "pify": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+      "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
       "dev": true
     },
     "pinkie": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
       "dev": true
     },
     "pinkie-promise": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+      "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
       "dev": true,
       "requires": {
         "pinkie": "^2.0.0"
@@ -17515,7 +17454,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         }
       }
@@ -17569,7 +17508,7 @@
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
       "dev": true
     },
     "postcss": {
@@ -18124,9 +18063,9 @@
       }
     },
     "postcss-selector-parser": {
-      "version": "6.0.9",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz",
-      "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==",
+      "version": "6.0.10",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
       "dev": true,
       "requires": {
         "cssesc": "^3.0.0",
@@ -18172,25 +18111,25 @@
     "prelude-ls": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
       "dev": true
     },
     "prepend-http": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
-      "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
+      "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
     },
     "prettier": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.0.tgz",
-      "integrity": "sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+      "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
       "dev": true,
       "optional": true
     },
     "pretty": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz",
-      "integrity": "sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=",
+      "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==",
       "dev": true,
       "requires": {
         "condense-newlines": "^0.2.1",
@@ -18201,7 +18140,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -18258,15 +18197,15 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         }
       }
     },
     "prismjs": {
-      "version": "1.27.0",
-      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
-      "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz",
+      "integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==",
       "dev": true
     },
     "private": {
@@ -18278,7 +18217,7 @@
     "process": {
       "version": "0.11.10",
       "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
       "dev": true
     },
     "process-exists": {
@@ -18303,7 +18242,7 @@
     "promise-inflight": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
+      "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="
     },
     "promise-retry": {
       "version": "2.0.1",
@@ -18326,7 +18265,7 @@
     "proto-list": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
-      "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
+      "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
       "dev": true
     },
     "proxy-addr": {
@@ -18342,7 +18281,7 @@
     "prr": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+      "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
       "dev": true
     },
     "ps-list": {
@@ -18358,7 +18297,7 @@
     "pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+      "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
       "dev": true
     },
     "psl": {
@@ -18436,7 +18375,7 @@
     "q": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+      "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
       "dev": true
     },
     "qrious": {
@@ -18463,13 +18402,13 @@
     "querystring": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
       "dev": true
     },
     "querystring-es3": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
-      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+      "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
       "dev": true
     },
     "querystringify": {
@@ -18510,17 +18449,36 @@
       "dev": true
     },
     "raw-body": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
-      "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+      "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
       "dev": true,
       "requires": {
         "bytes": "3.1.2",
-        "http-errors": "1.8.1",
+        "http-errors": "2.0.0",
         "iconv-lite": "0.4.24",
         "unpipe": "1.0.0"
       },
       "dependencies": {
+        "depd": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+          "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+          "dev": true
+        },
+        "http-errors": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+          "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+          "dev": true,
+          "requires": {
+            "depd": "2.0.0",
+            "inherits": "2.0.4",
+            "setprototypeof": "1.2.0",
+            "statuses": "2.0.1",
+            "toidentifier": "1.0.1"
+          }
+        },
         "iconv-lite": {
           "version": "0.4.24",
           "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -18529,6 +18487,12 @@
           "requires": {
             "safer-buffer": ">= 2.1.2 < 3"
           }
+        },
+        "statuses": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+          "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+          "dev": true
         }
       }
     },
@@ -18543,6 +18507,53 @@
         "strip-json-comments": "~2.0.1"
       }
     },
+    "rc-align": {
+      "version": "4.0.12",
+      "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.12.tgz",
+      "integrity": "sha512-3DuwSJp8iC/dgHzwreOQl52soj40LchlfUHtgACOUtwGuoFIOVh6n/sCpfqCU8kO5+iz6qR0YKvjgB8iPdE3aQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "dom-align": "^1.7.0",
+        "lodash": "^4.17.21",
+        "rc-util": "^5.3.0",
+        "resize-observer-polyfill": "^1.5.1"
+      }
+    },
+    "rc-cascader": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.6.1.tgz",
+      "integrity": "sha512-+GmN2Z0IybKT45t0Z94jkjmsOHGxAliobR2tzt05/Gw0AKBYLHX5bdvsVXR7abPnarYyYzZ/cWe8CoFgDjAFNw==",
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "array-tree-filter": "^2.1.0",
+        "classnames": "^2.3.1",
+        "rc-select": "~14.1.0",
+        "rc-tree": "~5.6.3",
+        "rc-util": "^5.6.1"
+      }
+    },
+    "rc-checkbox": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.3.2.tgz",
+      "integrity": "sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1"
+      }
+    },
+    "rc-collapse": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.3.1.tgz",
+      "integrity": "sha512-cOJfcSe3R8vocrF8T+PgaHDrgeA1tX+lwfhwSj60NX9QVRidsILIbRNDLD6nAzmcvVC5PWiIRiR4S1OobxdhCg==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-motion": "^2.3.4",
+        "rc-util": "^5.2.1",
+        "shallowequal": "^1.1.0"
+      }
+    },
     "rc-config-loader": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-3.0.0.tgz",
@@ -18554,11 +18565,366 @@
         "require-from-string": "^2.0.2"
       }
     },
+    "rc-dialog": {
+      "version": "8.9.0",
+      "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-8.9.0.tgz",
+      "integrity": "sha512-Cp0tbJnrvPchJfnwIvOMWmJ4yjX3HWFatO6oBFD1jx8QkgsQCR0p8nUWAKdd3seLJhEC39/v56kZaEjwp9muoQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.6",
+        "rc-motion": "^2.3.0",
+        "rc-util": "^5.21.0"
+      }
+    },
+    "rc-drawer": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-4.4.3.tgz",
+      "integrity": "sha512-FYztwRs3uXnFOIf1hLvFxIQP9MiZJA+0w+Os8dfDh/90X7z/HqP/Yg+noLCIeHEbKln1Tqelv8ymCAN24zPcfQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.6",
+        "rc-util": "^5.7.0"
+      }
+    },
+    "rc-dropdown": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.0.1.tgz",
+      "integrity": "sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "classnames": "^2.2.6",
+        "rc-trigger": "^5.3.1",
+        "rc-util": "^5.17.0"
+      }
+    },
+    "rc-field-form": {
+      "version": "1.26.7",
+      "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.26.7.tgz",
+      "integrity": "sha512-CIb7Gw+DG9R+g4HxaDGYHhOjhjQoU2mGU4y+UM2+KQ3uRz9HrrNgTspGvNynn3UamsYcYcaPWZJmiJ6VklkT/w==",
+      "requires": {
+        "@babel/runtime": "^7.18.0",
+        "async-validator": "^4.1.0",
+        "rc-util": "^5.8.0"
+      }
+    },
+    "rc-image": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-5.7.0.tgz",
+      "integrity": "sha512-v6dzSgYfYrH4liKmOZKZZO+x21sJ9KPXNinBfkAoQg2Ihcd5QZ+P/JjB7v60X981XTPGjegy8U17Z8VUX4V36g==",
+      "requires": {
+        "@babel/runtime": "^7.11.2",
+        "classnames": "^2.2.6",
+        "rc-dialog": "~8.9.0",
+        "rc-util": "^5.0.6"
+      }
+    },
+    "rc-input": {
+      "version": "0.0.1-alpha.7",
+      "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-0.0.1-alpha.7.tgz",
+      "integrity": "sha512-eozaqpCYWSY5LBMwlHgC01GArkVEP+XlJ84OMvdkwUnJBSv83Yxa15pZpn7vACAj84uDC4xOA2CoFdbLuqB08Q==",
+      "requires": {
+        "@babel/runtime": "^7.11.1",
+        "classnames": "^2.2.1",
+        "rc-util": "^5.18.1"
+      }
+    },
+    "rc-input-number": {
+      "version": "7.3.4",
+      "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-7.3.4.tgz",
+      "integrity": "sha512-W9uqSzuvJUnz8H8vsVY4kx+yK51SsAxNTwr8SNH4G3XqQNocLVmKIibKFRjocnYX1RDHMND9FFbgj2h7E7nvGA==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.5",
+        "rc-util": "^5.9.8"
+      }
+    },
+    "rc-mentions": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-1.8.0.tgz",
+      "integrity": "sha512-ch7yfMMvx2UXy+EvE4axm0Vp6VlVZ30WLrZtLtV/Eb1ty7rQQRzNzCwAHAMyw6tNKTMs9t9sF68AVjAzQ0rvJw==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.6",
+        "rc-menu": "~9.6.0",
+        "rc-textarea": "^0.3.0",
+        "rc-trigger": "^5.0.4",
+        "rc-util": "^5.0.1"
+      }
+    },
+    "rc-menu": {
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.6.0.tgz",
+      "integrity": "sha512-d26waws42U/rVwW/+rOE2FN9pX6wUc9bDy38vVQYoie6gE85auWIpl5oChGlnW6nE2epnTwUsgWl8ipOPgmnUA==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-motion": "^2.4.3",
+        "rc-overflow": "^1.2.0",
+        "rc-trigger": "^5.1.2",
+        "rc-util": "^5.12.0",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-motion": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.6.0.tgz",
+      "integrity": "sha512-1MDWA9+i174CZ0SIDenSYm2Wb9YbRkrexjZWR0CUFu7D6f23E8Y0KsTgk9NGOLJsGak5ELZK/Y5lOlf5wQdzbw==",
+      "requires": {
+        "@babel/runtime": "^7.11.1",
+        "classnames": "^2.2.1",
+        "rc-util": "^5.21.0"
+      }
+    },
+    "rc-notification": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.6.0.tgz",
+      "integrity": "sha512-xF3MKgIoynzjQAO4lqsoraiFo3UXNYlBfpHs0VWvwF+4pimen9/H1DYLN2mfRWhHovW6gRpla73m2nmyIqAMZQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-motion": "^2.2.0",
+        "rc-util": "^5.20.1"
+      }
+    },
+    "rc-overflow": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.2.6.tgz",
+      "integrity": "sha512-YqbocgzuQxfq2wZy72vdAgrgzzEuM/5d4gF9TBEodCpXPbUeXGrUXNm1J6G1MSkCU2N0ePIgCEu5qD/0Ldi63Q==",
+      "requires": {
+        "@babel/runtime": "^7.11.1",
+        "classnames": "^2.2.1",
+        "rc-resize-observer": "^1.0.0",
+        "rc-util": "^5.19.2"
+      }
+    },
+    "rc-pagination": {
+      "version": "3.1.17",
+      "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-3.1.17.tgz",
+      "integrity": "sha512-/BQ5UxcBnW28vFAcP2hfh+Xg15W0QZn8TWYwdCApchMH1H0CxiaUUcULP8uXcFM1TygcdKWdt3JqsL9cTAfdkQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1"
+      }
+    },
+    "rc-picker": {
+      "version": "2.6.10",
+      "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-2.6.10.tgz",
+      "integrity": "sha512-9wYtw0DFWs9FO92Qh2D76P0iojUr8ZhLOtScUeOit6ks/F+TBLrOC1uze3IOu+u9gbDAjmosNWLKbBzx/Yuv2w==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1",
+        "date-fns": "2.x",
+        "dayjs": "1.x",
+        "moment": "^2.24.0",
+        "rc-trigger": "^5.0.4",
+        "rc-util": "^5.4.0",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-progress": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-3.3.3.tgz",
+      "integrity": "sha512-MDVNVHzGanYtRy2KKraEaWeZLri2ZHWIRyaE1a9MQ2MuJ09m+Wxj5cfcaoaR6z5iRpHpA59YeUxAlpML8N4PJw==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.6",
+        "rc-util": "^5.16.1"
+      }
+    },
+    "rc-rate": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.2.tgz",
+      "integrity": "sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.5",
+        "rc-util": "^5.0.1"
+      }
+    },
+    "rc-resize-observer": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz",
+      "integrity": "sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1",
+        "rc-util": "^5.15.0",
+        "resize-observer-polyfill": "^1.5.1"
+      }
+    },
+    "rc-segmented": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.1.0.tgz",
+      "integrity": "sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw==",
+      "requires": {
+        "@babel/runtime": "^7.11.1",
+        "classnames": "^2.2.1",
+        "rc-motion": "^2.4.4",
+        "rc-util": "^5.17.0"
+      }
+    },
+    "rc-select": {
+      "version": "14.1.8",
+      "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.1.8.tgz",
+      "integrity": "sha512-1kU/7ZCggyR5r5jVEQfAiN6Sq3LGLD2b6FNz5GWel3TOEQZYyDn0o4FAoIRqS6Y5ldWmkFxtd834ilPnG6NV6w==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-motion": "^2.0.1",
+        "rc-overflow": "^1.0.0",
+        "rc-trigger": "^5.0.4",
+        "rc-util": "^5.16.1",
+        "rc-virtual-list": "^3.2.0"
+      }
+    },
+    "rc-slider": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.0.0.tgz",
+      "integrity": "sha512-Bk54UIKWW4wyhHcL8ehAxt+wX+n69dscnHTX6Uv0FMxSke/TGrlkZz1LSIWblCpfE2zr/dwR2Ca8nZGk3U+Tbg==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.5",
+        "rc-tooltip": "^5.0.1",
+        "rc-util": "^5.18.1",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-steps": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-4.1.4.tgz",
+      "integrity": "sha512-qoCqKZWSpkh/b03ASGx1WhpKnuZcRWmvuW+ZUu4mvMdfvFzVxblTwUM+9aBd0mlEUFmt6GW8FXhMpHkK3Uzp3w==",
+      "requires": {
+        "@babel/runtime": "^7.10.2",
+        "classnames": "^2.2.3",
+        "rc-util": "^5.0.1"
+      }
+    },
+    "rc-switch": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-3.2.2.tgz",
+      "integrity": "sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1",
+        "rc-util": "^5.0.1"
+      }
+    },
+    "rc-table": {
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.24.3.tgz",
+      "integrity": "sha512-3Z74jeIpQ2t0i/PQ3iSKXsl1WGT809yzq3KzIDzEB7SIA0zat+rqwI+OnrwfhJVRp7GTjeT7sJyCatXpw1YJdg==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.5",
+        "rc-resize-observer": "^1.1.0",
+        "rc-util": "^5.14.0",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-tabs": {
+      "version": "11.16.0",
+      "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-11.16.0.tgz",
+      "integrity": "sha512-CIDPv3lHaXSHTJevmFP2eHoD3Hq9psfKbOZYf6D4FYPACloNGHpz44y3RGeJgataQ7omFLrGBm3dOBMUki87tA==",
+      "requires": {
+        "@babel/runtime": "^7.11.2",
+        "classnames": "2.x",
+        "rc-dropdown": "~4.0.0",
+        "rc-menu": "~9.6.0",
+        "rc-resize-observer": "^1.0.0",
+        "rc-util": "^5.5.0"
+      }
+    },
+    "rc-textarea": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.3.7.tgz",
+      "integrity": "sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.1",
+        "rc-resize-observer": "^1.0.0",
+        "rc-util": "^5.7.0",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-tooltip": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.1.1.tgz",
+      "integrity": "sha512-alt8eGMJulio6+4/uDm7nvV+rJq9bsfxFDCI0ljPdbuoygUscbsMYb6EQgwib/uqsXQUvzk+S7A59uYHmEgmDA==",
+      "requires": {
+        "@babel/runtime": "^7.11.2",
+        "rc-trigger": "^5.0.0"
+      }
+    },
+    "rc-tree": {
+      "version": "5.6.5",
+      "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.6.5.tgz",
+      "integrity": "sha512-Bnyen46B251APyRZ9D/jYeTnSqbSEvK2AkU5B4vWkNYgUJNPrxO+VMgcDRedP/8N7YcsgdDT9hxqVvNOq7oCAQ==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-motion": "^2.0.1",
+        "rc-util": "^5.16.1",
+        "rc-virtual-list": "^3.4.8"
+      }
+    },
+    "rc-tree-select": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.4.0.tgz",
+      "integrity": "sha512-reRbOqC7Ic/nQocJAJeCl4n6nJUY3NoqiwRXKvhjgZJU7NGr9vIccXEsY+Lghkw5UMpPoxGsIJB0jiAvM18XYA==",
+      "requires": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "2.x",
+        "rc-select": "~14.1.0",
+        "rc-tree": "~5.6.1",
+        "rc-util": "^5.16.1"
+      }
+    },
+    "rc-trigger": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.3.1.tgz",
+      "integrity": "sha512-5gaFbDkYSefZ14j2AdzucXzlWgU2ri5uEjkHvsf1ynRhdJbKxNOnw4PBZ9+FVULNGFiDzzlVF8RJnR9P/xrnKQ==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "classnames": "^2.2.6",
+        "rc-align": "^4.0.0",
+        "rc-motion": "^2.0.0",
+        "rc-util": "^5.19.2"
+      }
+    },
+    "rc-upload": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.4.tgz",
+      "integrity": "sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "classnames": "^2.2.5",
+        "rc-util": "^5.2.0"
+      }
+    },
+    "rc-util": {
+      "version": "5.22.5",
+      "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.22.5.tgz",
+      "integrity": "sha512-awD2TGMGU97OZftT2R3JwrHWjR8k/xIwqjwcivPskciweUdgXE7QsyXkBKVSBHXS+c17AWWMDWuKWsJSheQy8g==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "react-is": "^16.12.0",
+        "shallowequal": "^1.1.0"
+      }
+    },
+    "rc-virtual-list": {
+      "version": "3.4.8",
+      "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz",
+      "integrity": "sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg==",
+      "requires": {
+        "classnames": "^2.2.6",
+        "rc-resize-observer": "^1.0.0",
+        "rc-util": "^5.15.0"
+      }
+    },
     "react-is": {
       "version": "16.13.1",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
-      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
-      "dev": true
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
     "read-package-json-fast": {
       "version": "2.0.3",
@@ -18630,13 +18996,13 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         },
         "read-pkg": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
-          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+          "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
           "dev": true,
           "requires": {
             "load-json-file": "^4.0.0",
@@ -18690,42 +19056,6 @@
         "source-map": "~0.6.1"
       }
     },
-    "redent": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
-      "dev": true,
-      "requires": {
-        "indent-string": "^2.1.0",
-        "strip-indent": "^1.0.1"
-      },
-      "dependencies": {
-        "get-stdin": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-          "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-          "dev": true
-        },
-        "indent-string": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-          "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
-          "dev": true,
-          "requires": {
-            "repeating": "^2.0.0"
-          }
-        },
-        "strip-indent": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
-          "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
-          "dev": true,
-          "requires": {
-            "get-stdin": "^4.0.1"
-          }
-        }
-      }
-    },
     "regenerate": {
       "version": "1.4.2",
       "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -18747,9 +19077,9 @@
       "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
     },
     "regenerator-transform": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
-      "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz",
+      "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==",
       "dev": true,
       "requires": {
         "@babel/runtime": "^7.8.4"
@@ -18766,13 +19096,14 @@
       }
     },
     "regexp.prototype.flags": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
-      "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+      "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.1.3",
+        "functions-have-names": "^1.2.2"
       }
     },
     "regexpp": {
@@ -18782,9 +19113,9 @@
       "dev": true
     },
     "regexpu-core": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz",
-      "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz",
+      "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==",
       "dev": true,
       "requires": {
         "regenerate": "^1.4.2",
@@ -18796,11 +19127,11 @@
       }
     },
     "registry-auth-token": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz",
-      "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==",
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz",
+      "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==",
       "requires": {
-        "rc": "^1.2.8"
+        "rc": "1.2.8"
       }
     },
     "registry-url": {
@@ -18829,7 +19160,7 @@
         "jsesc": {
           "version": "0.5.0",
           "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
-          "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+          "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
           "dev": true
         }
       }
@@ -18837,13 +19168,13 @@
     "relateurl": {
       "version": "0.2.7",
       "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
-      "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+      "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
       "dev": true
     },
     "remove-trailing-separator": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
       "dev": true
     },
     "renderkid": {
@@ -18860,28 +19191,28 @@
       },
       "dependencies": {
         "css-select": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
-          "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+          "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
           "dev": true,
           "requires": {
             "boolbase": "^1.0.0",
-            "css-what": "^5.1.0",
-            "domhandler": "^4.3.0",
+            "css-what": "^6.0.1",
+            "domhandler": "^4.3.1",
             "domutils": "^2.8.0",
             "nth-check": "^2.0.1"
           }
         },
         "css-what": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
-          "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+          "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
           "dev": true
         },
         "dom-serializer": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
-          "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+          "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
           "dev": true,
           "requires": {
             "domelementtype": "^2.0.1",
@@ -18890,9 +19221,9 @@
           }
         },
         "domelementtype": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-          "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+          "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
           "dev": true
         },
         "domutils": {
@@ -18907,9 +19238,9 @@
           }
         },
         "nth-check": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
-          "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+          "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
           "dev": true,
           "requires": {
             "boolbase": "^1.0.0"
@@ -18926,18 +19257,9 @@
     "repeat-string": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
       "dev": true
     },
-    "repeating": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
-      "dev": true,
-      "requires": {
-        "is-finite": "^1.0.0"
-      }
-    },
     "request": {
       "version": "2.88.2",
       "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@@ -18988,7 +19310,7 @@
     "require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
       "dev": true
     },
     "require-from-string": {
@@ -19015,7 +19337,7 @@
     "requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
       "dev": true
     },
     "resize-observer-polyfill": {
@@ -19034,7 +19356,7 @@
     "resolve-cwd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+      "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
       "dev": true,
       "requires": {
         "resolve-from": "^3.0.0"
@@ -19043,19 +19365,19 @@
     "resolve-from": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+      "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
       "dev": true
     },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
       "dev": true
     },
     "responselike": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
-      "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+      "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==",
       "requires": {
         "lowercase-keys": "^1.0.0"
       }
@@ -19063,7 +19385,7 @@
     "restore-cursor": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
-      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==",
       "dev": true,
       "requires": {
         "onetime": "^2.0.0",
@@ -19079,7 +19401,7 @@
     "retry": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-      "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="
     },
     "reusify": {
       "version": "1.0.4",
@@ -19090,13 +19412,13 @@
     "rgb-regex": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
-      "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
+      "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==",
       "dev": true
     },
     "rgba-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
-      "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
+      "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==",
       "dev": true
     },
     "rimraf": {
@@ -19151,7 +19473,7 @@
     "run-queue": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+      "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==",
       "dev": true,
       "requires": {
         "aproba": "^1.1.1"
@@ -19174,7 +19496,7 @@
     "safe-regex": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
       "dev": true,
       "requires": {
         "ret": "~0.1.10"
@@ -19231,7 +19553,7 @@
         "normalize-path": {
           "version": "2.1.1",
           "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
           "dev": true,
           "requires": {
             "remove-trailing-separator": "^1.0.1"
@@ -19240,9 +19562,9 @@
       }
     },
     "sass": {
-      "version": "1.49.9",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
-      "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==",
+      "version": "1.53.0",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.53.0.tgz",
+      "integrity": "sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ==",
       "dev": true,
       "requires": {
         "chokidar": ">=3.0.0 <4.0.0",
@@ -19250,172 +19572,6 @@
         "source-map-js": ">=0.6.2 <2.0.0"
       }
     },
-    "sass-graph": {
-      "version": "2.2.5",
-      "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
-      "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.0.0",
-        "lodash": "^4.0.0",
-        "scss-tokenizer": "^0.2.3",
-        "yargs": "^13.3.2"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-          "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "cliui": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-          "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
-          "dev": true,
-          "requires": {
-            "string-width": "^3.1.0",
-            "strip-ansi": "^5.2.0",
-            "wrap-ansi": "^5.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "7.0.3",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
-          "dev": true
-        },
-        "find-up": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^3.0.0"
-          }
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "locate-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        },
-        "wrap-ansi": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-          "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.0",
-            "string-width": "^3.0.0",
-            "strip-ansi": "^5.0.0"
-          }
-        },
-        "y18n": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-          "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
-          "dev": true
-        },
-        "yargs": {
-          "version": "13.3.2",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
-          "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
-          "dev": true,
-          "requires": {
-            "cliui": "^5.0.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^2.0.1",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^3.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^13.1.2"
-          }
-        },
-        "yargs-parser": {
-          "version": "13.1.2",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
-      }
-    },
     "sass-loader": {
       "version": "8.0.2",
       "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz",
@@ -19491,31 +19647,10 @@
         "compute-scroll-into-view": "^1.0.17"
       }
     },
-    "scss-tokenizer": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-      "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
-      "dev": true,
-      "requires": {
-        "js-base64": "^2.1.8",
-        "source-map": "^0.4.2"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
-          "dev": true,
-          "requires": {
-            "amdefine": ">=0.0.4"
-          }
-        }
-      }
-    },
     "sec": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/sec/-/sec-1.0.0.tgz",
-      "integrity": "sha1-Az1go60g7PLgCUDRT5eCNGV3QzU=",
+      "integrity": "sha512-rVnXtdy9keLxyssT6xung9WwtdB+kha3De93w50RNzwOslyLaDGhqoOqrh4InPoOhhqUZ4JOnovGd3BvLZ+f7A==",
       "dev": true
     },
     "seek-bzip": {
@@ -19538,12 +19673,12 @@
     "select": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
     },
     "select-hose": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
-      "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+      "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
       "dev": true
     },
     "selfsigned": {
@@ -19556,9 +19691,9 @@
       }
     },
     "semver": {
-      "version": "7.3.5",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
-      "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+      "version": "7.3.7",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+      "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
       "requires": {
         "lru-cache": "^6.0.0"
       }
@@ -19584,24 +19719,24 @@
       "integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA=="
     },
     "send": {
-      "version": "0.17.2",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
-      "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
       "dev": true,
       "requires": {
         "debug": "2.6.9",
-        "depd": "~1.1.2",
-        "destroy": "~1.0.4",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
         "encodeurl": "~1.0.2",
         "escape-html": "~1.0.3",
         "etag": "~1.8.1",
         "fresh": "0.5.2",
-        "http-errors": "1.8.1",
+        "http-errors": "2.0.0",
         "mime": "1.6.0",
         "ms": "2.1.3",
-        "on-finished": "~2.3.0",
+        "on-finished": "2.4.1",
         "range-parser": "~1.2.1",
-        "statuses": "~1.5.0"
+        "statuses": "2.0.1"
       },
       "dependencies": {
         "debug": {
@@ -19616,16 +19751,41 @@
             "ms": {
               "version": "2.0.0",
               "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-              "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+              "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
               "dev": true
             }
           }
         },
+        "depd": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+          "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+          "dev": true
+        },
+        "http-errors": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+          "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+          "dev": true,
+          "requires": {
+            "depd": "2.0.0",
+            "inherits": "2.0.4",
+            "setprototypeof": "1.2.0",
+            "statuses": "2.0.1",
+            "toidentifier": "1.0.1"
+          }
+        },
         "ms": {
           "version": "2.1.3",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
           "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
           "dev": true
+        },
+        "statuses": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+          "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+          "dev": true
         }
       }
     },
@@ -19641,7 +19801,7 @@
     "serve-index": {
       "version": "1.9.1",
       "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
-      "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+      "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
       "dev": true,
       "requires": {
         "accepts": "~1.3.4",
@@ -19665,7 +19825,7 @@
         "http-errors": {
           "version": "1.6.3",
           "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-          "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+          "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
           "dev": true,
           "requires": {
             "depd": "~1.1.2",
@@ -19677,13 +19837,13 @@
         "inherits": {
           "version": "2.0.3",
           "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
           "dev": true
         },
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "setprototypeof": {
@@ -19695,21 +19855,21 @@
       }
     },
     "serve-static": {
-      "version": "1.14.2",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
-      "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
       "dev": true,
       "requires": {
         "encodeurl": "~1.0.2",
         "escape-html": "~1.0.3",
         "parseurl": "~1.3.3",
-        "send": "0.17.2"
+        "send": "0.18.0"
       }
     },
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
     },
     "set-value": {
       "version": "2.0.1",
@@ -19726,7 +19886,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -19746,7 +19906,7 @@
     "setimmediate": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
       "dev": true
     },
     "setprototypeof": {
@@ -19779,10 +19939,15 @@
       "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
       "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
     },
+    "shallowequal": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+      "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+    },
     "shebang-command": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+      "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
       "dev": true,
       "requires": {
         "shebang-regex": "^1.0.0"
@@ -19791,7 +19956,7 @@
     "shebang-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
       "dev": true
     },
     "shell-quote": {
@@ -19837,7 +20002,7 @@
     "sigmund": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
-      "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+      "integrity": "sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==",
       "dev": true
     },
     "signal-exit": {
@@ -19848,7 +20013,7 @@
     "simple-swizzle": {
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-      "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+      "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
       "dev": true,
       "requires": {
         "is-arrayish": "^0.3.1"
@@ -19905,13 +20070,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
           "dev": true
         }
       }
@@ -19949,7 +20114,7 @@
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^0.1.0"
@@ -19958,7 +20123,7 @@
         "extend-shallow": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+          "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
           "dev": true,
           "requires": {
             "is-extendable": "^0.1.0"
@@ -19967,13 +20132,13 @@
         "ms": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
           "dev": true
         },
         "source-map": {
           "version": "0.5.7",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
           "dev": true
         }
       }
@@ -19992,7 +20157,7 @@
         "define-property": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^1.0.0"
@@ -20041,7 +20206,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -20069,13 +20234,13 @@
       }
     },
     "sockjs-client": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
-      "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz",
+      "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==",
       "dev": true,
       "requires": {
         "debug": "^3.2.7",
-        "eventsource": "^1.1.0",
+        "eventsource": "^2.0.2",
         "faye-websocket": "^0.11.4",
         "inherits": "^2.0.4",
         "url-parse": "^1.5.10"
@@ -20102,19 +20267,19 @@
       }
     },
     "socks-proxy-agent": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
-      "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz",
+      "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==",
       "requires": {
         "agent-base": "^6.0.2",
-        "debug": "^4.3.1",
-        "socks": "^2.6.1"
+        "debug": "^4.3.3",
+        "socks": "^2.6.2"
       }
     },
     "sort-keys": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
-      "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
+      "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==",
       "dev": true,
       "requires": {
         "is-plain-obj": "^1.0.0"
@@ -20123,7 +20288,7 @@
     "sort-keys-length": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz",
-      "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=",
+      "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==",
       "dev": true,
       "requires": {
         "sort-keys": "^1.0.0"
@@ -20186,7 +20351,7 @@
     "spawn-please": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-0.3.0.tgz",
-      "integrity": "sha1-2zOOxM/2Orxp8dDgjO6euL69nRE="
+      "integrity": "sha512-gf9GJwAWhW0gnQp0dGui+nhIVICx1lGM1Ox95HzfaDBOQTauqlvHFLpo4vtAB3E377SA0YMIyRCh1w0S6R5m2w=="
     },
     "spdx-correct": {
       "version": "3.1.1",
@@ -20281,7 +20446,7 @@
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
     },
     "sshpk": {
       "version": "1.17.0",
@@ -20331,15 +20496,15 @@
       }
     },
     "stackframe": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz",
-      "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==",
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
+      "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
       "dev": true
     },
     "static-extend": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
       "dev": true,
       "requires": {
         "define-property": "^0.2.5",
@@ -20349,7 +20514,7 @@
         "define-property": {
           "version": "0.2.5",
           "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
           "dev": true,
           "requires": {
             "is-descriptor": "^0.1.0"
@@ -20360,28 +20525,19 @@
     "statuses": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
       "dev": true
     },
-    "stdout-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
-      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
-      "dev": true,
-      "requires": {
-        "readable-stream": "^2.0.1"
-      }
-    },
     "stealthy-require": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
-      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+      "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==",
       "dev": true
     },
     "steno": {
       "version": "0.4.4",
       "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz",
-      "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=",
+      "integrity": "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==",
       "dev": true,
       "requires": {
         "graceful-fs": "^4.1.3"
@@ -20429,19 +20585,24 @@
     "streamsearch": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
-      "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=",
+      "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==",
       "dev": true
     },
     "strict-uri-encode": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
-      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+      "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
       "dev": true
     },
+    "string-convert": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+      "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
+    },
     "string-length": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
-      "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=",
+      "integrity": "sha512-Qka42GGrS8Mm3SZ+7cH8UXiIWI867/b/Z/feQSpQx/rbfB8UGknGEZVaUQMOUVj+soY6NpWAxily63HI1OckVQ==",
       "dev": true,
       "requires": {
         "astral-regex": "^1.0.0",
@@ -20449,15 +20610,15 @@
       },
       "dependencies": {
         "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+          "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
           "dev": true
         },
         "strip-ansi": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
           "dev": true,
           "requires": {
             "ansi-regex": "^3.0.0"
@@ -20468,7 +20629,7 @@
     "string-width": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
       "requires": {
         "code-point-at": "^1.0.0",
         "is-fullwidth-code-point": "^1.0.0",
@@ -20476,23 +20637,25 @@
       }
     },
     "string.prototype.trimend": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
-      "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
+      "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5"
       }
     },
     "string.prototype.trimstart": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
-      "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
+      "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
       "dev": true,
       "requires": {
         "call-bind": "^1.0.2",
-        "define-properties": "^1.1.3"
+        "define-properties": "^1.1.4",
+        "es-abstract": "^1.19.5"
       }
     },
     "string_decoder": {
@@ -20506,7 +20669,7 @@
     "strip-ansi": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
       "requires": {
         "ansi-regex": "^2.0.0"
       }
@@ -20514,7 +20677,7 @@
     "strip-bom": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
       "dev": true
     },
     "strip-dirs": {
@@ -20529,7 +20692,7 @@
     "strip-eof": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+      "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
       "dev": true
     },
     "strip-final-newline": {
@@ -20541,13 +20704,13 @@
     "strip-indent": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz",
-      "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=",
+      "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==",
       "dev": true
     },
     "strip-json-comments": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
-      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
     },
     "strip-outer": {
       "version": "1.0.1",
@@ -20612,7 +20775,7 @@
     "svg-tags": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz",
-      "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=",
+      "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
       "dev": true
     },
     "svgo": {
@@ -20668,13 +20831,13 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "mkdirp": {
@@ -20748,7 +20911,7 @@
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
           "dev": true
         },
         "string-width": {
@@ -20928,7 +21091,7 @@
         "pify": {
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
           "dev": true
         }
       }
@@ -21033,12 +21196,12 @@
           }
         },
         "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "minimist": "^1.2.6"
           }
         },
         "rimraf": {
@@ -21109,7 +21272,7 @@
     "text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
     "thenify": {
@@ -21124,7 +21287,7 @@
     "thenify-all": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
-      "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
       "dev": true,
       "requires": {
         "thenify": ">= 3.1.0 < 4"
@@ -21166,13 +21329,13 @@
     "throat": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
-      "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=",
+      "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==",
       "dev": true
     },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
       "dev": true
     },
     "through2": {
@@ -21194,7 +21357,7 @@
     "timed-out": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
-      "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
+      "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==",
       "dev": true
     },
     "timers-browserify": {
@@ -21209,7 +21372,7 @@
     "timsort": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
-      "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
+      "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==",
       "dev": true
     },
     "tiny-emitter": {
@@ -21235,7 +21398,7 @@
     "to-arraybuffer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
-      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+      "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
       "dev": true
     },
     "to-buffer": {
@@ -21247,13 +21410,13 @@
     "to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
       "dev": true
     },
     "to-object-path": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
@@ -21262,7 +21425,7 @@
         "kind-of": {
           "version": "3.2.2",
           "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
           "dev": true,
           "requires": {
             "is-buffer": "^1.1.5"
@@ -21290,13 +21453,18 @@
     "to-regex-range": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
       "dev": true,
       "requires": {
         "is-number": "^3.0.0",
         "repeat-string": "^1.6.1"
       }
     },
+    "toggle-selection": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+      "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+    },
     "toidentifier": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -21306,7 +21474,7 @@
     "toposort": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
-      "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=",
+      "integrity": "sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg==",
       "dev": true
     },
     "tough-cookie": {
@@ -21321,33 +21489,18 @@
     "tr46": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
-      "dev": true
-    },
-    "trim-newlines": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
-      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
       "dev": true
     },
     "trim-repeated": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
-      "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
+      "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
       "dev": true,
       "requires": {
         "escape-string-regexp": "^1.0.2"
       }
     },
-    "true-case-path": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
-      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.2"
-      }
-    },
     "tryer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
@@ -21384,7 +21537,7 @@
         "camelcase": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+          "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==",
           "dev": true
         },
         "mkdirp": {
@@ -21463,13 +21616,13 @@
     "tty-browserify": {
       "version": "0.0.0",
       "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
-      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+      "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==",
       "dev": true
     },
     "tunnel-agent": {
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
       "requires": {
         "safe-buffer": "^5.0.1"
       }
@@ -21477,12 +21630,12 @@
     "tweetnacl": {
       "version": "0.14.5",
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+      "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
     },
     "type-check": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
       "dev": true,
       "requires": {
         "prelude-ls": "~1.1.2"
@@ -21506,7 +21659,7 @@
     "typedarray": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
       "dev": true
     },
     "typedarray-to-buffer": {
@@ -21641,9 +21794,9 @@
           }
         },
         "uglify-js": {
-          "version": "3.15.3",
-          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz",
-          "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==",
+          "version": "3.16.1",
+          "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.1.tgz",
+          "integrity": "sha512-X5BGTIDH8U6IQ1TIRP62YC36k+ULAa1d59BxlWvPUJ1NkW5L3FwcGfEzuVvGmhJFBu0YJ5Ge25tmRISqCmLiRQ==",
           "dev": true
         },
         "webpack-sources": {
@@ -21671,14 +21824,14 @@
       }
     },
     "unbox-primitive": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
-      "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+      "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
       "dev": true,
       "requires": {
-        "function-bind": "^1.1.1",
-        "has-bigints": "^1.0.1",
-        "has-symbols": "^1.0.2",
+        "call-bind": "^1.0.2",
+        "has-bigints": "^1.0.2",
+        "has-symbols": "^1.0.3",
         "which-boxed-primitive": "^1.0.2"
       }
     },
@@ -21735,13 +21888,13 @@
     "uniq": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+      "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
       "dev": true
     },
     "uniqs": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
-      "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
+      "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==",
       "dev": true
     },
     "unique-filename": {
@@ -21777,19 +21930,19 @@
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
       "dev": true
     },
     "unquote": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
-      "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+      "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==",
       "dev": true
     },
     "unset-value": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
       "dev": true,
       "requires": {
         "has-value": "^0.3.1",
@@ -21799,7 +21952,7 @@
         "has-value": {
           "version": "0.3.1",
           "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
           "dev": true,
           "requires": {
             "get-value": "^2.0.3",
@@ -21810,7 +21963,7 @@
             "isobject": {
               "version": "2.1.0",
               "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
               "dev": true,
               "requires": {
                 "isarray": "1.0.0"
@@ -21821,7 +21974,7 @@
         "has-values": {
           "version": "0.1.4",
           "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
           "dev": true
         }
       }
@@ -21832,6 +21985,24 @@
       "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
       "dev": true
     },
+    "update-browserslist-db": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
+      "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
+      "dev": true,
+      "requires": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "dependencies": {
+        "picocolors": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+          "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+          "dev": true
+        }
+      }
+    },
     "update-notifier": {
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz",
@@ -21866,7 +22037,7 @@
     "upper-case": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
-      "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
+      "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==",
       "dev": true
     },
     "uri-js": {
@@ -21880,13 +22051,13 @@
     "urix": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
       "dev": true
     },
     "url": {
       "version": "0.11.0",
       "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
-      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
       "dev": true,
       "requires": {
         "punycode": "1.3.2",
@@ -21896,7 +22067,7 @@
         "punycode": {
           "version": "1.3.2",
           "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+          "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
           "dev": true
         }
       }
@@ -21953,7 +22124,7 @@
     "url-parse-lax": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
-      "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
+      "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==",
       "requires": {
         "prepend-http": "^2.0.0"
       }
@@ -21961,7 +22132,7 @@
     "url-to-options": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
-      "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=",
+      "integrity": "sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==",
       "dev": true
     },
     "use": {
@@ -21982,7 +22153,7 @@
         "inherits": {
           "version": "2.0.3",
           "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
           "dev": true
         }
       }
@@ -21990,7 +22161,7 @@
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "util.promisify": {
       "version": "1.1.1",
@@ -22008,13 +22179,13 @@
     "utila": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
-      "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
+      "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==",
       "dev": true
     },
     "utils-merge": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
-      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
       "dev": true
     },
     "uuid": {
@@ -22041,7 +22212,7 @@
     "validate-npm-package-name": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
-      "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+      "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==",
       "requires": {
         "builtins": "^1.0.3"
       }
@@ -22049,7 +22220,7 @@
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
       "dev": true
     },
     "vendors": {
@@ -22061,7 +22232,7 @@
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
       "requires": {
         "assert-plus": "^1.0.0",
         "core-util-is": "1.0.2",
@@ -22071,7 +22242,7 @@
         "core-util-is": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+          "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
         }
       }
     },
@@ -22082,15 +22253,15 @@
       "dev": true
     },
     "vue": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz",
-      "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
+      "version": "3.2.37",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz",
+      "integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==",
       "requires": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/compiler-sfc": "3.2.31",
-        "@vue/runtime-dom": "3.2.31",
-        "@vue/server-renderer": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-dom": "3.2.37",
+        "@vue/compiler-sfc": "3.2.37",
+        "@vue/runtime-dom": "3.2.37",
+        "@vue/server-renderer": "3.2.37",
+        "@vue/shared": "3.2.37"
       }
     },
     "vue-chartjs": {
@@ -22215,13 +22386,13 @@
           "dev": true
         },
         "micromatch": {
-          "version": "4.0.4",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-          "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
+          "version": "4.0.5",
+          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+          "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
           "dev": true,
           "requires": {
-            "braces": "^3.0.1",
-            "picomatch": "^2.2.3"
+            "braces": "^3.0.2",
+            "picomatch": "^2.3.1"
           }
         },
         "path-type": {
@@ -22286,14 +22457,63 @@
       "dev": true
     },
     "vue-i18n": {
-      "version": "9.1.9",
-      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.1.9.tgz",
-      "integrity": "sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==",
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
+      "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
       "requires": {
-        "@intlify/core-base": "9.1.9",
-        "@intlify/shared": "9.1.9",
-        "@intlify/vue-devtools": "9.1.9",
-        "@vue/devtools-api": "^6.0.0-beta.7"
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      },
+      "dependencies": {
+        "@intlify/core-base": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
+          "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+          "requires": {
+            "@intlify/devtools-if": "9.2.2",
+            "@intlify/message-compiler": "9.2.2",
+            "@intlify/shared": "9.2.2",
+            "@intlify/vue-devtools": "9.2.2"
+          }
+        },
+        "@intlify/devtools-if": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+          "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+          "requires": {
+            "@intlify/shared": "9.2.2"
+          }
+        },
+        "@intlify/message-compiler": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+          "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+          "requires": {
+            "@intlify/shared": "9.2.2",
+            "source-map": "0.6.1"
+          }
+        },
+        "@intlify/shared": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
+          "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
+        },
+        "@intlify/vue-devtools": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+          "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+          "requires": {
+            "@intlify/core-base": "9.2.2",
+            "@intlify/shared": "9.2.2"
+          }
+        },
+        "@vue/devtools-api": {
+          "version": "6.5.0",
+          "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
+          "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
+        }
       }
     },
     "vue-jest": {
@@ -22342,19 +22562,19 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "source-map": {
           "version": "0.5.6",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
-          "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+          "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
           "dev": true
         },
         "supports-color": {
@@ -22394,9 +22614,9 @@
       }
     },
     "vue-router": {
-      "version": "4.0.14",
-      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.14.tgz",
-      "integrity": "sha512-wAO6zF9zxA3u+7AkMPqw9LjoUCjSxfFvINQj3E/DceTt6uEz1XZLraDhdg2EYmvVwTBSGlLYsUw8bDmx0754Mw==",
+      "version": "4.0.16",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.16.tgz",
+      "integrity": "sha512-JcO7cb8QJLBWE+DfxGUL3xUDOae/8nhM1KVdnudadTAORbuxIC/xAydC5Zr/VLHUDQi1ppuTF5/rjBGzgzrJNA==",
       "requires": {
         "@vue/devtools-api": "^6.0.0"
       }
@@ -22414,7 +22634,7 @@
         "hash-sum": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
-          "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
+          "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
           "dev": true
         },
         "json5": {
@@ -22557,7 +22777,7 @@
     "watch": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz",
-      "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=",
+      "integrity": "sha512-1u+Z5n9Jc1E2c7qDO8SinPoZuHj7FgbgU1olSFoyaklduDvvtX7GMMtlE6OC9FTXq4KvNAOfj6Zu4vI1e9bAKA==",
       "dev": true,
       "requires": {
         "exec-sh": "^0.2.0",
@@ -22600,7 +22820,7 @@
             "normalize-path": {
               "version": "2.1.1",
               "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
               "dev": true,
               "optional": true,
               "requires": {
@@ -22651,7 +22871,7 @@
         "is-binary-path": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -22684,7 +22904,7 @@
     "wcwidth": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
-      "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
       "dev": true,
       "requires": {
         "defaults": "^1.0.3"
@@ -22693,7 +22913,7 @@
     "webidl-conversions": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
       "dev": true
     },
     "webpack": {
@@ -22748,12 +22968,12 @@
           }
         },
         "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "version": "0.5.6",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+          "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
           "dev": true,
           "requires": {
-            "minimist": "^1.2.5"
+            "minimist": "^1.2.6"
           }
         },
         "schema-utils": {
@@ -22844,7 +23064,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "commander": {
@@ -22856,7 +23076,7 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "mkdirp": {
@@ -23011,7 +23231,7 @@
             "normalize-path": {
               "version": "2.1.1",
               "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+              "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
               "dev": true,
               "requires": {
                 "remove-trailing-separator": "^1.0.1"
@@ -23079,7 +23299,7 @@
         "color-name": {
           "version": "1.1.3",
           "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+          "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
           "dev": true
         },
         "emoji-regex": {
@@ -23111,7 +23331,7 @@
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
           "dev": true
         },
         "http-proxy-middleware": {
@@ -23135,7 +23355,7 @@
         "is-binary-path": {
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+          "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
           "dev": true,
           "requires": {
             "binary-extensions": "^1.0.0"
@@ -23144,7 +23364,7 @@
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
           "dev": true
         },
         "locate-path": {
@@ -23169,7 +23389,7 @@
         "path-exists": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
           "dev": true
         },
         "readdirp": {
@@ -23372,7 +23592,7 @@
     "whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
       "dev": true,
       "requires": {
         "tr46": "~0.0.3",
@@ -23403,7 +23623,7 @@
     "which-module": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+      "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
       "dev": true
     },
     "wide-align": {
@@ -23515,7 +23735,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
     },
     "write": {
       "version": "1.0.3",
@@ -23549,9 +23769,9 @@
       }
     },
     "ws": {
-      "version": "7.5.7",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
-      "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz",
+      "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==",
       "dev": true
     },
     "xdg-basedir": {
@@ -23588,9 +23808,9 @@
       "dev": true
     },
     "xss": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz",
-      "integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==",
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.13.tgz",
+      "integrity": "sha512-clu7dxTm1e8Mo5fz3n/oW3UCXBfV89xZ72jM8yzo1vR/pIS0w3sgB3XV2H8Vm6zfGnHL0FzvLJPJEBhd86/z4Q==",
       "dev": true,
       "requires": {
         "commander": "^2.20.3",
@@ -23625,7 +23845,7 @@
     "yaml-front-matter": {
       "version": "3.4.1",
       "resolved": "https://registry.npmjs.org/yaml-front-matter/-/yaml-front-matter-3.4.1.tgz",
-      "integrity": "sha1-5S6E/qaYO5N1XpsVZNupibAGtaU=",
+      "integrity": "sha512-/sDeHR8GD6JIJ8j/2h28QsjXS9XsWp2WnjU8RQODri/u6INSEF9Q5w4mZVl0KtXsM1UCBYQhOwTvJKTsnmusBQ==",
       "dev": true,
       "requires": {
         "commander": "1.0.0",
@@ -23635,7 +23855,7 @@
         "commander": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/commander/-/commander-1.0.0.tgz",
-          "integrity": "sha1-XmqI5wcP9ZCINurRkWlUjDD5C80=",
+          "integrity": "sha512-ypAKENwAvjA+utibuxSPeduXV/tIX73+9IyWMkFNnbxiJTeY2xdcM8C2KZo3KEGlDnO5tSm2BVZ65QfuRcR8DQ==",
           "dev": true
         }
       }
@@ -23698,7 +23918,7 @@
     "yauzl": {
       "version": "2.10.0",
       "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
-      "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
       "dev": true,
       "requires": {
         "buffer-crc32": "~0.2.3",
@@ -23726,7 +23946,7 @@
         "cross-spawn": {
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+          "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
           "dev": true,
           "requires": {
             "lru-cache": "^4.0.1",
@@ -23737,7 +23957,7 @@
         "execa": {
           "version": "0.8.0",
           "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz",
-          "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=",
+          "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==",
           "dev": true,
           "requires": {
             "cross-spawn": "^5.0.1",
@@ -23752,7 +23972,7 @@
         "get-stream": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-          "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+          "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==",
           "dev": true
         },
         "is-ci": {
@@ -23777,7 +23997,7 @@
         "normalize-path": {
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz",
-          "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=",
+          "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==",
           "dev": true
         },
         "which": {
@@ -23792,7 +24012,7 @@
         "yallist": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+          "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
           "dev": true
         }
       }
diff --git a/ui/package.json b/ui/package.json
index 55a9c96..1bc174a 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -38,19 +38,24 @@
     "@fortawesome/free-brands-svg-icons": "^5.15.2",
     "@fortawesome/free-solid-svg-icons": "^5.15.2",
     "@fortawesome/vue-fontawesome": "^3.0.0-4",
-    "ant-design-vue": "^2.2.3",
+    "@vue-js-cron/ant": "^1.1.3",
+    "@vue-js-cron/core": "^3.7.1",
+    "ant-design-vue": "^3.2.9",
+    "antd": "^4.21.4",
     "antd-theme-webpack-plugin": "^1.3.9",
     "axios": "^0.21.1",
     "babel-plugin-require-context-hook": "^1.0.0",
     "chart.js": "^3.7.1",
     "chartjs-adapter-moment": "^1.0.0",
     "core-js": "^3.21.1",
+    "cronstrue": "^2.26.0",
     "enquire.js": "^2.1.6",
     "js-cookie": "^2.2.1",
     "lodash": "^4.17.15",
     "md5": "^2.2.1",
     "mitt": "^2.1.0",
     "moment": "^2.26.0",
+    "moment-timezone": "^0.5.43",
     "npm-check-updates": "^6.0.1",
     "nprogress": "^0.2.0",
     "qrious": "^4.0.2",
diff --git a/ui/public/locales/el_GR.json b/ui/public/locales/el_GR.json
index ffe6aaf..a72981d 100644
--- a/ui/public/locales/el_GR.json
+++ b/ui/public/locales/el_GR.json
@@ -1578,6 +1578,8 @@
 "label.shared": "Κοινόχρηστο",
 "label.sharedexecutable": "Κοινόχρηστο",
 "label.sharedmountpoint": "Κοινόχρηστο μέσο",
+"label.sharedrouterip": "Διεύθυνση IPv4 του δρομολογητή στο κοινόχρηστο δίκτυο ",
+"label.sharedrouteripv6": "Διεύθυνση IPv6 του δρομολογητή στο κοινόχρηστο δίκτυο",
 "label.sharewith": "Κοινή χρήση με",
 "label.showing": "Προβολή",
 "label.shrinkok": "Συρρίκνωση OK",
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 64144c5..2f6f335 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -69,6 +69,8 @@
 "label.action.delete.egress.firewall": "Delete egress firewall rule",
 "label.action.delete.firewall": "Delete firewall rule",
 "label.action.delete.interface.static.route": "Remove Tungsten Fabric interface static route",
+"label.action.delete.guest.os": "Delete guest os",
+"label.action.delete.guest.os.hypervisor.mapping": "Delete guest os hypervisor mapping",
 "label.action.delete.ip.range": "Delete IP range",
 "label.action.delete.iso": "Delete ISO",
 "label.action.delete.load.balancer": "Delete load balancer rule",
@@ -123,6 +125,7 @@
 "label.action.expunge.instance": "Expunge instance",
 "label.action.force.reconnect": "Force reconnect",
 "label.action.generate.keys": "Generate keys",
+"label.action.generate.api.secret.keys": "Generate New API/Secret Keys",
 "label.action.get.diagnostics": "Get diagnostics data",
 "label.action.health.monitor": "Health monitor",
 "label.action.image.store.read.only": "Make image store read-only",
@@ -164,6 +167,7 @@
 "label.action.router.health.checks": "Get health checks result",
 "label.action.run.diagnostics": "Run diagnostics",
 "label.action.secure.host": "Provision host security keys",
+"label.action.set.as.source.nat.ip": "make source NAT",
 "label.action.setup.2FA.user.auth": "Setup User Two Factor Authentication",
 "label.action.start.instance": "Start instance",
 "label.action.start.router": "Start router",
@@ -187,6 +191,7 @@
 "label.action.vmsnapshot.revert": "Revert to VM snapshot",
 "label.action.vmstoragesnapshot.create": "Take VM volume snapshot",
 "label.actions": "Actions",
+"label.active": "Active",
 "label.activate.project": "Activate project",
 "label.activeviewersessions": "Active sessions",
 "label.add": "Add",
@@ -210,6 +215,8 @@
 "label.add.firewall": "Add firewall rule",
 "label.add.firewallrule": "Add Firewall Rule",
 "label.add.guest.network": "Add guest network",
+"label.add.guest.os": "Add guest os",
+"label.add.guest.os.hypervisor.mapping": "Add guest os hypervisor mapping",
 "label.add.host": "Add host",
 "label.add.ingress.rule": "Add ingress rule",
 "label.add.intermediate.certificate": "Add intermediate certificate",
@@ -329,6 +336,7 @@
 "label.apply.tungsten.network.policy": "Apply Network Policy",
 "label.apply.tungsten.tag": "Apply tag",
 "label.archive": "Archive",
+"label.archived": "Archived",
 "label.archive.alerts": "Archive alerts",
 "label.archive.events": "Archive events",
 "label.as.default": "as default",
@@ -402,6 +410,7 @@
 "label.bypassvlanoverlapcheck": "Bypass VLAN id/range overlap",
 "label.cachemode": "Write-cache type",
 "label.cancel": "Cancel",
+"label.cancel.shutdown": "Cancel Shutdown",
 "label.cancelmaintenance": "Cancel maintenance",
 "label.capacity": "Capacity",
 "label.capacitybytes": "Capacity bytes",
@@ -438,6 +447,7 @@
 "label.clear.list": "Clear list",
 "label.clear.notification": "Clear notification",
 "label.close": "Close",
+"label.cloud.managed": "CloudManaged",
 "label.cloudian.storage": "Cloudian storage",
 "label.cluster": "Cluster",
 "label.cluster.name": "Cluster name",
@@ -452,6 +462,7 @@
 "label.collectiontime": "Collection time",
 "label.columns": "Columns",
 "label.comma.separated.list.description": "Enter comma-separated list of commands",
+"label.command": "Command",
 "label.comments": "Comments",
 "label.communities": "Communities",
 "label.community": "Community",
@@ -503,6 +514,7 @@
 "label.copied.clipboard": "Copied to clipboard",
 "label.copy": "Copy",
 "label.copy.clipboard": "Copy to clipboard",
+"label.copy.consoleurl": "Copy console URL to clipboard",
 "label.copyid": "Copy ID",
 "label.core": "Core",
 "label.core.zone.type": "Core zone type",
@@ -549,6 +561,7 @@
 "label.creating": "Creating",
 "label.creating.iprange": "Creating IP ranges",
 "label.credit": "Credit",
+"label.cron": "Cron expression",
 "label.crosszones": "Cross zones",
 "label.currency": "Currency",
 "label.current": "Current",
@@ -822,6 +835,7 @@
 "label.expunged": "Expunged",
 "label.expunging": "Expunging",
 "label.export.rules": "Export Rules",
+"label.external.managed": "ExternalManaged",
 "label.external.link": "External link",
 "label.externalid": "External Id",
 "label.externalloadbalanceripaddress": "External load balancer IP address.",
@@ -850,6 +864,8 @@
 "label.for": "for",
 "label.forbidden": "Forbidden",
 "label.forced": "Force",
+"label.force.stop": "Force stop",
+"label.force.reboot": "Force reboot",
 "label.forceencap": "Force UDP encapsulation of ESP packets",
 "label.forgedtransmits": "Forged transmits",
 "label.format": "Format",
@@ -887,6 +903,8 @@
 "label.guest.ip.range": "Guest IP range",
 "label.guest.netmask": "Guest netmask",
 "label.guest.networks": "Guest networks",
+"label.guest.os": "Guest OS",
+"label.guest.os.hypervisor.mappings": "Guest OS mappings",
 "label.guest.start.ip": "Guest start IP",
 "label.guest.traffic": "Guest traffic",
 "label.guestcidraddress": "Guest CIDR",
@@ -909,6 +927,7 @@
 "label.haenable": "HA enabled",
 "label.haprovider": "HA provider",
 "label.hardware": "Hardware",
+"label.hasrules":"FW rules defined",
 "label.hastate": "HA state",
 "label.header.backup.schedule": "You can set up recurring backup schedules by selecting from the available options below and applying your policy preference.",
 "label.header.volume.snapshot": "You can set up recurring snapshot schedules by selecting from the available options below and applying your policy preference.",
@@ -1069,6 +1088,7 @@
 "label.issourcenat": "Source NAT",
 "label.isstaticnat": "Static NAT",
 "label.issystem": "Is system",
+"label.isuserdefined": "User defined",
 "label.isvolatile": "Volatile",
 "label.items": "items",
 "label.items.selected": "item(s) selected",
@@ -1102,6 +1122,7 @@
 "label.kubernetes.version.update": "Manage Kubernetes version",
 "label.kubernetesversionid": "Kubernetes version",
 "label.kubernetesversionname": "Kubernetes version",
+"label.kvm": "KVM",
 "label.kvmnetworklabel": "KVM traffic label",
 "label.l2": "L2",
 "label.l2gatewayserviceuuid": "L2 Gateway Service UUID",
@@ -1165,6 +1186,7 @@
 "label.logout": "Logout",
 "label.lun": "LUN",
 "label.lun.number": "LUN #",
+"label.lxc": "LXC",
 "label.lxcnetworklabel": "LXC traffic label",
 "label.macaddress": "MAC address",
 "label.macaddress.example": "The MAC address. Example: 01:23:45:67:89:ab",
@@ -1334,6 +1356,10 @@
 "label.nfsserver": "NFS server",
 "label.nic": "NIC",
 "label.nicadaptertype": "NIC adapter type",
+"label.nicmultiqueuenumber" : "NIC multiqueue number",
+"label.nicmultiqueuenumber.tooltip" : "NIC multiqueue number. This supports only KVM. The value \"-1\" indicates the NIC multiqueue number will be set to the vCPU number of the instance.",
+"label.nicpackedvirtqueuesenabled" : "NIC packed virtqueues enabled",
+"label.nicpackedvirtqueuesenabled.tooltip" : "Enable NIC packed virtqueues or not. This supports only KVM with QEMU >= 4.2.0 and Libvirt >=6.3.0.",
 "label.nics": "NICs",
 "label.no": "No",
 "label.no.data": "No data to show",
@@ -1374,7 +1400,12 @@
 "label.operator.equal": "Equals to",
 "label.optional": "Optional",
 "label.order": "Order",
-"label.oscategoryid": "OS preference",
+"label.oscategoryid": "OS category",
+"label.oscategoryname": "OS category name",
+"label.osname": "OS name",
+"label.osdisplayname": "OS name",
+"label.osmappingcheckenabled": "Check OS name with hypervisor",
+"label.osnameforhypervisor": "Hypervisor mapping name",
 "label.ostypeid": "OS type",
 "label.osdistribution": "OS distribution",
 "label.ostypename": "OS type",
@@ -1393,6 +1424,7 @@
 "label.overridepublictraffic": "Override public-traffic",
 "label.override.root.diskoffering": "Override root disk offering",
 "label.ovf.properties": "vApp properties",
+"label.ovm3": "OVM3",
 "label.ovm3cluster": "Native Clustering",
 "label.ovm3networklabel": "OVM3 traffic label",
 "label.ovm3pool": "Native pooling",
@@ -1419,6 +1451,7 @@
 "label.pavr": "Virtual router",
 "label.payload": "Payload",
 "label.pcidevice": "GPU",
+"label.pending.jobs": "Pending Jobs",
 "label.per.account": "Per account",
 "label.per.zone": "Per zone",
 "label.percentage": "Percentage",
@@ -1457,6 +1490,7 @@
 "label.preferred": "Preferred",
 "label.prefix": "Prefix",
 "label.prefix.type": "Prefix type",
+"label.prepare.for.shutdown": "Prepare for Shutdown",
 "label.prepareformaintenance": "Prepare for Maintenance",
 "label.presetup": "PreSetup",
 "label.prev": "Prev",
@@ -1658,8 +1692,8 @@
 "label.router.health.check.success": "Success",
 "label.router.health.checks": "Health checks",
 "label.routercount": "Total of virtual routers",
-"label.routerip": "IPv4 address for the VR in this shared network.",
-"label.routeripv6": "IPv6 address for the VR in this shared network.",
+"label.routerip": "IPv4 address for the VR in this network.",
+"label.routeripv6": "IPv6 address for the VR in this network.",
 "label.resourcegroup": "Resource group",
 "label.routing.policy": "Routing policy",
 "label.routing.policy.terms": "Routing policy terms",
@@ -1698,8 +1732,10 @@
 "label.scaleup.policies": "ScaleUp policies",
 "label.scaleup.policy": "ScaleUp policy",
 "label.schedule": "Schedule",
+"label.schedule.add": "Add schedule",
 "label.scheduled.backups": "Scheduled backups",
 "label.scheduled.snapshots": "Scheduled snapshots",
+"label.schedules": "Schedules",
 "label.scope": "Scope",
 "label.search": "Search",
 "label.secondary.isolated.vlan.type.isolated": "Isolated",
@@ -1724,6 +1760,7 @@
 "label.select-view": "Select view",
 "label.select.a.zone": "Select a zone",
 "label.select.deployment.infrastructure": "Select deployment infrastructure",
+"label.select.guest.os.type": "Please select the guest OS type",
 "label.select.network": "Select Network",
 "label.select.period": "Select period",
 "label.select.project": "Select project",
@@ -1765,6 +1802,8 @@
 "label.shared": "Shared",
 "label.sharedexecutable": "Shared",
 "label.sharedmountpoint": "SharedMountPoint",
+"label.sharedrouterip": "IPv4 address for the VR in this shared network.",
+"label.sharedrouteripv6": "IPv6 address for the VR in this shared network.",
 "label.sharewith": "Share with",
 "label.showing": "Showing",
 "label.shrinkok": "Shrink OK",
@@ -1793,6 +1832,7 @@
 "label.sourceipaddress": "Source IP address",
 "label.sourceipaddressnetworkid": "Network ID of source IP address",
 "label.sourcenat": "Source NAT",
+"label.sourcenatipaddress": "Source NAT IP address",
 "label.sourcenatsupported": "Source NAT supported",
 "label.sourcenattype": "Supported source NAT type",
 "label.sourceport": "Source port",
@@ -1844,6 +1884,7 @@
 "label.startport": "Start port",
 "label.startquota": "Quota value",
 "label.state": "State",
+"label.staticnat": "Static NAT",
 "label.static.routes": "Static routes",
 "label.status": "Status",
 "label.step.1": "Step 1",
@@ -1976,6 +2017,7 @@
 "label.traffic.types": "Traffic types",
 "label.traffictype": "Traffic type",
 "label.transportzoneuuid": "Transport zone UUID",
+"label.trigger.shutdown": "Trigger Safe Shutdown",
 "label.try.again": "Try again",
 "label.tuesday": "Tuesday",
 "label.two.factor.authentication.secret.key": "Your Two factor authentication secret key",
@@ -2019,6 +2061,7 @@
 "label.unit": "Usage unit",
 "label.unknown": "Unknown",
 "label.unlimited": "Unlimited",
+"label.unmanaged": "Unmanaged",
 "label.unmanage.instance": "Unmanage instance",
 "label.unmanaged.instance": "Unmanaged instance",
 "label.unmanaged.instances": "Unmanaged instances",
@@ -2117,8 +2160,10 @@
 "label.vmlimit": "Instance limits",
 "label.vmname": "VM name",
 "label.vms": "VMs",
+"label.vmscheduleactions": "Actions",
 "label.vmstate": "VM state",
 "label.vmtotal": "Total of VMs",
+"label.vmware": "VMware",
 "label.vmware.storage.policy": "VMWare storage policy",
 "label.vmwaredcid": "VMware datacenter ID",
 "label.vmwaredcname": "VMware datacenter name",
@@ -2181,6 +2226,7 @@
 "label.writeio": "Write (IO)",
 "label.writethrough": "Write-through",
 "label.xennetworklabel": "XenServer Traffic Label",
+"label.xenserver": "XenServer",
 "label.xenservertoolsversion61plus": "Original XS Version is 6.1+",
 "label.yes": "Yes",
 "label.yourinstance": "Your instance",
@@ -2209,6 +2255,8 @@
 "message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.",
 "message.action.delete.external.load.balancer": "Please confirm that you would like to remove this external load balancer. Warning: If you are planning to add back the same external load balancer, you must reset usage data on the device.",
 "message.action.delete.ingress.rule": "Please confirm that you want to delete this ingress rule.",
+"message.action.delete.guest.os": "Please confirm that you want to delete this guest os. System defined entry cannot be deleted.",
+"message.action.delete.guest.os.hypervisor.mapping": "Please confirm that you want to delete this guest os hypervisor mapping. System defined entry cannot be deleted.",
 "message.action.delete.instance.group": "Please confirm that you want to delete the instance group.",
 "message.action.delete.interface.static.route": "Please confirm that you want to remove this interface Static Route?",
 "message.action.delete.iso": "Please confirm that you want to delete this ISO.",
@@ -2356,6 +2404,7 @@
 "message.backup.create": "Are you sure you want create a VM backup?",
 "message.backup.offering.remove": "Are you sure you want to remove VM from backup offering and delete the backup chain?",
 "message.backup.restore": "Please confirm that you want to restore the vm backup?",
+"message.cancel.shutdown": "Please confirm that you would like to cancel the  shutdown on this Management server. It will resume accepting any new Async Jobs.",
 "message.certificate.upload.processing": "Certificate upload in progress",
 "message.change.offering.confirm": "Please confirm that you wish to change the service offering of this virtual instance.",
 "message.change.offering.for.volume": "Successfully changed offering for the volume",
@@ -2419,6 +2468,7 @@
 "message.confirm.scale.up.system.vm": "Do you really want to scale up the system VM?",
 "message.confirm.start.lb.vm": "Please confirm you want to start LB VM.",
 "message.confirm.sync.storage": "Please confirm you want to sync the storage pool",
+"message.confirm.type": "To confirm, please type",
 "message.confirm.upgrade.router.newer.template": "Please confirm that you want to upgrade router to use newer template.",
 "message.cpu.usage.info": "The CPU usage percentage can exceed 100% if the VM has more than 1 vCPU or when CPU Cap is not enabled. This behavior happens according to the hypervisor being used (e.g: in KVM), due to how they account the stats",
 "message.create.compute.offering": "Compute offering created",
@@ -2553,6 +2603,7 @@
 "message.error.cluster.description": "Please enter Kubernetes cluster description.",
 "message.error.cluster.name": "Please enter cluster name.",
 "message.error.confirm.password": "Please confirm new password.",
+"message.error.confirm.text": "Please enter the confirmation text",
 "message.error.current.password": "Please enter current password.",
 "message.error.custom.disk.size": "Please enter custom disk size.",
 "message.error.date": "Please select a date.",
@@ -2618,6 +2669,7 @@
 "message.error.remove.nic": "There was an error",
 "message.error.remove.secondary.ipaddress": "There was an error removing the secondary IP Address",
 "message.error.remove.tungsten.routing.policy": "Removing Tungsten-Fabric Routing Policy from network failed",
+"message.error.remove.vm.schedule": "Removing Instance Schedule failed",
 "message.error.required.input": "Please enter input",
 "message.error.reset.config": "Unable to reset config to default value",
 "message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes cluster config",
@@ -2683,7 +2735,7 @@
 "message.failed.to.add": "Failed to add",
 "message.failed.to.assign.vms": "Failed to assign VMs",
 "message.failed.to.remove": "Failed to remove",
-"message.generate.keys": "Please confirm that you would like to generate new keys for this user.",
+"message.generate.keys": "Please confirm that you would like to generate new API/Secret keys for this user.",
 "message.chart.statistic.info": "The shown charts are self-adjustable, that means, if the value gets close to the limit or overpass it, it will grow to adjust the shown value",
 "message.guest.traffic.in.advanced.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of VLAN IDs or VXLAN network identifiers (VNIs) to carry guest traffic for each physical network.",
 "message.guest.traffic.in.basic.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range.",
@@ -2735,6 +2787,7 @@
 "message.linstor.resourcegroup.description": "Linstor resource group to use for primary storage.",
 "message.listnsp.not.return.providerid": "error: listNetworkServiceProviders API doesn't return VirtualRouter provider ID.",
 "message.load.host.failed": "Failed to load hosts.",
+"message.loadbalancer.stickypolicy.configuration": "Customize the load balancer stickiness policy:",
 "message.loading.add.interface.static.route": "Adding interface Static Route...",
 "message.loading.add.network.static.route": "Adding network Static Route...",
 "message.loading.add.policy.rule": "Adding Policy rule...",
@@ -2778,6 +2831,7 @@
 "message.network.offering.promiscuous.mode": "Applicable for guest networks on VMware hypervisor only.\nReject - The switch drops any outbound frame from a virtual machine adapter with a source MAC address that is different from the one in the .vmx configuration file.\nAccept - The switch does not perform filtering, and permits all outbound frames.\nNone - Default to value from global setting.",
 "message.network.removenic": "Please confirm that want to remove this NIC, which will also remove the associated network from the VM.",
 "message.network.secondaryip": "Please confirm that you would like to acquire a new secondary IP for this NIC. \n NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.",
+"message.network.selection": "Choose one or more networks to attach the instance to. A new network can also be created here.",
 "message.network.updateip": "Please confirm that you would like to change the IP address for this NIC on VM.",
 "message.network.usage.info.data.points": "Each data point represents the difference in data traffic since the last data point.",
 "message.network.usage.info.sum.of.vnics": "The network usage shown is made up of the sum of data traffic from all the vNICs in the VM.",
@@ -2795,6 +2849,7 @@
 "message.please.wait.while.zone.is.being.created": "Please wait while your zone is being created; this may take a while...",
 "message.pod.dedicated": "Pod dedicated.",
 "message.pod.dedication.released": "Pod dedication released.",
+"message.prepare.for.shutdown": "Please confirm that you would like to prep this Management server for shutdown. It will not accept any new Async Jobs but will NOT terminate after there are no pending jobs.",
 "message.primary.storage.invalid.state": "Primary storage is not in Up state",
 "message.processing.complete": "Processing complete!",
 "message.protocol.description": "For XenServer, choose NFS, iSCSI, or PreSetup. For KVM, choose NFS, SharedMountPoint, RDB, CLVM or Gluster. For vSphere, choose NFS, PreSetup (VMFS or iSCSI or FiberChannel or vSAN or vVols) or DatastoreCluster. For Hyper-V, choose SMB/CIFS. For LXC, choose NFS or SharedMountPoint. For OVM, choose NFS or OCFS2.",
@@ -2870,6 +2925,9 @@
 "message.setup.physical.network.during.zone.creation": "When adding a zone, you need to set up one or more physical networks. Each network corresponds to a NIC on the hypervisor. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.",
 "message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical network, which corresponds to a NIC on the hypervisor. The network carries several types of traffic.<br/><br/>You may also <strong>add</strong> other traffic types onto the physical network.",
 "message.shared.network.offering.warning": "Domain admins and regular users can only create shared networks from network offering with the setting specifyvlan=false. Please contact an administrator to create a network offering if this list is empty.",
+"message.shutdown.triggered": "A shutdown has been triggered. CloudStack will not accept new jobs",
+"message.sourcenatip.change.warning": "WARNING: Changing the sourcenat IP address of the network will cause connectivity downtime for the VMs with NICs in the network.",
+"message.sourcenatip.change.inhibited": "Changing the sourcenat to this IP of the network to this address is inhibited as firewall rules are defined for it. This can include port forwarding or load balancing rules.\n - If this is an isolated network, please use updateNetwork/click the edit button.\n - If this is a VPC, first clear all other rules for this address.",
 "message.specify.tag.key": "Please specify a tag key.",
 "message.specify.tag.value": "Please specify a tag value.",
 "message.step.2.continue": "Please select a service offering to continue.",
@@ -2914,6 +2972,7 @@
 "message.success.config.backup.schedule": "Successfully configured VM backup schedule",
 "message.success.config.health.monitor": "Successfully Configure Health Monitor",
 "message.success.config.sticky.policy": "Successfully configured sticky policy",
+"message.success.config.vm.schedule": "Successfully configured instance schedule",
 "message.success.copy.clipboard": "Successfully copied to clipboard",
 "message.success.create.account": "Successfully created account",
 "message.success.create.internallb": "Successfully created Internal Load Balancer",
@@ -2995,6 +3054,7 @@
 "message.template.type.change.warning": "WARNING: Changing the template type to SYSTEM will disable further changes to the template.",
 "message.tooltip.reserved.system.netmask": "The network prefix that defines the pod subnet. Uses CIDR notation.",
 "message.traffic.type.to.basic.zone": "traffic type to basic zone",
+"message.trigger.shutdown": "Please confirm that you would like to trigger a shutdown on this Management server. It will not accept any new Async Jobs and will terminate after there are no pending jobs.",
 "message.type.values.to.add": "Please add additonal values by typing them in",
 "message.update.autoscale.policy.failed": "Failed to update autoscale policy",
 "message.update.autoscale.vmgroup.failed": "Failed to update autoscale vm group",
diff --git a/ui/public/locales/ja_JP.json b/ui/public/locales/ja_JP.json
index 19938b9..f151828 100644
--- a/ui/public/locales/ja_JP.json
+++ b/ui/public/locales/ja_JP.json
@@ -2046,6 +2046,8 @@
   "label.shared": "共有",
   "label.sharedexecutable": "共有",
   "label.sharedmountpoint": "SharedMountPoint",
+  "label.sharedrouterip": "共有ネットワークのルーターのIPv4アドレス",
+  "label.sharedrouteripv6": "共有ネットワークのルーターのIPv6アドレス",
   "label.sharewith": "共有",
   "label.show.ingress.rule": "受信ルールの表示",
   "label.showing": "表示中",
diff --git a/ui/public/locales/ko_KR.json b/ui/public/locales/ko_KR.json
index 68c6fe9..ef7b4f4 100644
--- a/ui/public/locales/ko_KR.json
+++ b/ui/public/locales/ko_KR.json
@@ -1373,6 +1373,8 @@
 "label.shared": "shared",
 "label.sharedexecutable": "\uacf5\uc720",
 "label.sharedmountpoint": "\uacf5\uc720 \ub9c8\uc6b4\ud2b8 \ud3ec\uc778\ud2b8",
+"label.sharedrouterip": "\uc11c\ube44\uc2a4\uc6a9 \ub124\ud2b8\uc6cc\ud06c\uc758 \ub77c\uc6b0\ud130\uc5d0 \ub300\ud55c IPv4 \uc8fc\uc18c",
+"label.sharedrouteripv6": "\uc11c\ube44\uc2a4\uc6a9 \ub124\ud2b8\uc6cc\ud06c\uc758 \ub77c\uc6b0\ud130\uc5d0 \ub300\ud55c IPv6 \uc8fc\uc18c",
 "label.sharewith": "\uacf5\uc720",
 "label.showing": "\ubcf4\uae30",
 "label.shrinkok": "\ubcc0\uacbd \uc644\ub8cc",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index 712d9f1..40256c8 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -1468,6 +1468,8 @@
 "label.shared": "Compatilhado",
 "label.sharedexecutable": "Compatilhado",
 "label.sharedmountpoint": "SharedMountPoint",
+"label.sharedrouterip": "Endere\u00e7os IPv4 para o roteador dentro da rede compartilhada",
+"label.sharedrouteripv6": "Endere\u00e7os IPv6 para o roteador dentro da rede compartilhada",
 "label.sharewith": "Compartilhar com",
 "label.showing": "Exibindo",
 "label.shrinkok": "Encolhimento OK",
diff --git a/ui/public/locales/zh_CN.json b/ui/public/locales/zh_CN.json
index 7ba77fd..49fe783 100644
--- a/ui/public/locales/zh_CN.json
+++ b/ui/public/locales/zh_CN.json
@@ -2333,6 +2333,8 @@
   "label.shared": "\u5DF2\u5171\u4EAB",
   "label.sharedexecutable": "\u5DF2\u5171\u4EAB",
   "label.sharedmountpoint": "\u5171\u4EAB\u6302\u8F7D\u70B9",
+  "label.sharedrouterip": "\u5171\u4EAB\u7F51\u7EDC\u4E2D\u8DEF\u7531\u5668\u7684 IPv4 \u5730\u5740",
+  "label.sharedrouteripv6": "\u5171\u4EAB\u7F51\u7EDC\u4E2D\u8DEF\u7531\u5668\u7684 IPv6 \u5730\u5740",
   "label.sharewith": "\u5206\u4EAB",
 
   "label.show.ingress.rule": "\u663E\u793A\u5165\u53E3\u89C4\u5219",
diff --git a/ui/src/components/CheckBoxSelectPair.vue b/ui/src/components/CheckBoxSelectPair.vue
index 19dd8bd..de6aed4 100644
--- a/ui/src/components/CheckBoxSelectPair.vue
+++ b/ui/src/components/CheckBoxSelectPair.vue
@@ -34,13 +34,14 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             @change="val => { handleSelectChange(val) }">
             <a-select-option
               v-for="(opt) in selectSource"
               :key="opt.id"
-              :disabled="opt.enabled === false">
+              :disabled="opt.enabled === false"
+              :label="opt.displaytext || opt.name || opt.description">
               {{ opt.displaytext || opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/components/header/ProjectMenu.vue b/ui/src/components/header/ProjectMenu.vue
index 722bc28..11b1a79 100644
--- a/ui/src/components/header/ProjectMenu.vue
+++ b/ui/src/components/header/ProjectMenu.vue
@@ -27,18 +27,6 @@
       @focus="fetchData"
       showSearch>
 
-      <template #suffixIcon>
-        <a-tooltip placement="bottom">
-          <template #title>
-            <span>{{ $t('label.projects') }}</span>
-          </template>
-          <span class="custom-suffix-icon">
-            <ProjectOutlined v-if="!loading" class="ant-select-suffix" />
-            <LoadingOutlined v-else />
-          </span>
-        </a-tooltip>
-      </template>
-
       <a-select-option
         v-for="(project, index) in projects"
         :key="index"
diff --git a/ui/src/components/header/SamlDomainSwitcher.vue b/ui/src/components/header/SamlDomainSwitcher.vue
index fbb7557..1d820dc 100644
--- a/ui/src/components/header/SamlDomainSwitcher.vue
+++ b/ui/src/components/header/SamlDomainSwitcher.vue
@@ -25,7 +25,7 @@
       showSearch
       optionFilterProp="label"
       :filterOption="(input, option) => {
-        return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+        return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
       }"
       @change="changeAccount"
       @focus="fetchData" >
@@ -42,7 +42,7 @@
         </a-tooltip>
       </template>
 
-      <a-select-option v-for="(account, index) in samlAccounts" :key="index">
+      <a-select-option v-for="(account, index) in samlAccounts" :key="index" :label="`${account.accountName} (${account.domainName})`">
         {{ `${account.accountName} (${account.domainName})` }}
       </a-select-option>
     </a-select>
diff --git a/ui/src/components/header/UserMenu.vue b/ui/src/components/header/UserMenu.vue
index d2b7787..42df044 100644
--- a/ui/src/components/header/UserMenu.vue
+++ b/ui/src/components/header/UserMenu.vue
@@ -29,42 +29,34 @@
         <span v-if="image">
           <resource-icon :image="image" size="2x" style="margin-right: 5px"/>
         </span>
-        <a-avatar v-else-if="userInitials" class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: '#1890ff', color: 'white' }">
+        <a-avatar v-else-if="userInitials" class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: $config.theme['@primary-color'], color: 'white' }">
           {{ userInitials }}
         </a-avatar>
-        <a-avatar v-else class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: '#1890ff', color: 'white' }">
+        <a-avatar v-else class="user-menu-avatar avatar" size="small" :style="{ backgroundColor: $config.theme['@primary-color'], color: 'white' }">
           <template #icon><user-outlined /></template>
         </a-avatar>
         <span>{{ nickname() }}</span>
       </span>
       <template #overlay>
-        <a-menu class="user-menu-wrapper">
-          <router-link :to="{ path: '/accountuser/' + $store.getters.userInfo.id }">
-            <a-menu-item class="user-menu-item" key="0">
-                <UserOutlined class="user-menu-item-icon" />
-                <span class="user-menu-item-name">{{ $t('label.profilename') }}</span>
-            </a-menu-item>
-          </router-link>
-          <a @click="toggleUseBrowserTimezone">
-            <a-menu-item class="user-menu-item" key="1">
-                <ClockCircleOutlined class="user-menu-item-icon" />
-                <span class="user-menu-item-name" style="margin-right: 5px">{{ $t('label.use.local.timezone') }}</span>
-                <a-switch :checked="$store.getters.usebrowsertimezone" />
-            </a-menu-item>
-          </a>
-          <a :href="$config.docBase" target="_blank">
-            <a-menu-item class="user-menu-item" key="2">
-              <QuestionCircleOutlined class="user-menu-item-icon" />
-              <span class="user-menu-item-name">{{ $t('label.help') }}</span>
-            </a-menu-item>
-          </a>
+        <a-menu class="user-menu-wrapper" @click="handleClickMenu">
+          <a-menu-item class="user-menu-item" key="profile">
+            <UserOutlined class="user-menu-item-icon" />
+            <span class="user-menu-item-name">{{ $t('label.profilename') }}</span>
+          </a-menu-item>
+          <a-menu-item class="user-menu-item" key="timezone">
+            <ClockCircleOutlined class="user-menu-item-icon" />
+            <span class="user-menu-item-name" style="margin-right: 5px">{{ $t('label.use.local.timezone') }}</span>
+            <a-switch :checked="$store.getters.usebrowsertimezone" />
+          </a-menu-item>
+          <a-menu-item class="user-menu-item" key="document">
+            <QuestionCircleOutlined class="user-menu-item-icon" />
+            <span class="user-menu-item-name">{{ $t('label.help') }}</span>
+          </a-menu-item>
           <a-menu-divider/>
-          <a href="javascript:;" @click="handleLogout">
-            <a-menu-item class="user-menu-item" key="3">
-              <LogoutOutlined class="user-menu-item-icon" />
-              <span class="user-menu-item-name">{{ $t('label.logout') }}</span>
-            </a-menu-item>
-          </a>
+          <a-menu-item class="user-menu-item" key="logout">
+            <LogoutOutlined class="user-menu-item-icon" />
+            <span class="user-menu-item-name">{{ $t('label.logout') }}</span>
+          </a-menu-item>
         </a-menu>
       </template>
     </a-dropdown>
@@ -145,6 +137,22 @@
         })
       })
     },
+    handleClickMenu (item) {
+      switch (item.key) {
+        case 'profile':
+          this.$router.push(`/accountuser/${this.$store.getters.userInfo.id}`)
+          break
+        case 'timezone':
+          this.toggleUseBrowserTimezone()
+          break
+        case 'document':
+          window.open(this.$config.docBase, '_blank')
+          break
+        case 'logout':
+          this.handleLogout()
+          break
+      }
+    },
     handleLogout () {
       return this.Logout({}).then(() => {
         this.$router.push('/user/login')
diff --git a/ui/src/components/page/GlobalLayout.vue b/ui/src/components/page/GlobalLayout.vue
index 3f8779d..d807525 100644
--- a/ui/src/components/page/GlobalLayout.vue
+++ b/ui/src/components/page/GlobalLayout.vue
@@ -16,98 +16,105 @@
 // under the License.
 
 <template>
-  <a-layout class="layout" :class="[device]">
-    <a-affix style="z-index: 200">
-      <template v-if="isSideMenu()">
-        <a-drawer
-          v-if="isMobile()"
-          :wrapClassName="'drawer-sider ' + navTheme"
-          :closable="false"
-          :visible="collapsed"
-          placement="left"
-          @close="() => this.collapsed = false"
-        >
-          <side-menu
-            :menus="menus"
-            :theme="navTheme"
-            :collapsed="false"
-            :collapsible="true"
-            mode="inline"
-            @menuSelect="menuSelect"></side-menu>
-        </a-drawer>
-
-        <side-menu
-          v-else
-          mode="inline"
-          :menus="menus"
-          :theme="navTheme"
-          :collapsed="collapsed"
-          :collapsible="true"></side-menu>
-      </template>
-      <template v-else>
-        <a-drawer
-          v-if="isMobile()"
-          :wrapClassName="'drawer-sider ' + navTheme"
-          placement="left"
-          @close="() => this.collapsed = false"
-          :closable="false"
-          :visible="collapsed"
-        >
-          <side-menu
-            :menus="menus"
-            :theme="navTheme"
-            :collapsed="false"
-            :collapsible="true"
-            mode="inline"
-            @menuSelect="menuSelect"></side-menu>
-        </a-drawer>
-      </template>
-
-      <drawer :visible="showSetting" placement="right" v-if="isAdmin && (isDevelopmentMode || allowSettingTheme)">
-        <template #handler>
-          <a-button type="primary" size="large">
-            <close-outlined v-if="showSetting" />
-            <setting-outlined v-else />
-          </a-button>
-        </template>
-        <template #drawer>
-          <setting :visible="showSetting" />
-        </template>
-      </drawer>
-
+  <div>
+    <a-affix v-if="this.$store.getters.shutdownTriggered" >
+      <a-alert :message="$t('message.shutdown.triggered')" type="error" banner :showIcon="false" class="shutdownHeader" />
     </a-affix>
+    <a-layout class="layout" :class="[device]">
+      <a-affix style="z-index: 200" :offsetTop="this.$store.getters.shutdownTriggered ? 25 : 0">
+        <template v-if="isSideMenu()">
+          <a-drawer
+            v-if="isMobile()"
+            :wrapClassName="'drawer-sider ' + navTheme"
+            :closable="false"
+            :visible="collapsed"
+            placement="left"
+            @close="() => this.collapsed = false"
+          >
+            <side-menu
+              :menus="menus"
+              :theme="navTheme"
+              :collapsed="false"
+              :collapsible="true"
+              mode="inline"
+              @menuSelect="menuSelect"></side-menu>
+          </a-drawer>
+          <side-menu
+            v-else
+            mode="inline"
+            :menus="menus"
+            :theme="navTheme"
+            :collapsed="collapsed"
+            :collapsible="true"></side-menu>
+        </template>
+        <template v-else>
+          <a-drawer
+            v-if="isMobile()"
+            :wrapClassName="'drawer-sider ' + navTheme"
+            placement="left"
+            @close="() => this.collapsed = false"
+            :closable="false"
+            :visible="collapsed"
+          >
+            <side-menu
+              :menus="menus"
+              :theme="navTheme"
+              :collapsed="false"
+              :collapsible="true"
+              mode="inline"
+              @menuSelect="menuSelect"></side-menu>
+          </a-drawer>
+        </template>
 
-    <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
-      <!-- layout header -->
-      <a-affix style="z-index: 100">
-        <global-header
-          :mode="layoutMode"
-          :menus="menus"
-          :theme="navTheme"
-          :collapsed="collapsed"
-          :device="device"
-          @toggle="toggle"
-        />
+        <drawer :visible="showSetting" placement="right" v-if="isAdmin && (isDevelopmentMode || allowSettingTheme)">
+          <template #handler>
+            <a-button type="primary" size="large">
+              <close-outlined v-if="showSetting" />
+              <setting-outlined v-else />
+            </a-button>
+          </template>
+          <template #drawer>
+            <setting :visible="showSetting" />
+          </template>
+        </drawer>
+
       </a-affix>
 
-      <a-button
-        v-if="showClear"
-        type="default"
-        size="small"
-        class="button-clear-notification"
-        @click="onClearNotification">{{ $t('label.clear.notification') }}</a-button>
+      <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }">
+        <!-- layout header -->
+        <a-affix style="z-index: 100">
+          <global-header
+            :style="this.$store.getters.shutdownTriggered ? 'margin-top: 25px;' : null"
+            :mode="layoutMode"
+            :menus="menus"
+            :theme="navTheme"
+            :collapsed="collapsed"
+            :device="device"
+            @toggle="toggle"
+          />
+        </a-affix>
 
-      <!-- layout content -->
-      <a-layout-content class="layout-content" :class="{'is-header-fixed': fixedHeader}">
-        <slot></slot>
-      </a-layout-content>
+        <a-button
+          v-if="showClear"
+          type="default"
+          size="small"
+          class="button-clear-notification"
+          @click="onClearNotification">{{ $t('label.clear.notification') }}</a-button>
 
-      <!-- layout footer -->
-      <a-layout-footer style="padding: 0">
-        <global-footer />
-      </a-layout-footer>
+        <!-- layout content -->
+        <a-layout-content
+        class="layout-content"
+        :class="{'is-header-fixed': fixedHeader}">
+          <slot></slot>
+        </a-layout-content>
+
+        <!-- layout footer -->
+        <a-layout-footer style="padding: 0">
+          <global-footer />
+        </a-layout-footer>
+      </a-layout>
     </a-layout>
-  </a-layout>
+  </div>
 </template>
 
 <script>
@@ -118,6 +125,7 @@
 import { mapState, mapActions } from 'vuex'
 import { mixin, mixinDevice } from '@/utils/mixin.js'
 import { isAdmin } from '@/role'
+import { api } from '@/api'
 import Drawer from '@/components/widgets/Drawer'
 import Setting from '@/components/view/Setting.vue'
 
@@ -191,6 +199,7 @@
   created () {
     this.menus = this.mainMenu.find((item) => item.path === '/').children
     this.collapsed = !this.sidebarOpened
+    setInterval(this.checkShutdown, 5000)
   },
   mounted () {
     const layoutMode = this.$config.theme['@layout-mode'] || 'light'
@@ -243,6 +252,11 @@
     onClearNotification () {
       this.$notification.destroy()
       this.$store.commit('SET_COUNT_NOTIFY', 0)
+    },
+    checkShutdown () {
+      api('readyForShutdown', {}).then(json => {
+        this.$store.dispatch('SetShutdownTriggered', json.readyforshutdownresponse.readyforshutdown.shutdowntriggered || false)
+      })
     }
   }
 }
@@ -279,4 +293,15 @@
     padding: 0
   }
 }
+
+.shutdownHeader {
+  font-weight: bold;
+  height: 25px;
+  text-align: center;
+  padding: 0px;
+  margin: 0px;
+  width: 100vw;
+  position: absolute;
+}
+
 </style>
diff --git a/ui/src/components/view/ActionButton.vue b/ui/src/components/view/ActionButton.vue
index 95554f2..f15ec2f 100644
--- a/ui/src/components/view/ActionButton.vue
+++ b/ui/src/components/view/ActionButton.vue
@@ -21,14 +21,36 @@
       <template #title>
         {{ $t('label.view.console') }}
       </template>
-      <console :resource="resource" :size="size" />
+      <console
+        style="margin-top: -5px;"
+        :resource="resource"
+        size="default"
+        v-if="resource.id"
+        icon="code"
+      />
+    </a-tooltip>
+    <a-tooltip arrowPointAtCenter placement="bottomRight" v-if="resource && resource.id && dataView">
+      <template #title>
+        {{ $t('label.copy.consoleurl') }}
+      </template>
+      <console
+        copyUrlToClipboard
+        style="margin-top: -5px;"
+        :resource="resource"
+        size="default"
+        v-if="resource.id"
+        icon="copy"
+      />
     </a-tooltip>
     <a-tooltip
       v-for="(action, actionIndex) in actions"
       :key="actionIndex"
       arrowPointAtCenter
       placement="bottomRight">
-      <template #title>
+      <template v-if="action.hoverLabel" #title>
+        {{ $t(action.hoverLabel) }}
+      </template>
+      <template v-else #title>
         {{ $t(action.label) }}
       </template>
       <a-badge
diff --git a/ui/src/components/view/AnnotationsTab.vue b/ui/src/components/view/AnnotationsTab.vue
index 36539a2..97cf926 100644
--- a/ui/src/components/view/AnnotationsTab.vue
+++ b/ui/src/components/view/AnnotationsTab.vue
@@ -96,7 +96,7 @@
         <template #content>
           <div v-ctrl-enter="saveNote">
             <a-textarea
-              rows="4"
+              :rows="4"
               @change="handleNoteChange"
               v-model:value="annotation"
               :placeholder="$t('label.add.note')" />
@@ -192,6 +192,7 @@
         case 'SystemVm': return 'SYSTEM_VM'
         case 'VirtualRouter': return 'VR'
         case 'AutoScaleVmGroup': return 'AUTOSCALE_VM_GROUP'
+        case 'ManagementServer': return 'MANAGEMENT_SERVER'
         default: return ''
       }
     },
diff --git a/ui/src/components/view/BulkActionProgress.vue b/ui/src/components/view/BulkActionProgress.vue
index be21a05..c4068d0 100644
--- a/ui/src/components/view/BulkActionProgress.vue
+++ b/ui/src/components/view/BulkActionProgress.vue
@@ -52,36 +52,38 @@
         size="middle"
         :columns="selectedColumns"
         :dataSource="tableChanged ? filteredItems : selectedItems"
-        :rowKey="(record, idx) => ($route.path.includes('/template') || $route.path.includes('/iso')) ? record.zoneid: record.id"
+        :rowKey="record => ($route.path.includes('/template') || $route.path.includes('/iso')) ? record.zoneid: record.id"
         :pagination="true"
         @change="handleTableChange"
         style="overflow-y: auto">
-        <template #status="{text}">
-          <status :text=" text ? text : $t('state.inprogress') " displayText></status>
-        </template>
-        <template #algorithm="{record}">
-          {{ returnAlgorithmName(record.algorithm) }}
-        </template>
-        <template #privateport="{record}">
-          {{ record.privateport }} - {{ record.privateendport }}
-        </template>
-        <template #publicport="{record}">
-          {{ record.publicport }} - {{ record.publicendport }}
-        </template>
-        <template #protocol="{record}">
-          {{ capitalise(record.protocol) }}
-        </template>
-        <template #startport="{record}">
-          {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
-        </template>
-        <template #endport="{record}">
-          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
-        </template>
-        <template #vm="{record}">
-          <div><desktop-outlined /> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
-        </template>
-        <template #cidrlist="{ record }">
-          <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
+        <template #bodyCell="{ column, text, record }">
+          <template v-if="column.key === 'status'">
+            <status :text=" text ? text : $t('state.inprogress') " displayText></status>
+          </template>
+          <template v-if="column.key === 'algorithm'">
+            {{ returnAlgorithmName(record.algorithm) }}
+          </template>
+          <template v-if="column.key === 'privateport'">
+            {{ record.privateport }} - {{ record.privateendport }}
+          </template>
+          <template v-if="column.key === 'publicport'">
+            {{ record.publicport }} - {{ record.publicendport }}
+          </template>
+          <template v-if="column.key === 'protocol'">
+            {{ capitalise(record.protocol) }}
+          </template>
+          <template v-if="column.key === 'startport'">
+            {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
+          </template>
+          <template v-if="column.key === 'endport'">
+            {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
+          </template>
+          <template v-if="column.key === 'vm'">
+            <div><desktop-outlined /> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
+          </template>
+          <template v-if="column.key === 'cidrlist'">
+            <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
+          </template>
         </template>
       </a-table>
       <br/>
diff --git a/ui/src/components/view/BulkActionView.vue b/ui/src/components/view/BulkActionView.vue
index a22ab5a..215abb6 100644
--- a/ui/src/components/view/BulkActionView.vue
+++ b/ui/src/components/view/BulkActionView.vue
@@ -55,35 +55,37 @@
           size="middle"
           :columns="selectedColumns"
           :dataSource="selectedItems"
-          :rowKey="(record, idx) => $route.path.includes('/iso/') ? record.zoneid : record.id"
+          :rowKey="record => $route.path.includes('/iso/') ? record.zoneid : record.id"
           :pagination="true"
           style="overflow-y: auto">
-          <template #algorithm="{record}">
-            {{ returnAlgorithmName(record.algorithm) }}
-          </template>
-          <template #column="{ text }">
-            <span v-for="(column, index) in selectedColumns" :key="index"> {{ text }} ==== {{ column }}</span>
-          </template>
-          <template #privateport="{record}">
-            {{ record.privateport }} - {{ record.privateendport }}
-          </template>
-          <template #publicport="{record}">
-            {{ record.publicport }} - {{ record.publicendport }}
-          </template>
-          <template #protocol="{record}">
-            {{ capitalise(record.protocol) }}
-          </template>
-          <template #vm="{record}">
-            <div><desktop-outlined /> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
-          </template>
-          <template #startport="{record}">
-            {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
-          </template>
-          <template #endport="{record}">
-            {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
-          </template>
-          <template #cidrlist="{record}">
-            <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'algorithm'">
+              {{ returnAlgorithmName(record.algorithm) }}
+            </template>
+            <template v-if="column.key === 'column'">
+              <span v-for="(column, index) in selectedColumns" :key="index"> {{ text }} ==== {{ column }}</span>
+            </template>
+            <template v-if="column.key === 'privateport'">
+              {{ record.privateport }} - {{ record.privateendport }}
+            </template>
+            <template v-if="column.key === 'publicport'">
+              {{ record.publicport }} - {{ record.publicendport }}
+            </template>
+            <template v-if="column.key === 'protocol'">
+              {{ capitalise(record.protocol) }}
+            </template>
+            <template v-if="column.key === 'vm'">
+              <div><desktop-outlined /> {{ record.virtualmachinename }} ({{ record.vmguestip }})</div>
+            </template>
+            <template v-if="column.key === 'startport'">
+              {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
+            </template>
+            <template v-if="column.key === 'endport'">
+              {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
+            </template>
+            <template v-if="column.key === 'cidrlist'">
+              <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
+            </template>
           </template>
         </a-table>
         <a-divider />
diff --git a/ui/src/components/view/DedicateDomain.vue b/ui/src/components/view/DedicateDomain.vue
index 5f04083..0b3645c 100644
--- a/ui/src/components/view/DedicateDomain.vue
+++ b/ui/src/components/view/DedicateDomain.vue
@@ -26,12 +26,16 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }"
           @change="handleChangeDomain"
           v-focus="true"
           v-model:value="domainId">
-          <a-select-option v-for="(domain, index) in domainsList" :value="domain.id" :key="index">
+          <a-select-option
+            v-for="(domain, index) in domainsList"
+            :value="domain.id"
+            :key="index"
+            :label="domain.path || domain.name || domain.description">
             {{ domain.path || domain.name || domain.description }}
           </a-select-option>
         </a-select>
@@ -43,9 +47,9 @@
         style="width: 100%"
         @change="handleChangeAccount"
         showSearch
-        optionFilterProp="label"
+        optionFilterProp="value"
         :filterOption="(input, option) => {
-          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
         }" >
         <a-select-option v-for="(account, index) in accountsList" :value="account.name" :key="index">
           {{ account.name }}
diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue
index 17a4e95..e276e05 100644
--- a/ui/src/components/view/DetailsTab.vue
+++ b/ui/src/components/view/DetailsTab.vue
@@ -112,14 +112,14 @@
       type: Object,
       required: true
     },
-    loading: {
-      type: Boolean,
-      default: false
-    },
     items: {
       type: Object,
       default: () => {}
     },
+    loading: {
+      type: Boolean,
+      default: false
+    },
     bordered: {
       type: Boolean,
       default: false
diff --git a/ui/src/components/view/EventsTab.vue b/ui/src/components/view/EventsTab.vue
index 66f8ae7..899a430 100644
--- a/ui/src/components/view/EventsTab.vue
+++ b/ui/src/components/view/EventsTab.vue
@@ -152,19 +152,15 @@
       for (var columnKey of this.columnKeys) {
         if (!this.selectedColumnKeys.includes(columnKey)) continue
         this.columns.push({
+          key: columnKey,
           title: this.$t('label.' + String(columnKey).toLowerCase()),
           dataIndex: columnKey,
-          slots: { customRender: columnKey },
           sorter: function (a, b) { return genericCompare(a[this.dataIndex] || '', b[this.dataIndex] || '') }
         })
       }
-      this.columns.push({
-        dataIndex: 'dropdownFilter',
-        slots: {
-          filterDropdown: 'filterDropdown',
-          filterIcon: 'filterIcon'
-        }
-      })
+      if (this.columns.length > 0) {
+        this.columns[this.columns.length - 1].customFilterDropdown = true
+      }
     }
   }
 }
diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue
index 0b9ebf4..df1411a 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -84,11 +84,31 @@
               <a-tag v-if="resource.internetprotocol && ['IPv6', 'DualStack'].includes(resource.internetprotocol)">
                 {{ resource.internetprotocol ? $t('label.ip.v4.v6') : resource.internetprotocol }}
               </a-tag>
+              <a-tag v-if="resource.archived" :color="this.$config.theme['@warning-color']">
+                {{ $t('label.archived') }}
+              </a-tag>
               <a-tooltip placement="right" >
                 <template #title>
                   <span>{{ $t('label.view.console') }}</span>
                 </template>
-                <console style="margin-top: -5px;" :resource="resource" size="default" v-if="resource.id" />
+                <console
+                  style="margin-top: -5px;"
+                  :resource="resource"
+                  size="default"
+                  v-if="resource.id"
+                />
+              </a-tooltip>
+              <a-tooltip placement="right" >
+                <template #title>
+                  <span>{{ $t('label.copy.consoleurl') }}</span>
+                </template>
+                <console
+                  copyUrlToClipboard
+                  style="margin-top: -5px;"
+                  :resource="resource"
+                  size="default"
+                  v-if="resource.id"
+                />
               </a-tooltip>
             </div>
           </slot>
@@ -124,7 +144,7 @@
               icon="barcode-outlined"
               type="dashed"
               size="small"
-              :copyResource="resource.id"
+              :copyResource="String(resource.id)"
               @onClick="$message.success($t('label.copied.clipboard'))" />
             <span style="margin-left: 10px;">{{ resource.id }}</span>
           </div>
@@ -837,7 +857,7 @@
     },
     name () {
       return this.resource.displayname || this.resource.name || this.resource.displaytext || this.resource.username ||
-        this.resource.ipaddress || this.resource.virtualmachinename || this.resource.templatetype
+        this.resource.ipaddress || this.resource.virtualmachinename || this.resource.osname || this.resource.osdisplayname || this.resource.templatetype
     },
     keypairs () {
       if (!this.resource.keypairs) {
@@ -852,8 +872,13 @@
       return this.resource.templateid
     },
     resourceIcon () {
-      if (this.$showIcon() && this.resource?.icon?.base64image) {
-        return this.resource.icon.base64image
+      if (this.$showIcon()) {
+        if (this.resource?.icon?.base64image) {
+          return this.resource.icon.base64image
+        }
+        if (this.resource?.resourceIcon?.base64image) {
+          return this.resource.resourceIcon.base64image
+        }
       }
       return null
     },
diff --git a/ui/src/components/view/InstanceNicsNetworkSelectListView.vue b/ui/src/components/view/InstanceNicsNetworkSelectListView.vue
index 8ec485c..b9fe55a 100644
--- a/ui/src/components/view/InstanceNicsNetworkSelectListView.vue
+++ b/ui/src/components/view/InstanceNicsNetworkSelectListView.vue
@@ -25,23 +25,25 @@
       :dataSource="nics"
       :pagination="false"
       :rowKey="record => record.InstanceID">
-      <template #displaytext="{record}">
-        <span>{{ record.elementName + ' - ' + record.name }}
-          <a-tooltip :title="record.nicDescription" placement="top">
-            <info-circle-outlined class="table-tooltip-icon" />
-          </a-tooltip>
-        </span>
-      </template>
-      <template #size="{record}">
-        <span v-if="record.size">
-          {{ $bytesToHumanReadableSize(record.size) }}
-        </span>
-      </template>
-      <template #selectednetwork="{record}">
-        <span>{{ record.selectednetworkname || '' }}</span>
-      </template>
-      <template #select="{record}">
-        <div style="display: flex; justify-content: flex-end;"><a-button @click="openNicNetworkSelector(record)">{{ record.selectednetworkid ? $t('label.change') : $t('label.select') }}</a-button></div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'displaytext'">
+          <span>{{ record.elementName + ' - ' + record.name }}
+            <a-tooltip :title="record.nicDescription" placement="top">
+              <info-circle-outlined class="table-tooltip-icon" />
+            </a-tooltip>
+          </span>
+        </template>
+        <template v-if="column.key === 'size'">
+          <span v-if="record.size">
+            {{ $bytesToHumanReadableSize(record.size) }}
+          </span>
+        </template>
+        <template v-if="column.key === 'selectednetwork'">
+          <span>{{ record.selectednetworkname || '' }}</span>
+        </template>
+        <template v-if="column.key === 'select'">
+          <div style="display: flex; justify-content: flex-end;"><a-button @click="openNicNetworkSelector(record)">{{ record.selectednetworkid ? $t('label.change') : $t('label.select') }}</a-button></div>
+        </template>
       </template>
     </a-table>
 
@@ -87,16 +89,16 @@
     return {
       nicColumns: [
         {
-          title: this.$t('label.nic'),
-          slots: { customRender: 'displaytext' }
+          key: 'displaytext',
+          title: this.$t('label.nic')
         },
         {
-          title: this.$t('label.network'),
-          slots: { customRender: 'selectednetwork' }
+          key: 'selectednetwork',
+          title: this.$t('label.network')
         },
         {
-          title: '',
-          slots: { customRender: 'select' }
+          key: 'select',
+          title: ''
         }
       ],
       selectedNicForNetworkSelection: {}
diff --git a/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue b/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue
index 4489af6..77f1b49 100644
--- a/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue
+++ b/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue
@@ -26,16 +26,18 @@
       :dataSource="volumes"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #size="{ record }">
-        <span v-if="record.size">
-          {{ $bytesToHumanReadableSize(record.size) }}
-        </span>
-      </template>
-      <template #selectedstorage="{ record }">
-        <span>{{ record.selectedstoragename || '' }}</span>
-      </template>
-      <template #select="{ record }">
-        <div style="display: flex; justify-content: flex-end;"><a-button @click="openVolumeStoragePoolSelector(record)">{{ record.selectedstorageid ? $t('label.change') : $t('label.select') }}</a-button></div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'size'">
+          <span v-if="record.size">
+            {{ $bytesToHumanReadableSize(record.size) }}
+          </span>
+        </template>
+        <template v-if="column.key === 'selectedstorage'">
+          <span>{{ record.selectedstoragename || '' }}</span>
+        </template>
+        <template v-if="column.key === 'select'">
+          <div style="display: flex; justify-content: flex-end;"><a-button @click="openVolumeStoragePoolSelector(record)">{{ record.selectedstorageid ? $t('label.change') : $t('label.select') }}</a-button></div>
+        </template>
       </template>
     </a-table>
 
@@ -86,24 +88,26 @@
       volumesLoading: false,
       volumeColumns: [
         {
+          key: 'name',
           title: this.$t('label.volumeid'),
           dataIndex: 'name'
         },
         {
+          key: 'type',
           title: this.$t('label.type'),
           dataIndex: 'type'
         },
         {
-          title: this.$t('label.size'),
-          slots: { customRender: 'size' }
+          key: 'size',
+          title: this.$t('label.size')
         },
         {
-          title: this.$t('label.storage'),
-          slots: { customRender: 'selectedstorage' }
+          key: 'selectedstorage',
+          title: this.$t('label.storage')
         },
         {
-          title: '',
-          slots: { customRender: 'select' }
+          key: 'select',
+          title: ''
         }
       ],
       selectedVolumeForStoragePoolSelection: {},
diff --git a/ui/src/components/view/ListResourceTable.vue b/ui/src/components/view/ListResourceTable.vue
index 398691a..c97aa0f 100644
--- a/ui/src/components/view/ListResourceTable.vue
+++ b/ui/src/components/view/ListResourceTable.vue
@@ -34,22 +34,23 @@
       :pagination="defaultPagination"
       @change="handleTableChange"
       @handle-search-filter="handleTableChange" >
+      <template #bodyCell="{ column, text, record }">
+        <div
+          v-for="(col, index) in Object.keys(routerlinks({}))"
+          :key="index">
+          <template v-if="column.key === col">
+            <router-link :set="routerlink = routerlinks(record)" :to="{ path: routerlink[col] }" >{{ text }}</router-link>
+          </template>
+        </div>
 
-      <template
-        v-for="(column, index) in Object.keys(routerlinks({}))"
-        :key="index"
-        #[column]="{ text, record }" >
-        <router-link :set="routerlink = routerlinks(record)" :to="{ path: routerlink[column] }" >{{ text }}</router-link>
+        <template v-if="column.key === 'state'">
+          <status :text="text ? text : ''" />{{ text }}
+        </template>
+
+        <template v-if="column.key === 'status'">
+          <status :text="text ? text : ''" />{{ text }}
+        </template>
       </template>
-
-      <template #state="{text}">
-        <status :text="text ? text : ''" />{{ text }}
-      </template>
-
-      <template #status="{text}">
-        <status :text="text ? text : ''" />{{ text }}
-      </template>
-
     </a-table>
 
     <div v-if="!defaultPagination" style="display: block; text-align: right; margin-top: 10px;">
@@ -197,9 +198,9 @@
       var columns = []
       for (const col of this.columns) {
         columns.push({
+          key: col,
           dataIndex: col,
-          title: this.$t('label.' + col),
-          slots: { customRender: col }
+          title: this.$t('label.' + col)
         })
       }
       return columns
diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue
index 7ccb028..78d312e 100644
--- a/ui/src/components/view/ListView.vue
+++ b/ui/src/components/view/ListView.vue
@@ -23,11 +23,11 @@
     :dataSource="items"
     :rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
     :pagination="false"
-    :rowSelection=" enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
+    :rowSelection=" enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null"
     :rowClassName="getRowClassName"
     style="overflow-y: auto"
   >
-    <template #filterDropdown>
+    <template #customFilterDropdown>
       <div style="padding: 8px" class="filter-dropdown">
         <a-menu>
           <a-menu-item v-for="(column, idx) in columnKeys" :key="idx" @click="updateSelectedColumns(column)">
@@ -37,391 +37,403 @@
         </a-menu>
       </div>
     </template>
-    <template #footer>
-      <span v-if="hasSelected">
-        {{ `Selected ${selectedRowKeys.length} items` }}
-      </span>
-    </template>
-
-    <!--
-    <div #expandedRowRender="{ resource }">
-      <info-card :resource="resource style="margin-left: 0px; width: 50%">
-        <div #actions style="padding-top: 12px">
-          <a-tooltip
-            v-for="(action, actionIndex) in $route.meta.actions"
-            :key="actionIndex"
-            placement="bottom">
-            <template #title>
-              {{ $t(action.label) }}
-            </template>
-            <a-button
-              v-if="action.api in $store.getters.apis && action.dataView &&
-                ('show' in action ? action.show(resource, $store.getters.userInfo) : true)"
-              :icon="action.icon"
-              :type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
-              shape="circle"
-              style="margin-right: 5px; margin-top: 12px"
-              @click="$parent.execAction(action)"
-            >
-            </a-button>
-          </a-tooltip>
-        </div>
-      </info-card>
-    </div>
-    -->
-
-    <template #name="{text, record}">
-      <span v-if="['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
-        <span v-if="record.icon && record.icon.base64image">
-          <resource-icon :image="record.icon.base64image" size="1x"/>
+    <template #bodyCell="{ column, text, record }">
+      <template v-if="column.key === 'name'">
+        <span v-if="['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
+          <span v-if="record.icon && record.icon.base64image">
+            <resource-icon :image="record.icon.base64image" size="1x"/>
+          </span>
+          <os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="lg" />
         </span>
-        <os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="lg" />
-      </span>
-      <span style="min-width: 120px" >
+        <span style="min-width: 120px" >
+          <QuickView
+            style="margin-left: 5px"
+            :actions="actions"
+            :resource="record"
+            :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'name' "
+            @exec-action="$parent.execAction"/>
+          <span v-if="$route.path.startsWith('/project')" style="margin-right: 5px">
+            <tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" />
+          </span>
+          <span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
+            <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="1x"/>
+            <os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="1x" />
+            <render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
+            <render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" />
+          </span>
+          <span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }">
+            <os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" />
+          </span>
+
+          <span v-if="record.hasannotations">
+            <span v-if="record.id">
+              <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
+              <router-link :to="{ path: $route.path + '/' + record.id, query: { tab: 'comments' } }"><message-filled style="padding-left: 10px" size="small"/></router-link>
+            </span>
+            <router-link v-else :to="{ path: $route.path + '/' + record.name }" >{{ text }}</router-link>
+          </span>
+          <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span>
+          <span v-else-if="$route.path.startsWith('/alert')">
+            <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
+            <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
+          </span>
+          <span v-else-if="$route.path.startsWith('/tungstenfabric')">
+            <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
+            <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
+          </span>
+          <span v-else-if="isTungstenPath()">
+            <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" v-if="record.uuid && record.zoneid">{{ $t(text.toLowerCase()) }}</router-link>
+            <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link>
+            <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
+          </span>
+          <span v-else>
+            <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
+            <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
+          </span>
+        </span>
+      </template>
+      <template v-if="record.clustertype === 'ExternalManaged' && $route.path.split('/')[1] === 'kubernetes' && ['cpunumber', 'memory', 'size'].includes(column.key)">
+        <span>{{ text <= 0 ? 'N/A' : text }}</span>
+      </template>
+      <template v-if="column.key === 'templatetype'">
+        <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'type'">
+        <span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span>
+        <span v-else>{{ text }}</span>
+      </template>
+
+      <template v-if="column.key === 'schedule'">
+          {{ text }}
+          <br/>
+          ({{ generateHumanReadableSchedule(text) }})
+      </template>
+      <template v-if="column.key === 'displayname'">
         <QuickView
           style="margin-left: 5px"
           :actions="actions"
           :resource="record"
-          :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'name' "
+          :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' "
           @exec-action="$parent.execAction"/>
-        <span v-if="$route.path.startsWith('/project')" style="margin-right: 5px">
-          <tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" />
-        </span>
+        <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'username'">
         <span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
           <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="1x"/>
-          <os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="1x" />
-          <render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
-          <render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" />
+          <user-outlined v-else style="font-size: 16px;" />
         </span>
-        <span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }">
-          <os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" />
+        <router-link :to="{ path: $route.path + '/' + record.id }" v-if="['/accountuser', '/vpnuser'].includes($route.path)">{{ text }}</router-link>
+        <router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'entityid'">
+        <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname }}</router-link>
+      </template>
+      <template v-if="column.key === 'entitytype'">
+        {{ generateHumanReadableEntityType(record) }}
+      </template>
+      <template v-if="column.key === 'adminsonly' && ['Admin'].includes($store.getters.userInfo.roletype)">
+        <a-checkbox :checked="record.adminsonly" :value="record.id" v-if="record.userid === $store.getters.userInfo.id" @change="e => updateAdminsOnly(e)" />
+        <a-checkbox :checked="record.adminsonly" disabled v-else />
+      </template>
+      <template v-if="column.key === 'ipaddress'" href="javascript:;">
+        <router-link v-if="['/publicip', '/privategw'].includes($route.path)" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+        <span v-if="record.issourcenat">
+          &nbsp;
+          <a-tag>source-nat</a-tag>
         </span>
-
-        <span v-if="record.hasannotations">
-          <span v-if="record.id">
-            <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
-            <router-link :to="{ path: $route.path + '/' + record.id, query: { tab: 'comments' } }"><message-filled style="padding-left: 10px" size="small"/></router-link>
-          </span>
-          <router-link v-else :to="{ path: $route.path + '/' + record.name }" >{{ text }}</router-link>
+        <span v-if="record.isstaticnat">
+          &nbsp;
+          <a-tag>static-nat</a-tag>
         </span>
-        <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span>
-        <span v-else-if="$route.path.startsWith('/preferences')">{{ text }}</span>
-        <span v-else-if="$route.path.startsWith('/alert')">
-          <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
-          <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
-        </span>
-        <span v-else-if="$route.path.startsWith('/tungstenfabric')">
-          <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link>
-          <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
-        </span>
-        <span v-else-if="isTungstenPath()">
-          <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" v-if="record.uuid && record.zoneid">{{ $t(text.toLowerCase()) }}</router-link>
-          <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link>
-          <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link>
-        </span>
-        <span v-else>
-          <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
-          <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
-        </span>
+      </template>
+      <template v-if="column.key === 'ip6address'" href="javascript:;">
+        <span>{{ ipV6Address(text, record) }}</span>
+      </template>
+      <template v-if="column.key === 'publicip'">
+        <router-link v-if="['/autoscalevmgroup'].includes($route.path)" :to="{ path: '/publicip' + '/' + record.publicipid }">{{ text }}</router-link>
+        <router-link v-else :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'traffictype'">
+        {{ text }}
+      </template>
+      <template v-if="column.key === 'vmname'">
+        <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'virtualmachinename'">
+        <router-link :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'hypervisor'">
+        <span v-if="$route.name === 'hypervisorcapability'">
+        <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
       </span>
-    </template>
-    <template #templatetype="{ text, record }">
-      <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link>
-    </template>
-    <template #type="{ text }">
-      <span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #displayname="{text, record}">
-      <QuickView
-        style="margin-left: 5px"
-        :actions="actions"
-        :resource="record"
-        :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' "
-        @exec-action="$parent.execAction"/>
-      <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
-    </template>
-    <template #username="{text, record}">
-      <span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px">
-        <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="1x"/>
-        <user-outlined v-else style="font-size: 16px;" />
-      </span>
-      <router-link :to="{ path: $route.path + '/' + record.id }" v-if="['/accountuser', '/vpnuser'].includes($route.path)">{{ text }}</router-link>
-      <router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #entityid="{ record }" href="javascript:;">
-      <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname }}</router-link>
-    </template>
-    <template #entitytype="{ record }" href="javascript:;">
-      {{ generateHumanReadableEntityType(record) }}
-    </template>
-    <template #adminsonly="{ record }" v-if="['Admin'].includes($store.getters.userInfo.roletype)" href="javascript:;">
-      <a-checkbox :checked="record.adminsonly" :value="record.id" v-if="record.userid === $store.getters.userInfo.id" @change="e => updateAdminsOnly(e)" />
-      <a-checkbox :checked="record.adminsonly" disabled v-else />
-    </template>
-    <template #ipaddress="{ text, record }" href="javascript:;">
-      <router-link v-if="['/publicip', '/privategw'].includes($route.path)" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-      <span v-if="record.issourcenat">
-        &nbsp;
-        <a-tag>source-nat</a-tag>
-      </span>
-      <span v-if="record.isstaticnat">
-        &nbsp;
-        <a-tag>static-nat</a-tag>
-      </span>
-    </template>
-    <template #ip6address="{ text, record }" href="javascript:;">
-      <span>{{ ipV6Address(text, record) }}</span>
-    </template>
-    <template #publicip="{ text, record }">
-      <router-link v-if="['/autoscalevmgroup'].includes($route.path)" :to="{ path: '/publicip' + '/' + record.publicipid }">{{ text }}</router-link>
-      <router-link v-else :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
-    </template>
-    <template #traffictype="{ text }" href="javascript:;">
-      {{ text }}
-    </template>
-    <template #vmname="{ text, record }">
-      <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text }}</router-link>
-    </template>
-    <template #virtualmachinename="{ text, record }">
-      <router-link :to="{ path: '/vm/' + record.virtualmachineid }">{{ text }}</router-link>
-    </template>
-    <template #hypervisor="{ text, record }">
-      <span v-if="$route.name === 'hypervisorcapability'">
+      <span v-else-if="$route.name === 'guestoshypervisormapping'">
+        <QuickView
+          style="margin-left: 5px"
+          :actions="actions"
+          :resource="record"
+          :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'hypervisor' "
+          @exec-action="$parent.execAction"/>
         <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
       </span>
       <span v-else>{{ text }}</span>
     </template>
-    <template #state="{ text, record }">
+    <template v-if="column.key === 'osname'">
+      <span v-if="$route.name === 'guestos'">
+        <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
+      </span>
+      <span v-else>{{ text }}</span>
+    </template>
+    <template v-if="column.key === 'state'">
       <status v-if="$route.path.startsWith('/host')" :text="getHostState(record)" displayText />
       <status v-else :text="text ? text : ''" displayText :styles="{ 'min-width': '80px' }" />
     </template>
-    <template #allocationstate="{ text }">
+    <template v-if="column.key === 'allocationstate'">
       <status :text="text ? text : ''" displayText />
     </template>
-    <template #resourcestate="{ text }">
+    <template v-if="column.key === 'resourcestate'">
       <status :text="text ? text : ''" displayText />
     </template>
-    <template #powerstate="{ text }">
+    <template v-if="column.key === 'powerstate'">
       <status :text="text ? text : ''" displayText />
     </template>
-    <template #agentstate="{ text }">
+    <template v-if="column.key === 'agentstate'">
       <status :text="text ? text : ''" displayText />
     </template>
-    <template #quotastate="{ text }">
+    <template v-if="column.key === 'quotastate'">
       <status :text="text ? text : ''" displayText />
     </template>
-    <template #vlan="{ text, record }">
+    <template v-if="column.key === 'vlan'">
       <a href="javascript:;">
         <router-link v-if="$route.path === '/guestvlans'" :to="{ path: '/guestvlans/' + record.id }">{{ text }}</router-link>
       </a>
     </template>
-    <template #guestnetworkname="{ text, record }">
+    <template v-if="column.key === 'guestnetworkname'">
       <router-link :to="{ path: '/guestnetwork/' + record.guestnetworkid }">{{ text }}</router-link>
     </template>
-    <template #associatednetworkname="{ text, record }">
+    <template v-if="column.key === 'associatednetworkname'">
       <router-link :to="{ path: '/guestnetwork/' + record.associatednetworkid }">{{ text }}</router-link>
     </template>
-    <template #vpcname="{ text, record }">
+    <template v-if="column.key === 'vpcname'">
       <router-link :to="{ path: '/vpc/' + record.vpcid }">{{ text }}</router-link>
     </template>
-    <template #hostname="{ text, record }">
+    <template v-if="column.key === 'hostname'">
       <router-link v-if="record.hostid" :to="{ path: '/host/' + record.hostid }">{{ text }}</router-link>
       <router-link v-else-if="record.hostname" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
       <span v-else>{{ text }}</span>
     </template>
-    <template #storage="{ text, record }">
+    <template v-if="column.key === 'storage'">
       <router-link v-if="record.storageid" :to="{ path: '/storagepool/' + record.storageid }">{{ text }}</router-link>
       <span v-else>{{ text }}</span>
     </template>
-
-    <template
-      v-for="(value, name) in thresholdMapping"
-      :key="name"
-      #[name]="{ text, record }"
-      href="javascript:;">
-      <span>
-        <span v-if="record[value.disable]" class="alert-disable-threshold">
-          {{ text }}
-        </span>
-        <span v-else-if="record[value.notification]" class="alert-notification-threshold">
-          {{ text }}
-        </span>
-        <span style="padding: 10%;" v-else>
-          {{ text }}
-        </span>
-      </span>
-    </template>
-
-    <template #level="{ text, record }">
-      <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link>
-    </template>
-
-    <template #clustername="{ text, record }">
-      <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link>
-    </template>
-    <template #podname="{ text, record }">
-      <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
-    </template>
-    <template #account="{ text, record }">
-      <template v-if="record.owner">
-        <template v-for="(item, idx) in record.owner" :key="idx">
-          <span style="margin-right:5px">
-            <span v-if="$store.getters.userInfo.roletype !== 'User'">
-              <router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
-              <router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: record.domainid, dataView: true } }">{{ item.account }}</router-link>
-            </span>
-            <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span>
+    <template v-for="(value, name) in thresholdMapping" :key="name">
+      <template v-if="column.key === name">
+        <span>
+          <span v-if="record[value.disable]" class="alert-disable-threshold">
+            {{ text }}
           </span>
+          <span v-else-if="record[value.notification]" class="alert-notification-threshold">
+            {{ text }}
+          </span>
+          <span style="padding: 10%;" v-else>
+            {{ text }}
+          </span>
+        </span>
+      </template>
+    </template>
+
+      <template v-if="column.key === 'level'">
+        <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link>
+      </template>
+
+      <template v-if="column.key === 'clustername'">
+        <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'podname'">
+        <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'account'">
+        <template v-if="record.owner">
+          <template v-for="(item, idx) in record.owner" :key="idx">
+            <span style="margin-right:5px">
+              <span v-if="$store.getters.userInfo.roletype !== 'User'">
+                <router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
+                <router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: record.domainid, dataView: true } }">{{ item.account }}</router-link>
+              </span>
+              <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span>
+            </span>
+          </template>
+        </template>
+        <template v-if="text && !text.startsWith('PrjAcct-')">
+          <router-link
+            v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`).matched[0].redirect !== '/exception/404'"
+            :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link>
+          <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link>
+          <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
+          <span v-else>{{ text }}</span>
         </template>
       </template>
-      <template v-if="text && !text.startsWith('PrjAcct-')">
-        <router-link
-          v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`).matched[0].redirect !== '/exception/404'"
-          :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link>
-        <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link>
-        <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
+      <template v-if="column.key === 'resource'">
+        <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" />
+      </template>
+      <template v-if="column.key === 'domain'">
+        <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
         <span v-else>{{ text }}</span>
       </template>
-    </template>
-    <template #resource="{ record }">
-      <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" />
-    </template>
-    <template #domain="{ text, record }">
-      <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #domainpath="{ text, record }">
-      <router-link v-if="record.domainid && !record.domainid.includes(',') && $router.resolve('/domain/' + record.domainid).matched[0].redirect !== '/exception/404'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #zone="{ text, record }">
-      <router-link v-if="record.zoneid && !record.zoneid.includes(',') && $router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #zonename="{ text, record }">
-      <router-link v-if="$router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #rolename="{ text, record }">
-      <router-link v-if="record.roleid && $router.resolve('/role/' + record.roleid).matched[0].redirect !== '/exception/404'" :to="{ path: '/role/' + record.roleid }">{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #templateversion="{ record }">
-      <span>  {{ record.version }} </span>
-    </template>
-    <template #softwareversion="{ record }">
-      <span>  {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span>
-    </template>
-    <template #access="{ record }">
-      <status :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" displayText />
-    </template>
-    <template #requiresupgrade="{ record }">
-      <status :text="record.requiresupgrade ? 'warning' : ''" />
-      {{ record.requiresupgrade ? 'Yes' : 'No' }}
-    </template>
-    <template #loadbalancerrule="{ record }">
-      <span>  {{ record.loadbalancerrule }} </span>
-    </template>
-    <template #autoscalingenabled="{ record }">
-      <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" />
-      {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }}
-    </template>
-    <template #current="{record}">
-      <status :text="record.current ? record.current.toString() : 'false'" />
-    </template>
-    <template #created="{ text }">
-      {{ $toLocaleDate(text) }}
-    </template>
-    <template #sent="{ text }">
-      {{ $toLocaleDate(text) }}
-    </template>
-    <template #order="{ text, record }">
-      <div class="shift-btns">
-        <a-tooltip :name="text" placement="top">
-          <template #title>{{ $t('label.move.to.top') }}</template>
-          <a-button
-            shape="round"
-            @click="moveItemTop(record)"
-            class="shift-btn">
-            <DoubleLeftOutlined class="shift-btn shift-btn--rotated" />
-          </a-button>
-        </a-tooltip>
-        <a-tooltip placement="top">
-          <template #title>{{ $t('label.move.to.bottom') }}</template>
-          <a-button
-            shape="round"
-            @click="moveItemBottom(record)"
-            class="shift-btn">
-            <DoubleRightOutlined class="shift-btn shift-btn--rotated" />
-          </a-button>
-        </a-tooltip>
-        <a-tooltip placement="top">
-          <template #title>{{ $t('label.move.up.row') }}</template>
-          <a-button shape="round" @click="moveItemUp(record)" class="shift-btn">
-            <CaretUpOutlined class="shift-btn" />
-          </a-button>
-        </a-tooltip>
-        <a-tooltip placement="top">
-          <template #title>{{ $t('label.move.down.row') }}</template>
-          <a-button shape="round" @click="moveItemDown(record)" class="shift-btn">
-            <CaretDownOutlined class="shift-btn" />
-          </a-button>
-        </a-tooltip>
-      </div>
-    </template>
+      <template v-if="column.key === 'domainpath'">
+        <router-link v-if="record.domainid && !record.domainid.includes(',') && $router.resolve('/domain/' + record.domainid).matched[0].redirect !== '/exception/404'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'zone'">
+        <router-link v-if="record.zoneid && !record.zoneid.includes(',') && $router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'zonename'">
+        <router-link v-if="$router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + record.zoneid }">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'rolename'">
+        <router-link v-if="record.roleid && $router.resolve('/role/' + record.roleid).matched[0].redirect !== '/exception/404'" :to="{ path: '/role/' + record.roleid }">{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'templateversion'">
+        <span>  {{ record.version }} </span>
+      </template>
+      <template v-if="column.key === 'softwareversion'">
+        <span>  {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span>
+      </template>
+      <template v-if="column.key === 'access'">
+        <status :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" displayText />
+      </template>
+      <template v-if="column.key === 'requiresupgrade'">
+        <status :text="record.requiresupgrade ? 'warning' : ''" />
+        {{ record.requiresupgrade ? 'Yes' : 'No' }}
+      </template>
+      <template v-if="column.key === 'loadbalancerrule'">
+        <span>  {{ record.loadbalancerrule }} </span>
+      </template>
+      <template v-if="column.key === 'autoscalingenabled'">
+        <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" />
+        {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }}
+      </template>
+      <template v-if="column.key === 'current'">
+        <status :text="record.current ? record.current.toString() : 'false'" />
+      </template>
+      <template v-if="column.key === 'enabled'">
+        <status :text="record.enabled ? record.enabled.toString() : 'false'" />
+        {{ record.enabled ? 'Enabled' : 'Disabled' }}
+      </template>
+      <template v-if="['created', 'sent'].includes(column.key)">
+        {{ $toLocaleDate(text) }}
+      </template>
+      <template v-if="['startdate', 'enddate'].includes(column.key) && ['vm'].includes($route.path.split('/')[1])">
+        {{ getDateAtTimeZone(text, record.timezone) }}
+      </template>
+      <template v-if="column.key === 'order'">
+        <div class="shift-btns">
+          <a-tooltip :name="text" placement="top">
+            <template #title>{{ $t('label.move.to.top') }}</template>
+            <a-button
+              shape="round"
+              @click="moveItemTop(record)"
+              class="shift-btn">
+              <DoubleLeftOutlined class="shift-btn shift-btn--rotated" />
+            </a-button>
+          </a-tooltip>
+          <a-tooltip placement="top">
+            <template #title>{{ $t('label.move.to.bottom') }}</template>
+            <a-button
+              shape="round"
+              @click="moveItemBottom(record)"
+              class="shift-btn">
+              <DoubleRightOutlined class="shift-btn shift-btn--rotated" />
+            </a-button>
+          </a-tooltip>
+          <a-tooltip placement="top">
+            <template #title>{{ $t('label.move.up.row') }}</template>
+            <a-button shape="round" @click="moveItemUp(record)" class="shift-btn">
+              <CaretUpOutlined class="shift-btn" />
+            </a-button>
+          </a-tooltip>
+          <a-tooltip placement="top">
+            <template #title>{{ $t('label.move.down.row') }}</template>
+            <a-button shape="round" @click="moveItemDown(record)" class="shift-btn">
+              <CaretDownOutlined class="shift-btn" />
+            </a-button>
+          </a-tooltip>
+        </div>
+      </template>
 
-    <template #value="{ text, record }">
-      <a-input
-        v-if="editableValueKey === record.key"
-        v-focus="true"
-        :defaultValue="record.value"
-        :disabled="!('updateConfiguration' in $store.getters.apis)"
-        v-model:value="editableValue"
-        @keydown.esc="editableValueKey = null"
-        @pressEnter="saveValue(record)">
-      </a-input>
-      <div v-else style="width: 200px; word-break: break-all">
-        {{ text }}
-      </div>
+      <template v-if="column.key === 'value'">
+        <a-input
+          v-if="editableValueKey === record.key"
+          v-focus="true"
+          :defaultValue="record.value"
+          :disabled="!('updateConfiguration' in $store.getters.apis)"
+          v-model:value="editableValue"
+          @keydown.esc="editableValueKey = null"
+          @pressEnter="saveValue(record)">
+        </a-input>
+        <div v-else style="width: 200px; word-break: break-all">
+          {{ text }}
+        </div>
+      </template>
+      <template v-if="column.key === 'actions'">
+        <tooltip-button
+          :tooltip="$t('label.edit')"
+          :disabled="!('updateConfiguration' in $store.getters.apis)"
+          v-if="editableValueKey !== record.key"
+          icon="edit-outlined"
+          @onClick="editValue(record)" />
+        <tooltip-button
+          :tooltip="$t('label.cancel')"
+          @onClick="editableValueKey = null"
+          v-if="editableValueKey === record.key"
+          iconType="CloseCircleTwoTone"
+          iconTwoToneColor="#f5222d" />
+        <tooltip-button
+          :tooltip="$t('label.ok')"
+          :disabled="!('updateConfiguration' in $store.getters.apis)"
+          @onClick="saveValue(record)"
+          v-if="editableValueKey === record.key"
+          iconType="CheckCircleTwoTone"
+          iconTwoToneColor="#52c41a" />
+        <tooltip-button
+          :tooltip="$t('label.reset.config.value')"
+          @onClick="resetConfig(record)"
+          v-if="editableValueKey !== record.key"
+          icon="reload-outlined"
+          :disabled="!('updateConfiguration' in $store.getters.apis)" />
+      </template>
+      <template v-if="column.key === 'tariffActions'">
+        <tooltip-button
+          :tooltip="$t('label.edit')"
+          v-if="editableValueKey !== record.key"
+          :disabled="!('quotaTariffUpdate' in $store.getters.apis)"
+          icon="edit-outlined"
+          @onClick="editTariffValue(record)" />
+        <slot></slot>
+      </template>
+      <template v-if="column.key === 'vmScheduleActions'">
+        <tooltip-button
+          :tooltip="$t('label.edit')"
+          :disabled="!('updateVMSchedule' in $store.getters.apis)"
+          icon="edit-outlined"
+          @onClick="updateVMSchedule(record)" />
+        <tooltip-button
+          :tooltip="$t('label.remove')"
+          :disabled="!('deleteVMSchedule' in $store.getters.apis)"
+          icon="delete-outlined"
+          :danger="true"
+          type="primary"
+          @onClick="removeVMSchedule(record)" />
+      </template>
     </template>
-    <template #actions="{ record }">
-      <tooltip-button
-        :tooltip="$t('label.edit')"
-        :disabled="!('updateConfiguration' in $store.getters.apis)"
-        v-if="editableValueKey !== record.key"
-        icon="edit-outlined"
-        @onClick="editValue(record)" />
-      <tooltip-button
-        :tooltip="$t('label.cancel')"
-        @onClick="editableValueKey = null"
-        v-if="editableValueKey === record.key"
-        iconType="CloseCircleTwoTone"
-        iconTwoToneColor="#f5222d" />
-      <tooltip-button
-        :tooltip="$t('label.ok')"
-        :disabled="!('updateConfiguration' in $store.getters.apis)"
-        @onClick="saveValue(record)"
-        v-if="editableValueKey === record.key"
-        iconType="CheckCircleTwoTone"
-        iconTwoToneColor="#52c41a" />
-      <tooltip-button
-        :tooltip="$t('label.reset.config.value')"
-        @onClick="resetConfig(record)"
-        v-if="editableValueKey !== record.key"
-        icon="reload-outlined"
-        :disabled="!('updateConfiguration' in $store.getters.apis)" />
-    </template>
-    <template #tariffActions="{ record }">
-      <tooltip-button
-        :tooltip="$t('label.edit')"
-        v-if="editableValueKey !== record.key"
-        :disabled="!('quotaTariffUpdate' in $store.getters.apis)"
-        icon="edit-outlined"
-        @onClick="editTariffValue(record)" />
-      <slot></slot>
+    <template #footer>
+      <span v-if="hasSelected">
+        {{ `Selected ${selectedRowKeys.length} items` }}
+      </span>
     </template>
   </a-table>
 </template>
@@ -435,6 +447,8 @@
 import ResourceIcon from '@/components/view/ResourceIcon'
 import ResourceLabel from '@/components/widgets/ResourceLabel'
 import { createPathBasedOnVmType } from '@/utils/plugins'
+import cronstrue from 'cronstrue/i18n'
+import moment from 'moment-timezone'
 
 export default {
   name: 'ListView',
@@ -542,7 +556,7 @@
         '/project', '/account',
         '/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
         '/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
-        '/tungstenfabric'].join('|'))
+        '/tungstenfabric', '/guestos', '/guestoshypervisormapping'].join('|'))
         .test(this.$route.path)
     },
     enableGroupAction () {
@@ -552,6 +566,9 @@
         'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment'
       ].includes(this.$route.name)
     },
+    getDateAtTimeZone (date, timezone) {
+      return date ? moment(date).tz(timezone).format('YYYY-MM-DD HH:mm:ss') : null
+    },
     fetchColumns () {
       if (this.isOrderUpdatable()) {
         return this.columns
@@ -716,6 +733,12 @@
     editTariffValue (record) {
       this.$emit('edit-tariff-action', true, record)
     },
+    updateVMSchedule (record) {
+      this.$emit('update-vm-schedule', record)
+    },
+    removeVMSchedule (record) {
+      this.$emit('remove-vm-schedule', record)
+    },
     ipV6Address (text, record) {
       if (!record || !record.nic || record.nic.length === 0) {
         return ''
@@ -760,6 +783,9 @@
         default: return record.entitytype.toLowerCase().replace('_', '')
       }
     },
+    generateHumanReadableSchedule (schedule) {
+      return cronstrue.toString(schedule, { locale: this.$i18n.locale })
+    },
     entityTypeToPath (entitytype) {
       switch (entitytype) {
         case 'VM' : return 'vm'
@@ -822,6 +848,14 @@
     },
     updateSelectedColumns (name) {
       this.$emit('update-selected-columns', name)
+    },
+    getVmRouteUsingType (record) {
+      switch (record.virtualmachinetype) {
+        case 'DomainRouter' : return '/router/'
+        case 'ConsoleProxy' :
+        case 'SecondaryStorageVm': return '/systemvm/'
+        default: return '/vm/'
+      }
     }
   }
 }
diff --git a/ui/src/components/view/NicNetworkSelectForm.vue b/ui/src/components/view/NicNetworkSelectForm.vue
index f19b151..d54f775 100644
--- a/ui/src/components/view/NicNetworkSelectForm.vue
+++ b/ui/src/components/view/NicNetworkSelectForm.vue
@@ -33,11 +33,13 @@
         :dataSource="networks"
         :pagination="false"
         :rowKey="record => record.id">
-        <template #select="{record}">
-          <a-radio
-            @click="updateSelection(record)"
-            :checked="selectedNetwork != null && record.id === selectedNetwork.id">
-          </a-radio>
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'select'">
+            <a-radio
+              @click="updateSelection(record)"
+              :checked="selectedNetwork != null && record.id === selectedNetwork.id">
+            </a-radio>
+          </template>
         </template>
       </a-table>
       <a-pagination
@@ -97,24 +99,28 @@
       selectedNetwork: null,
       columns: [
         {
+          key: 'name',
           title: this.$t('label.networkid'),
           dataIndex: 'name'
         },
         {
+          key: 'type',
           title: this.$t('label.guestiptype'),
           dataIndex: 'type'
         },
         {
+          key: 'vpcName',
           title: this.$t('label.vpc'),
           dataIndex: 'vpcName'
         },
         {
+          key: 'cidr',
           title: this.$t('label.cidr'),
           dataIndex: 'cidr'
         },
         {
-          title: this.$t('label.select'),
-          slots: { customRender: 'select' }
+          key: 'select',
+          title: this.$t('label.select')
         }
       ]
     }
diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue
index 680d552..267d893 100644
--- a/ui/src/components/view/SearchView.vue
+++ b/ui/src/components/view/SearchView.vue
@@ -70,7 +70,7 @@
                       v-for="(opt, idx) in field.opts"
                       :key="idx"
                       :value="opt.id"
-                      :label="$t(opt.name)">
+                      :label="$t(opt.path || opt.name)">
                       <div>
                         <span v-if="(field.name.startsWith('zone'))">
                           <span v-if="opt.icon">
@@ -618,6 +618,7 @@
     },
     onClear () {
       this.formRef.value.resetFields()
+      this.form = reactive({})
       this.isFiltered = false
       this.inputKey = null
       this.inputValue = null
@@ -630,7 +631,6 @@
       this.formRef.value.validate().then(() => {
         const values = toRaw(this.form)
         this.isFiltered = true
-        console.log(values)
         for (const key in values) {
           const input = values[key]
           if (input === '' || input === null || input === undefined) {
diff --git a/ui/src/components/view/StoragePoolSelectView.vue b/ui/src/components/view/StoragePoolSelectView.vue
index 1422cea..8f006ed 100644
--- a/ui/src/components/view/StoragePoolSelectView.vue
+++ b/ui/src/components/view/StoragePoolSelectView.vue
@@ -32,45 +32,49 @@
       :dataSource="storagePools"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #suitabilityCustomTitle>
-        {{ $t('label.suitability') }}
-        <a-tooltip :title="$t('message.volume.state.primary.storage.suitability')" placement="top">
-          <info-circle-outlined class="table-tooltip-icon" />
-        </a-tooltip>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'suitability'">
+          {{ $t('label.suitability') }}
+          <a-tooltip :title="$t('message.volume.state.primary.storage.suitability')" placement="top">
+            <info-circle-outlined class="table-tooltip-icon" />
+          </a-tooltip>
+        </template>
       </template>
-      <template #name="{ record }">
-        {{ record.name }}
-        <a-tooltip v-if="record.name === $t('label.auto.assign')" :title="$t('message.migrate.volume.pool.auto.assign')" placement="top">
-          <info-circle-outlined class="table-tooltip-icon" />
-        </a-tooltip>
-      </template>
-      <template #suitability="{ record }">
-        <check-circle-two-tone
-          class="host-item__suitability-icon"
-          twoToneColor="#52c41a"
-          v-if="record.suitableformigration" />
-        <close-circle-two-tone
-          class="host-item__suitability-icon"
-          twoToneColor="#f5222d"
-          v-else />
-      </template>
-      <template #disksizetotal="{ record }">
-        <span v-if="record.disksizetotal">{{ $bytesToHumanReadableSize(record.disksizetotal) }}</span>
-      </template>
-      <template #disksizeused="{ record }">
-        <span v-if="record.disksizeused">{{ $bytesToHumanReadableSize(record.disksizeused) }}</span>
-      </template>
-      <template #disksizefree="{ record }">
-        <span v-if="record.disksizetotal && record.disksizeused">{{ $bytesToHumanReadableSize(record.disksizetotal * 1 - record.disksizeused * 1) }}</span>
-      </template>
-      <template #select="{ record }">
-        <a-tooltip placement="top" :title="record.state !== 'Up' ? $t('message.primary.storage.invalid.state') : ''">
-          <a-radio
-            :disabled="record.id !== -1 && record.state !== 'Up'"
-            @click="updateSelection(record)"
-            :checked="selectedStoragePool != null && record.id === selectedStoragePool.id">
-          </a-radio>
-        </a-tooltip>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          {{ record.name }}
+          <a-tooltip v-if="record.name === $t('label.auto.assign')" :title="$t('message.migrate.volume.pool.auto.assign')" placement="top">
+            <info-circle-outlined class="table-tooltip-icon" />
+          </a-tooltip>
+        </template>
+        <template v-if="column.key === 'suitability'">
+          <check-circle-two-tone
+            class="host-item__suitability-icon"
+            twoToneColor="#52c41a"
+            v-if="record.suitableformigration" />
+          <close-circle-two-tone
+            class="host-item__suitability-icon"
+            twoToneColor="#f5222d"
+            v-else />
+        </template>
+        <template v-if="column.key === 'disksizetotal'">
+          <span v-if="record.disksizetotal">{{ $bytesToHumanReadableSize(record.disksizetotal) }}</span>
+        </template>
+        <template v-if="column.key === 'disksizeused'">
+          <span v-if="record.disksizeused">{{ $bytesToHumanReadableSize(record.disksizeused) }}</span>
+        </template>
+        <template v-if="column.key === 'disksizefree'">
+          <span v-if="record.disksizetotal && record.disksizeused">{{ $bytesToHumanReadableSize(record.disksizetotal * 1 - record.disksizeused * 1) }}</span>
+        </template>
+        <template v-if="column.key === 'select'">
+          <a-tooltip placement="top" :title="record.state !== 'Up' ? $t('message.primary.storage.invalid.state') : ''">
+            <a-radio
+              :disabled="record.id !== -1 && record.state !== 'Up'"
+              @click="updateSelection(record)"
+              :checked="selectedStoragePool != null && record.id === selectedStoragePool.id">
+            </a-radio>
+          </a-tooltip>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -132,8 +136,8 @@
       selectedStoragePool: null,
       columns: [
         {
-          title: this.$t('label.storageid'),
-          slots: { customRender: 'name' }
+          key: 'name',
+          title: this.$t('label.storageid')
         },
         {
           title: this.$t('label.clusterid'),
@@ -144,27 +148,27 @@
           dataIndex: 'podname'
         },
         {
-          title: this.$t('label.disksizetotal'),
-          slots: { customRender: 'disksizetotal' }
+          key: 'disksizetotal',
+          title: this.$t('label.disksizetotal')
         },
         {
-          title: this.$t('label.disksizeused'),
-          slots: { customRender: 'disksizeused' }
+          key: 'disksizeused',
+          title: this.$t('label.disksizeused')
         },
         {
-          title: this.$t('label.disksizefree'),
-          slots: { customRender: 'disksizefree' }
+          key: 'disksizefree',
+          title: this.$t('label.disksizefree')
         },
         {
-          title: this.$t('label.select'),
-          slots: { customRender: 'select' }
+          key: 'select',
+          title: this.$t('label.select')
         }
       ]
     }
   },
   created () {
     if (this.suitabilityEnabled) {
-      this.columns.splice(1, 0, { title: 'suitabilityCustomTitle', slots: 'suitability' })
+      this.columns.splice(1, 0, { key: 'suitability' })
     }
     this.preselectStoragePool()
     this.fetchStoragePools()
diff --git a/ui/src/components/view/TreeView.vue b/ui/src/components/view/TreeView.vue
index 47f199b..40cd6f0 100644
--- a/ui/src/components/view/TreeView.vue
+++ b/ui/src/components/view/TreeView.vue
@@ -42,8 +42,9 @@
               @select="onSelect"
               @expand="onExpand"
               :expandedKeys="arrExpand">
-              <template #parent><folder-outlined /></template>
-              <template #leaf><block-outlined /></template>
+              <template #icon="{data}">
+                <block-outlined v-if="data.isLeaf" />
+              </template>
             </a-tree>
           </a-spin>
         </a-card>
@@ -136,6 +137,10 @@
       default () {
         return {}
       }
+    },
+    treeDeletedKey: {
+      type: String,
+      default: null
     }
   },
   provide: function () {
@@ -183,11 +188,11 @@
       this.treeViewData = []
       this.arrExpand = []
       if (!this.loading) {
-        this.treeViewData = this.treeData
-        this.treeVerticalData = this.treeData
+        this.treeViewData = this.treeData.slice()
+        this.treeVerticalData = this.treeData.slice()
 
         if (this.treeViewData.length > 0) {
-          this.oldTreeViewData = this.treeViewData
+          this.oldTreeViewData = this.treeViewData.slice()
           this.rootKey = this.treeViewData[0].key
         }
       }
@@ -288,39 +293,47 @@
             }
           }
 
-          this.onSelectResource()
+          this.handleSelectResource(treeNode.eventKey)
           resolve()
         })
       })
     },
-    onSelectResource () {
-      if (this.treeStore.selected) {
-        this.selectedTreeKey = this.treeStore.selected
-        this.defaultSelected = [this.selectedTreeKey]
+    async handleSelectResource (treeKey) {
+      if (this.treeDeletedKey && this.treeDeletedKey === this.treeStore?.treeSelected?.key) {
+        const treeSelectedKey = this.treeStore.treeSelected.parentdomainid
+        if (treeSelectedKey === this.rootKey) {
+          this.resource = this.treeVerticalData[0]
 
-        const resource = this.treeVerticalData.filter(item => item.id === this.selectedTreeKey)
-        if (resource.length > 0) {
-          this.resource = resource[0]
+          this.selectedTreeKey = treeSelectedKey
+          this.defaultSelected = [treeSelectedKey]
           this.$emit('change-resource', this.resource)
-        } else {
-          const resourceIdx = this.treeVerticalData.findIndex(item => item.id === this.resource.id)
-          const parentIndex = this.treeVerticalData.findIndex(item => item.id === this.resource.parentdomainid)
-          if (resourceIdx !== -1) {
-            this.resource = this.treeVerticalData[resourceIdx]
-          } else if (parentIndex !== 1) {
-            this.resource = this.treeVerticalData[parentIndex]
-          } else {
-            this.resource = this.treeVerticalData[0]
-          }
-          this.selectedTreeKey = this.resource.key
-          this.defaultSelected = [this.selectedTreeKey]
+          await this.setTreeStore(false, false, this.resource)
+          return
+        }
+        const resourceIndex = await this.treeVerticalData.findIndex(item => item.id === treeSelectedKey)
+        if (resourceIndex > -1) {
+          const resource = await this.getDetailResource(treeSelectedKey)
+          this.resource = await this.createResourceData(resource)
+
+          this.selectedTreeKey = treeSelectedKey
+          this.defaultSelected = [treeSelectedKey]
           this.$emit('change-resource', this.resource)
+          await this.setTreeStore(false, false, this.resource)
+          return
         }
       }
+      const treeSelectedKey = this.treeStore.treeSelected.key
+      const resourceIndex = await this.treeVerticalData.findIndex(item => item.id === treeSelectedKey)
+      if (resourceIndex > -1) {
+        this.selectedTreeKey = treeSelectedKey
+        this.defaultSelected = [treeSelectedKey]
+
+        this.resource = this.treeStore.treeSelected
+        this.$emit('change-resource', this.resource)
+      }
     },
-    onSelect (selectedKeys, event) {
+    async onSelect (selectedKeys, event) {
       if (!event.selected) {
-        setTimeout(() => { event.node.$refs.selectHandle.click() })
         return
       }
 
@@ -331,20 +344,20 @@
 
       this.defaultSelected = []
       this.defaultSelected.push(this.selectedTreeKey)
+      const resource = await this.getDetailResource(this.selectedTreeKey)
+      this.resource = await this.createResourceData(resource)
+      const index = this.treeVerticalData.findIndex(item => item.key === this.selectedTreeKey)
+      this.treeVerticalData[index] = this.resource
 
-      const treeStore = this.treeStore
-      treeStore.expands = this.arrExpand
-      treeStore.selected = this.selectedTreeKey
-      this.$emit('change-tree-store', this.treeStore)
-
-      this.getDetailResource(this.selectedTreeKey)
+      this.$emit('change-resource', this.resource)
+      await this.setTreeStore(false, false, this.resource)
     },
     onExpand (treeExpand) {
       const treeStore = this.treeStore
       this.arrExpand = treeExpand
       treeStore.isExpand = true
       treeStore.expands = this.arrExpand
-      treeStore.selected = this.selectedTreeKey
+      treeStore.treeSelected = this.resource
       this.$emit('change-tree-store', treeStore)
     },
     onSearch (value) {
@@ -416,40 +429,35 @@
     onTabChange (key) {
       this.tabActive = key
     },
+    setTreeStore (arrExpand, isExpand, resource) {
+      const treeStore = this.treeStore
+      if (arrExpand) treeStore.expands = arrExpand
+      if (isExpand) treeStore.isExpand = true
+      if (resource) treeStore.treeSelected = resource
+      this.$emit('change-tree-store', treeStore)
+    },
     getDetailResource (selectedKey) {
-      // set api name and parameter
-      const apiName = this.$route.meta.permission[0]
-      const params = {}
+      return new Promise(resolve => {
+        // set api name and parameter
+        const apiName = this.$route.meta.permission[0]
+        const params = {}
 
-      // set id to parameter
-      params.id = selectedKey
-      params.listAll = true
-      params.showicon = true
-      params.page = 1
-      params.pageSize = 1
+        // set id to parameter
+        params.id = selectedKey
+        params.listAll = true
+        params.showicon = true
+        params.page = 1
+        params.pageSize = 1
 
-      this.detailLoading = true
-      api(apiName, params).then(json => {
-        const jsonResponse = this.getResponseJsonData(json)
-
-        // check json response is empty
-        if (!jsonResponse || jsonResponse.length === 0) {
-          this.resource = []
-        } else {
-          this.resource = jsonResponse[0]
-          this.resource = this.createResourceData(this.resource)
-          // set all value of resource tree data
-          this.treeVerticalData.filter((item, index) => {
-            if (item.id === this.resource.id) {
-              this.treeVerticalData[index] = this.resource
-            }
-          })
-        }
-
-        // emit change resource to parent
-        this.$emit('change-resource', this.resource)
-      }).finally(() => {
-        this.detailLoading = false
+        this.detailLoading = true
+        api(apiName, params).then(json => {
+          const jsonResponse = this.getResponseJsonData(json)
+          resolve(jsonResponse[0])
+        }).catch(() => {
+          resolve()
+        }).finally(() => {
+          this.detailLoading = false
+        })
       })
     },
     getResponseJsonData (json) {
@@ -524,15 +532,13 @@
       })
       resource.title = resource.name
       resource.key = resource.id
-      resource.slots = {
-        icon: 'parent'
+      if (resource?.icon) {
+        resource.resourceIcon = resource.icon
+        delete resource.icon
       }
 
       if (!resource.haschild) {
         resource.isLeaf = true
-        resource.slots = {
-          icon: 'leaf'
-        }
       }
 
       return resource
@@ -586,8 +592,9 @@
 </script>
 
 <style lang="less" scoped>
-.list-tree-view {
+:deep(.ant-tree).list-tree-view {
   overflow-y: hidden;
+  margin-top: 10px;
 }
 :deep(.ant-tree).ant-tree-directory {
   li.ant-tree-treenode-selected {
@@ -615,7 +622,7 @@
   }
 }
 
-:deep(.ant-tree) li span.ant-tree-switcher.ant-tree-switcher-noop {
+:deep(.ant-tree-show-line) .ant-tree-switcher.ant-tree-switcher-noop {
   display: none
 }
 
@@ -626,10 +633,38 @@
 
 :deep(.ant-tree-icon__customize) {
   padding-right: 5px;
+  background: transparent;
 }
 
-:deep(.ant-tree) li .ant-tree-node-content-wrapper {
-  padding-left: 0;
-  margin-left: 3px;
+:deep(.ant-tree) {
+  .ant-tree-treenode {
+    .ant-tree-node-content-wrapper {
+      margin-left: 0;
+      margin-bottom: 2px;
+
+      &:before {
+        border-left: 1px solid #d9d9d9;
+        content: " ";
+        height: 100%;
+        height: calc(100% - 15px);
+        left: 12px;
+        margin: 22px 0 0;
+        position: absolute;
+        width: 1px;
+        top: 0;
+      }
+    }
+
+    &.ant-tree-treenode-switcher-close, &.ant-tree-treenode-switcher-open, &.ant-tree-treenode-leaf-last {
+      .ant-tree-node-content-wrapper:before {
+        display: none;
+      }
+    }
+  }
+}
+
+:deep(.ant-tree-show-line) .ant-tree-indent-unit:before {
+  bottom: -2px;
+  top: -5px;
 }
 </style>
diff --git a/ui/src/components/view/VolumesTab.vue b/ui/src/components/view/VolumesTab.vue
index 939f708..e9165b1 100644
--- a/ui/src/components/view/VolumesTab.vue
+++ b/ui/src/components/view/VolumesTab.vue
@@ -24,20 +24,22 @@
     :rowKey="item => item.id"
     :pagination="false"
   >
-    <template #name="{ text, record }">
-      <hdd-outlined style="margin-right: 5px"/>
-      <router-link :to="{ path: '/volume/' + record.id }" style="margin-right: 5px">
-        {{ text }}
-      </router-link>
-      <a-tag v-if="record.provisioningtype">
-        {{  record.provisioningtype  }}
-      </a-tag>
-    </template>
-    <template #state="{ text }">
-      <status :text="text ? text : ''" />{{ text }}
-    </template>
-    <template #size="{ record }">
-      {{ parseFloat(record.size / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GB
+    <template #bodyCell="{ column, text, record }">
+      <template v-if="column.key === 'name'">
+        <hdd-outlined style="margin-right: 5px"/>
+        <router-link :to="{ path: '/volume/' + record.id }" style="margin-right: 5px">
+          {{ text }}
+        </router-link>
+        <a-tag v-if="record.provisioningtype">
+          {{  record.provisioningtype  }}
+        </a-tag>
+      </template>
+      <template v-if="column.key === 'state'">
+        <status :text="text ? text : ''" />{{ text }}
+      </template>
+      <template v-if="column.key === 'size'">
+        {{ parseFloat(record.size / (1024.0 * 1024.0 * 1024.0)).toFixed(2) }} GB
+      </template>
     </template>
   </a-table>
 </template>
@@ -68,23 +70,23 @@
       volumes: [],
       volumeColumns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.type'),
           dataIndex: 'type'
         },
         {
+          key: 'size',
           title: this.$t('label.size'),
-          dataIndex: 'size',
-          slots: { customRender: 'size' }
+          dataIndex: 'size'
         }
       ]
     }
@@ -92,7 +94,6 @@
   created () {
     this.vm = this.resource
     this.fetchData()
-    console.log(this.resource.volumes)
   },
   watch: {
     resource: function (newItem) {
diff --git a/ui/src/components/widgets/Breadcrumb.vue b/ui/src/components/widgets/Breadcrumb.vue
index 097a922..147e779 100644
--- a/ui/src/components/widgets/Breadcrumb.vue
+++ b/ui/src/components/widgets/Breadcrumb.vue
@@ -40,7 +40,7 @@
           </span>
         </label>
         <label v-else>
-          {{ resource.displayname || resource.name || resource.displaytext || resource.hostname || resource.username || resource.ipaddress || $route.params.id }}
+          {{ resource.displayname || resource.name || resource.displaytext || resource.hostname || resource.username || resource.ipaddress || resource.osname || resource.osdisplayname || $route.params.id }}
         </label>
       </span>
       <span v-else>
diff --git a/ui/src/components/widgets/Console.vue b/ui/src/components/widgets/Console.vue
index 03a37a2..d73753c 100644
--- a/ui/src/components/widgets/Console.vue
+++ b/ui/src/components/widgets/Console.vue
@@ -20,7 +20,8 @@
     v-if="['vm', 'systemvm', 'router', 'ilbvm'].includes($route.meta.name) && 'listVirtualMachines' in $store.getters.apis && 'createConsoleEndpoint' in $store.getters.apis"
     @click="consoleUrl">
     <a-button style="margin-left: 5px" shape="circle" type="dashed" :size="size" :disabled="['Stopped', 'Error', 'Destroyed'].includes(resource.state) || resource.hostcontrolstate === 'Offline'" >
-      <code-outlined />
+      <code-outlined v-if="!copyUrlToClipboard"/>
+      <copy-outlined v-else />
     </a-button>
   </a>
 </template>
@@ -39,7 +40,8 @@
     size: {
       type: String,
       default: 'small'
-    }
+    },
+    copyUrlToClipboard: Boolean
   },
   data () {
     return {
@@ -53,7 +55,21 @@
       api('createConsoleEndpoint', params).then(json => {
         this.url = (json && json.createconsoleendpointresponse) ? json.createconsoleendpointresponse.consoleendpoint.url : '#/exception/404'
         if (json.createconsoleendpointresponse.consoleendpoint.success) {
-          window.open(this.url, '_blank')
+          if (this.copyUrlToClipboard) {
+            this.$message.success({
+              content: this.$t('label.copied.clipboard')
+            })
+            const hiddenElement = document.createElement('textarea')
+            hiddenElement.value = this.url
+            document.body.appendChild(hiddenElement)
+            hiddenElement.focus()
+            hiddenElement.select()
+
+            document.execCommand('copy')
+            document.body.removeChild(hiddenElement)
+          } else {
+            window.open(this.url, '_blank')
+          }
         } else {
           this.$notification.error({
             message: this.$t('error.execute.api.failed') + ' ' + 'createConsoleEndpoint',
diff --git a/ui/src/components/widgets/Status.vue b/ui/src/components/widgets/Status.vue
index 6243882..6575d40 100644
--- a/ui/src/components/widgets/Status.vue
+++ b/ui/src/components/widgets/Status.vue
@@ -16,14 +16,16 @@
 // under the License.
 
 <template>
-  <a-tooltip placement="bottom" :title="getTooltip(text)">
-    <a-badge
-      :style="getStyle()"
-      :title="text"
-      :color="getStatusColor(text)"
-      :status="getBadgeStatus(text)"
-      :text="getText()" />
-  </a-tooltip>
+  <div style="display: inline-flex;">
+    <a-tooltip placement="bottom" :title="getTooltip(text)">
+      <a-badge
+        :style="getStyle()"
+        :title="text"
+        :color="getStatusColor(text)"
+        :status="getBadgeStatus(text)"
+        :text="getText()" />
+    </a-tooltip>
+  </div>
 </template>
 
 <script>
diff --git a/ui/src/config/section/account.js b/ui/src/config/section/account.js
index 21878c1..3141b96 100644
--- a/ui/src/config/section/account.js
+++ b/ui/src/config/section/account.js
@@ -199,9 +199,8 @@
       label: 'label.action.delete.account',
       message: 'message.delete.account',
       dataView: true,
-      show: (record, store) => {
-        return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
-          !(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1)
+      disabled: (record, store) => {
+        return record.id !== 'undefined' && store.userInfo.accountid === record.id
       },
       groupAction: true,
       popup: true,
diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index 5393e8b..058c41e 100644
--- a/ui/src/config/section/compute.js
+++ b/ui/src/config/section/compute.js
@@ -447,7 +447,7 @@
       docHelp: 'plugins/cloudstack-kubernetes-service.html',
       permission: ['listKubernetesClusters'],
       columns: (store) => {
-        var fields = ['name', 'state', 'size', 'cpunumber', 'memory']
+        var fields = ['name', 'state', 'clustertype', 'size', 'cpunumber', 'memory']
         if (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) {
           fields.push('account')
         }
@@ -457,7 +457,11 @@
         fields.push('zonename')
         return fields
       },
-      details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'cpunumber', 'memory', 'keypair', 'associatednetworkname', 'account', 'domain', 'zonename', 'created'],
+      filters: () => {
+        const filters = ['cloud.managed', 'external.managed']
+        return filters
+      },
+      details: ['name', 'description', 'zonename', 'kubernetesversionname', 'autoscalingenabled', 'minsize', 'maxsize', 'size', 'controlnodes', 'cpunumber', 'memory', 'keypair', 'associatednetworkname', 'account', 'domain', 'zonename', 'clustertype', 'created'],
       tabs: [{
         name: 'k8s',
         component: shallowRef(defineAsyncComponent(() => import('@/views/compute/KubernetesServiceTab.vue')))
@@ -480,7 +484,7 @@
           message: 'message.kubernetes.cluster.start',
           docHelp: 'plugins/cloudstack-kubernetes-service.html#starting-a-stopped-kubernetes-cluster',
           dataView: true,
-          show: (record) => { return ['Stopped'].includes(record.state) },
+          show: (record) => { return ['Stopped'].includes(record.state) && record.clustertype === 'CloudManaged' },
           groupAction: true,
           popup: true,
           groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
@@ -492,7 +496,7 @@
           message: 'message.kubernetes.cluster.stop',
           docHelp: 'plugins/cloudstack-kubernetes-service.html#stopping-kubernetes-cluster',
           dataView: true,
-          show: (record) => { return !['Stopped', 'Destroyed', 'Destroying'].includes(record.state) },
+          show: (record) => { return !['Stopped', 'Destroyed', 'Destroying'].includes(record.state) && record.clustertype === 'CloudManaged' },
           groupAction: true,
           popup: true,
           groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
@@ -504,7 +508,7 @@
           message: 'message.kubernetes.cluster.scale',
           docHelp: 'plugins/cloudstack-kubernetes-service.html#scaling-kubernetes-cluster',
           dataView: true,
-          show: (record) => { return ['Created', 'Running', 'Stopped'].includes(record.state) },
+          show: (record) => { return ['Created', 'Running', 'Stopped'].includes(record.state) && record.clustertype === 'CloudManaged' },
           popup: true,
           component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleKubernetesCluster.vue')))
         },
@@ -515,7 +519,7 @@
           message: 'message.kubernetes.cluster.upgrade',
           docHelp: 'plugins/cloudstack-kubernetes-service.html#upgrading-kubernetes-cluster',
           dataView: true,
-          show: (record) => { return ['Created', 'Running'].includes(record.state) },
+          show: (record) => { return ['Created', 'Running'].includes(record.state) && record.clustertype === 'CloudManaged' },
           popup: true,
           component: shallowRef(defineAsyncComponent(() => import('@/views/compute/UpgradeKubernetesCluster.vue')))
         },
@@ -529,7 +533,11 @@
           show: (record) => { return !['Destroyed', 'Destroying'].includes(record.state) },
           groupAction: true,
           popup: true,
-          groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
+          args: (record, store, group) => {
+            return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervm)
+              ? ['cleanup', 'expunge'] : ['cleanup']
+          },
+          groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge, cleanup: values.cleanup } }) }
         }
       ]
     },
diff --git a/ui/src/config/section/config.js b/ui/src/config/section/config.js
index c7e0972..3cddc63 100644
--- a/ui/src/config/section/config.js
+++ b/ui/src/config/section/config.js
@@ -25,6 +25,7 @@
       name: 'globalsetting',
       title: 'label.global.settings',
       icon: 'setting-outlined',
+      docHelp: 'adminguide/index.html#tuning',
       permission: ['listConfigurations'],
       listView: true,
       popup: true,
@@ -34,6 +35,7 @@
       name: 'ldapsetting',
       title: 'label.ldap.configuration',
       icon: 'team-outlined',
+      docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication',
       permission: ['listLdapConfigurations'],
       columns: ['hostname', 'port', 'domainid'],
       details: ['hostname', 'port', 'domainid'],
@@ -72,6 +74,7 @@
       name: 'hypervisorcapability',
       title: 'label.hypervisor.capabilities',
       icon: 'database-outlined',
+      docHelp: 'adminguide/hosts.html?highlight=Hypervisor%20capabilities#hypervisor-capabilities',
       permission: ['listHypervisorCapabilities'],
       columns: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxhostspercluster'],
       details: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxdatavolumeslimit', 'maxhostspercluster', 'securitygroupenabled', 'storagemotionenabled'],
@@ -84,6 +87,102 @@
           args: ['maxguestslimit']
         }
       ]
+    },
+    {
+      name: 'guestos',
+      title: 'label.guest.os',
+      docHelp: 'adminguide/guest_os.html#guest-os',
+      icon: 'laptop-outlined',
+      permission: ['listOsTypes', 'listOsCategories'],
+      columns: ['name', 'oscategoryname', 'isuserdefined'],
+      details: ['name', 'oscategoryname', 'isuserdefined'],
+      related: [{
+        name: 'guestoshypervisormapping',
+        title: 'label.guest.os.hypervisor.mappings',
+        param: 'ostypeid'
+      }],
+      actions: [
+        {
+          api: 'addGuestOs',
+          icon: 'plus-outlined',
+          label: 'label.add.guest.os',
+          listView: true,
+          dataView: false,
+          args: ['osdisplayname', 'oscategoryid'],
+          mapping: {
+            oscategoryid: {
+              api: 'listOsCategories',
+              params: (record) => { return { oscategoryid: record.id } }
+            }
+          }
+        },
+        {
+          api: 'updateGuestOs',
+          icon: 'edit-outlined',
+          label: 'label.edit',
+          dataView: true,
+          popup: true,
+          args: ['osdisplayname']
+        },
+        {
+          api: 'addGuestOsMapping',
+          icon: 'link-outlined',
+          label: 'label.add.guest.os.hypervisor.mapping',
+          dataView: true,
+          popup: true,
+          args: ['ostypeid', 'hypervisor', 'hypervisorversion', 'osnameforhypervisor', 'osmappingcheckenabled', 'forced'],
+          mapping: {
+            ostypeid: {
+              value: (record) => { return record.id }
+            }
+          }
+        },
+        {
+          api: 'removeGuestOs',
+          icon: 'delete-outlined',
+          label: 'label.action.delete.guest.os',
+          message: 'message.action.delete.guest.os',
+          dataView: true,
+          popup: true
+        }
+      ]
+    },
+    {
+      name: 'guestoshypervisormapping',
+      title: 'label.guest.os.hypervisor.mappings',
+      docHelp: 'adminguide/guest_os.html#guest-os-hypervisor-mapping',
+      icon: 'api-outlined',
+      permission: ['listGuestOsMapping'],
+      columns: ['hypervisor', 'hypervisorversion', 'osdisplayname', 'osnameforhypervisor'],
+      details: ['hypervisor', 'hypervisorversion', 'osdisplayname', 'osnameforhypervisor', 'isuserdefined'],
+      filters: ['all', 'kvm', 'vmware', 'xenserver', 'lxc', 'ovm3'],
+      searchFilters: ['osdisplayname', 'osnameforhypervisor', 'hypervisor', 'hypervisorversion'],
+      actions: [
+        {
+          api: 'addGuestOsMapping',
+          icon: 'plus-outlined',
+          label: 'label.add.guest.os.hypervisor.mapping',
+          listView: true,
+          dataView: false,
+          args: ['ostypeid', 'hypervisor', 'hypervisorversion', 'osnameforhypervisor', 'osmappingcheckenabled', 'forced']
+        },
+        {
+          api: 'updateGuestOsMapping',
+          icon: 'edit-outlined',
+          label: 'label.edit',
+          dataView: true,
+          popup: true,
+          args: ['osnameforhypervisor', 'osmappingcheckenabled']
+        },
+        {
+          api: 'removeGuestOsMapping',
+          icon: 'delete-outlined',
+          label: 'label.action.delete.guest.os.hypervisor.mapping',
+          message: 'message.action.delete.guest.os.hypervisor.mapping',
+          dataView: true,
+          popup: true
+        }
+      ]
     }
   ]
 }
diff --git a/ui/src/config/section/event.js b/ui/src/config/section/event.js
index 4673a64..5f4b27b 100644
--- a/ui/src/config/section/event.js
+++ b/ui/src/config/section/event.js
@@ -29,6 +29,9 @@
     title: 'label.event.timeline',
     param: 'startid'
   }],
+  filters: () => {
+    return ['active', 'archived']
+  },
   actions: [
     {
       api: 'archiveEvents',
@@ -45,6 +48,12 @@
         ids: {
           value: (record) => { return record.id }
         }
+      },
+      show: (record) => {
+        return !(record.archived)
+      },
+      groupShow: (selectedItems) => {
+        return selectedItems.filter(x => { return !(x.archived) }).length > 0
       }
     },
     {
diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js
index c95e0b9..049ec15 100644
--- a/ui/src/config/section/image.js
+++ b/ui/src/config/section/image.js
@@ -93,6 +93,7 @@
           api: 'registerTemplate',
           icon: 'cloud-upload-outlined',
           label: 'label.upload.template.from.local',
+          show: () => { return 'getUploadParamsForTemplate' in store.getters.apis },
           docHelp: 'adminguide/templates.html#uploading-templates-and-isos-from-a-local-computer',
           listView: true,
           popup: true,
@@ -233,6 +234,7 @@
           api: 'registerIso',
           icon: 'cloud-upload-outlined',
           label: 'label.upload.iso.from.local',
+          show: () => { return 'getUploadParamsForIso' in store.getters.apis },
           docHelp: 'adminguide/templates.html#id10',
           listView: true,
           popup: true,
diff --git a/ui/src/config/section/infra/clusters.js b/ui/src/config/section/infra/clusters.js
index 904938c..ca6dde7 100644
--- a/ui/src/config/section/infra/clusters.js
+++ b/ui/src/config/section/infra/clusters.js
@@ -22,6 +22,7 @@
   name: 'cluster',
   title: 'label.clusters',
   icon: 'cluster-outlined',
+  docHelp: 'conceptsandterminology/concepts.html#about-clusters',
   permission: ['listClustersMetrics'],
   columns: () => {
     const fields = ['name', 'state', 'allocationstate', 'clustertype', 'hypervisortype', 'hosts']
diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js
index 9f2c629..60f30e3 100644
--- a/ui/src/config/section/infra/hosts.js
+++ b/ui/src/config/section/infra/hosts.js
@@ -22,6 +22,7 @@
   name: 'host',
   title: 'label.hosts',
   icon: 'desktop-outlined',
+  docHelp: 'conceptsandterminology/concepts.html#about-hosts',
   permission: ['listHostsMetrics'],
   resourceType: 'Host',
   filters: () => {
@@ -102,8 +103,9 @@
       label: 'label.disable.host',
       message: 'message.confirm.disable.host',
       dataView: true,
-      defaultArgs: { allocationstate: 'Disable' },
-      show: (record) => { return record.resourcestate === 'Enabled' }
+      show: (record) => { return record.resourcestate === 'Enabled' },
+      popup: true,
+      component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable')))
     },
     {
       api: 'updateHost',
@@ -111,8 +113,9 @@
       label: 'label.enable.host',
       message: 'message.confirm.enable.host',
       dataView: true,
-      defaultArgs: { allocationstate: 'Enable' },
-      show: (record) => { return record.resourcestate === 'Disabled' }
+      show: (record) => { return record.resourcestate === 'Disabled' },
+      popup: true,
+      component: shallowRef(defineAsyncComponent(() => import('@/views/infra/HostEnableDisable')))
     },
     {
       api: 'prepareHostForMaintenance',
diff --git a/ui/src/config/section/infra/ilbvms.js b/ui/src/config/section/infra/ilbvms.js
index beba33e..fa20d69 100644
--- a/ui/src/config/section/infra/ilbvms.js
+++ b/ui/src/config/section/infra/ilbvms.js
@@ -20,6 +20,7 @@
   name: 'ilbvm',
   title: 'label.internal.lb',
   icon: 'share-alt-outlined',
+  docHelp: 'adminguide/networking_and_traffic.html#creating-an-internal-lb-rule',
   permission: ['listInternalLoadBalancerVMs'],
   params: { projectid: '-1' },
   columns: ['name', 'state', 'publicip', 'guestnetworkname', 'vpcname', 'version', 'softwareversion', 'hostname', 'account', 'zonename', 'requiresupgrade'],
diff --git a/ui/src/config/section/infra/managementServers.js b/ui/src/config/section/infra/managementServers.js
index 6059af9..d1dfa6d 100644
--- a/ui/src/config/section/infra/managementServers.js
+++ b/ui/src/config/section/infra/managementServers.js
@@ -22,7 +22,9 @@
   name: 'managementserver',
   title: 'label.management.servers',
   icon: 'CloudServerOutlined',
+  docHelp: 'conceptsandterminology/concepts.html#management-server-overview',
   permission: ['listManagementServersMetrics'],
+  resourceType: 'ManagementServer',
   columns: () => {
     const fields = ['name', 'state', 'version']
     const metricsFields = ['collectiontime', 'availableprocessors', 'cpuload', 'heapmemoryused', 'agentcount']
@@ -36,6 +38,53 @@
     {
       name: 'details',
       component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
+    },
+    {
+      name: 'pending.jobs',
+      component: shallowRef(defineAsyncComponent(() => import('@/views/infra/AsyncJobsTab.vue')))
+    },
+    {
+      name: 'comments',
+      component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue')))
+    }
+  ],
+  actions: [
+    {
+      api: 'prepareForShutdown',
+      icon: 'exclamation-circle-outlined',
+      label: 'label.prepare.for.shutdown',
+      message: 'message.prepare.for.shutdown',
+      dataView: true,
+      popup: true,
+      confirmationText: 'SHUTDOWN',
+      show: (record, store) => { return record.state === 'Up' },
+      component: shallowRef(defineAsyncComponent(() => import('@/views/infra/Confirmation.vue')))
+    },
+    {
+      api: 'triggerShutdown',
+      icon: 'poweroff-outlined',
+      label: 'label.trigger.shutdown',
+      message: 'message.trigger.shutdown',
+      dataView: true,
+      popup: true,
+      confirmationText: 'SHUTDOWN',
+      show: (record, store) => { return ['Up', 'PreparingToShutDown', 'ReadyToShutDown'].includes(record.state) },
+      component: shallowRef(defineAsyncComponent(() => import('@/views/infra/Confirmation.vue')))
+    },
+    {
+      api: 'cancelShutdown',
+      icon: 'close-circle-outlined',
+      label: 'label.cancel.shutdown',
+      message: 'message.cancel.shutdown',
+      docHelp: 'installguide/configuration.html#adding-a-zone',
+      dataView: true,
+      popup: true,
+      show: (record, store) => { return ['PreparingToShutDown', 'ReadyToShutDown', 'ShuttingDown'].includes(record.state) },
+      mapping: {
+        managementserverid: {
+          value: (record, params) => { return record.id }
+        }
+      }
     }
   ]
 }
diff --git a/ui/src/config/section/infra/pods.js b/ui/src/config/section/infra/pods.js
index a462478..eff62e0 100644
--- a/ui/src/config/section/infra/pods.js
+++ b/ui/src/config/section/infra/pods.js
@@ -22,6 +22,7 @@
   name: 'pod',
   title: 'label.pods',
   icon: 'appstore-outlined',
+  docHelp: 'conceptsandterminology/concepts.html#about-pods',
   permission: ['listPods'],
   columns: ['name', 'allocationstate', 'gateway', 'netmask', 'zonename'],
   details: ['name', 'id', 'allocationstate', 'netmask', 'gateway', 'zonename'],
diff --git a/ui/src/config/section/infra/zones.js b/ui/src/config/section/infra/zones.js
index cc9fa16..b2768f7 100644
--- a/ui/src/config/section/infra/zones.js
+++ b/ui/src/config/section/infra/zones.js
@@ -22,6 +22,7 @@
   name: 'zone',
   title: 'label.zones',
   icon: 'global-outlined',
+  docHelp: 'conceptsandterminology/concepts.html#about-zones',
   permission: ['listZonesMetrics'],
   columns: () => {
     const fields = ['name', 'allocationstate', 'type', 'networktype', 'clusters']
diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js
index 2f4b5b0..26cd469 100644
--- a/ui/src/config/section/network.js
+++ b/ui/src/config/section/network.js
@@ -30,6 +30,7 @@
       name: 'guestnetwork',
       title: 'label.guest.networks',
       icon: 'apartment-outlined',
+      docHelp: 'adminguide/networking_and_traffic.html#adding-an-additional-guest-network',
       permission: ['listNetworks'],
       resourceType: 'Network',
       columns: () => {
@@ -129,6 +130,7 @@
           icon: 'edit-outlined',
           label: 'label.update.network',
           dataView: true,
+          disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
           popup: true,
           component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
         },
@@ -138,6 +140,7 @@
           label: 'label.restart.network',
           message: 'message.restart.network',
           dataView: true,
+          disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
           args: (record, store, isGroupAction) => {
             var fields = []
             if (isGroupAction || record.vpcid == null) {
@@ -176,6 +179,7 @@
           label: 'label.action.delete.network',
           message: 'message.action.delete.network',
           dataView: true,
+          disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
           groupAction: true,
           popup: true,
           groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
@@ -224,7 +228,7 @@
           icon: 'edit-outlined',
           label: 'label.edit',
           dataView: true,
-          args: ['name', 'displaytext', 'publicmtu']
+          args: ['name', 'displaytext', 'publicmtu', 'sourcenatipaddress']
         },
         {
           api: 'restartVPC',
@@ -319,7 +323,7 @@
       docHelp: 'adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts',
       permission: ['listPublicIpAddresses'],
       resourceType: 'PublicIpAddress',
-      columns: ['ipaddress', 'state', 'associatednetworkname', 'virtualmachinename', 'allocated', 'account', 'zonename'],
+      columns: ['ipaddress', 'state', 'associatednetworkname', 'vpcname', 'virtualmachinename', 'allocated', 'account', 'zonename'],
       details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'zonename'],
       filters: ['allocated', 'reserved', 'free'],
       component: shallowRef(() => import('@/views/network/PublicIpResource.vue')),
@@ -905,6 +909,7 @@
       name: 'guestvlans',
       title: 'label.guest.vlan',
       icon: 'folder-outlined',
+      docHelp: 'conceptsandterminology/network_setup.html#vlan-allocation-example',
       permission: ['listGuestVlans'],
       resourceType: 'GuestVlan',
       filters: ['allocatedonly', 'all'],
diff --git a/ui/src/config/section/project.js b/ui/src/config/section/project.js
index a9bfb95..adbfac7 100644
--- a/ui/src/config/section/project.js
+++ b/ui/src/config/section/project.js
@@ -101,7 +101,7 @@
       icon: 'edit-outlined',
       label: 'label.edit.project.details',
       dataView: true,
-      args: ['displaytext'],
+      args: ['name', 'displaytext'],
       show: (record, store) => {
         return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) || record.isCurrentUserProjectAdmin
       }
diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js
index e8a5ecd..bec5404 100644
--- a/ui/src/config/section/storage.js
+++ b/ui/src/config/section/storage.js
@@ -108,6 +108,7 @@
           icon: 'cloud-upload-outlined',
           docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine',
           label: 'label.upload.volume.from.local',
+          show: () => { return 'getUploadParamsForVolume' in store.getters.apis },
           listView: true,
           popup: true,
           component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UploadLocalVolume.vue')))
diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js
index 894e4a6..eef9ea3 100644
--- a/ui/src/config/section/user.js
+++ b/ui/src/config/section/user.js
@@ -68,6 +68,7 @@
       api: 'registerUserKeys',
       icon: 'file-protect-outlined',
       label: 'label.action.generate.keys',
+      hoverLabel: 'label.action.generate.api.secret.keys',
       message: 'message.generate.keys',
       dataView: true
     },
@@ -144,9 +145,8 @@
       label: 'label.action.delete.user',
       message: 'message.delete.user',
       dataView: true,
-      show: (record, store) => {
-        return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
-          !(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1)
+      disabled: (record, store) => {
+        return record.id !== 'undefined' && store.userInfo.id === record.id
       }
     }
   ]
diff --git a/ui/src/core/lazy_lib/components_use.js b/ui/src/core/lazy_lib/components_use.js
index 10790d6..1c2e8ee 100644
--- a/ui/src/core/lazy_lib/components_use.js
+++ b/ui/src/core/lazy_lib/components_use.js
@@ -68,6 +68,9 @@
 import VueClipboard from 'vue3-clipboard'
 import VueCropper from 'vue-cropper'
 
+import cronAnt from '@vue-js-cron/ant'
+import '@vue-js-cron/ant/dist/ant.css'
+
 export default {
   install: (app) => {
     app.config.globalProperties.$confirm = Modal.confirm
@@ -77,6 +80,7 @@
     app.config.globalProperties.$error = Modal.error
     app.config.globalProperties.$warning = Modal.warning
 
+    app.use(cronAnt)
     app.use(VueClipboard, { autoSetContainer: true })
     app.use(VueCropper)
     app.use(ConfigProvider)
diff --git a/ui/src/core/lazy_lib/icons_use.js b/ui/src/core/lazy_lib/icons_use.js
index ec2d67d..0a95fac 100644
--- a/ui/src/core/lazy_lib/icons_use.js
+++ b/ui/src/core/lazy_lib/icons_use.js
@@ -16,6 +16,7 @@
 // under the License.
 
 import {
+  AimOutlined,
   ApartmentOutlined,
   ApiOutlined,
   AppstoreOutlined,
@@ -118,6 +119,7 @@
   MoreOutlined,
   NotificationOutlined,
   NumberOutlined,
+  LaptopOutlined,
   OrderedListOutlined,
   PaperClipOutlined,
   PauseCircleOutlined,
@@ -170,6 +172,7 @@
 
 export default {
   install: (app) => {
+    app.component('AimOutlined', AimOutlined)
     app.component('ApartmentOutlined', ApartmentOutlined)
     app.component('ApiOutlined', ApiOutlined)
     app.component('AppstoreOutlined', AppstoreOutlined)
@@ -272,6 +275,7 @@
     app.component('MoreOutlined', MoreOutlined)
     app.component('NotificationOutlined', NotificationOutlined)
     app.component('NumberOutlined', NumberOutlined)
+    app.component('LaptopOutlined', LaptopOutlined)
     app.component('OrderedListOutlined', OrderedListOutlined)
     app.component('PaperClipOutlined', PaperClipOutlined)
     app.component('PauseCircleOutlined', PauseCircleOutlined)
diff --git a/ui/src/layouts/UserLayout.vue b/ui/src/layouts/UserLayout.vue
index 24ed6b4..6c81c85 100644
--- a/ui/src/layouts/UserLayout.vue
+++ b/ui/src/layouts/UserLayout.vue
@@ -46,6 +46,7 @@
 <script>
 import RouteView from '@/layouts/RouteView'
 import { mixinDevice } from '@/utils/mixin.js'
+import notification from 'ant-design-vue/es/notification'
 
 export default {
   name: 'UserLayout',
@@ -90,7 +91,7 @@
   },
   methods: {
     onClearNotification () {
-      this.$notification.destroy()
+      notification.destroy()
       this.$store.commit('SET_COUNT_NOTIFY', 0)
     }
   }
diff --git a/ui/src/permission.js b/ui/src/permission.js
index ec6a816..b780073 100644
--- a/ui/src/permission.js
+++ b/ui/src/permission.js
@@ -26,7 +26,7 @@
 import message from 'ant-design-vue/es/message'
 import notification from 'ant-design-vue/es/notification'
 import { setDocumentTitle } from '@/utils/domUtil'
-import { ACCESS_TOKEN, APIS, SERVER_MANAGER } from '@/store/mutation-types'
+import { ACCESS_TOKEN, APIS, SERVER_MANAGER, CURRENT_PROJECT } from '@/store/mutation-types'
 
 NProgress.configure({ showSpinner: false }) // NProgress Configuration
 
@@ -104,7 +104,8 @@
               } else {
                 next({ path: redirect })
               }
-              store.dispatch('ToggleTheme', 'light')
+              const project = vueProps.$localStorage.get(CURRENT_PROJECT)
+              store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
             })
           })
           .catch(() => {
diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js
index f6a6dab..d795e33 100644
--- a/ui/src/store/getters.js
+++ b/ui/src/store/getters.js
@@ -44,6 +44,7 @@
   countNotify: state => state.user.countNotify,
   customColumns: state => state.user.customColumns,
   logoutFlag: state => state.user.logoutFlag,
+  shutdownTriggered: state => state.user.shutdownTriggered,
   twoFaEnabled: state => state.user.twoFaEnabled,
   twoFaProvider: state => state.user.twoFaProvider,
   twoFaIssuer: state => state.user.twoFaIssuer,
diff --git a/ui/src/store/modules/app.js b/ui/src/store/modules/app.js
index 8ed27f8..f8753a1 100644
--- a/ui/src/store/modules/app.js
+++ b/ui/src/store/modules/app.js
@@ -121,6 +121,9 @@
     SET_CUSTOM_COLUMNS: (state, customColumns) => {
       vueProps.$localStorage.set(CUSTOM_COLUMNS, customColumns)
       state.customColumns = customColumns
+    },
+    SET_SHUTDOWN_TRIGGERED: (state, shutdownTriggered) => {
+      state.shutdownTriggered = shutdownTriggered
     }
   },
   actions: {
@@ -177,6 +180,9 @@
     },
     SetCustomColumns ({ commit }, bool) {
       commit('SET_CUSTOM_COLUMNS', bool)
+    },
+    SetShutdownTriggered ({ commit }, bool) {
+      commit('SET_SHUTDOWN_TRIGGERED', bool)
     }
   }
 }
diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js
index 3e9aef7..0513802 100644
--- a/ui/src/store/modules/user.js
+++ b/ui/src/store/modules/user.js
@@ -61,6 +61,7 @@
     loginFlag: false,
     logoutFlag: false,
     customColumns: {},
+    shutdownTriggered: false,
     twoFaEnabled: false,
     twoFaProvider: '',
     twoFaIssuer: ''
@@ -133,6 +134,9 @@
       vueProps.$localStorage.set(CUSTOM_COLUMNS, customColumns)
       state.customColumns = customColumns
     },
+    SET_SHUTDOWN_TRIGGERED: (state, shutdownTriggered) => {
+      state.shutdownTriggered = shutdownTriggered
+    },
     SET_LOGOUT_FLAG: (state, flag) => {
       state.logoutFlag = flag
     },
diff --git a/ui/src/style/dark-mode.less b/ui/src/style/dark-mode.less
index 1b338ea..9da0997 100644
--- a/ui/src/style/dark-mode.less
+++ b/ui/src/style/dark-mode.less
@@ -83,6 +83,7 @@
 
   .sider.light {
     background: @dark-secondary-bgColor;
+    padding-top: 15px;
 
     .logo {
       background-color: @dark-secondary-bgColor;
@@ -139,6 +140,10 @@
         color: @dark-text-color-3;
       }
     }
+
+    &-inline, &-vertical, &-vertical-left {
+      border-color: transparent;
+    }
   }
 
   .kubernet-icon path
@@ -289,6 +294,10 @@
       & > tr > th.ant-table-column-sort {
         background-color: @dark-bgColor;
       }
+
+      & > tr.ant-table-row:hover > td, & > tr > td.ant-table-cell-row-hover {
+        background: transparent;
+      }
     }
 
     &-footer {
@@ -336,6 +345,12 @@
         border-color: @dark-border-color;
       }
     }
+
+    &-small {
+      .ant-table-thead > tr > th {
+        background-color: transparent;
+      }
+    }
   }
 
   .light-row, .dark-row {
@@ -524,13 +539,17 @@
       background-color: #1890ff;
     }
 
-    &-loading-icon, &:after {
+    &-loading-icon, &::before {
       background-color: @dark-secondary-bgColor;
     }
 
     &:disabled {
       background-color: @disabled-bgColor;
     }
+
+    &-handle::before {
+      background-color: @dark-secondary-bgColor;
+    }
   }
 
   .ant-select-dropdown {
@@ -783,7 +802,7 @@
     background-color: @dark-secondary-bgColor;
   }
 
-  .ant-upload.ant-upload-drag {
+  .ant-form-item .ant-upload.ant-upload-drag {
     background-color: transparent;
     border-color: @dark-border-color;
   }
@@ -792,12 +811,43 @@
     color: @dark-text-color-3;
   }
 
-  .ant-tree.ant-tree-show-line li span.ant-tree-switcher {
+  .ant-upload-list {
+    color: @dark-text-color-3;
+
+    &-item {
+      &:hover {
+        .ant-upload-list-item-info {
+          background: @dark-bgColor;
+        }
+
+        .ant-upload-list-item-card-actions-btn {
+          border-color: transparent;
+        }
+
+        .ant-upload-list-item-card-actions .anticon {
+          color: @dark-text-color-3;
+        }
+      }
+    }
+  }
+
+  .ant-upload-list-item-info {
+    .anticon-loading .anticon,
+    .ant-upload-text-icon .anticon {
+      color: rgba(255, 255, 255, 0.65);
+    }
+  }
+
+  .ant-tree {
+    background: transparent;
+  }
+
+  .ant-tree-show-line .ant-tree-switcher {
     background-color: transparent;
     color: @dark-text-color-3;
   }
 
-  .ant-tree li .ant-tree-node-content-wrapper {
+  .ant-tree .ant-tree-node-content-wrapper {
     color: @dark-text-color-3;
   }
 
@@ -806,7 +856,7 @@
     background-color: transparent;
   }
 
-  .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
+  .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected {
     .ant-tree-icon__customize {
       color: #000;
     }
@@ -814,7 +864,7 @@
     color: #000;
   }
 
-  .ant-tree li .ant-tree-node-content-wrapper:hover {
+  .ant-tree .ant-tree-node-content-wrapper:hover {
     .ant-tree-icon__customize {
       color: #000;
     }
diff --git a/ui/src/style/index.less b/ui/src/style/index.less
index 38b4c6b..c0f1866 100644
--- a/ui/src/style/index.less
+++ b/ui/src/style/index.less
@@ -16,8 +16,8 @@
 // under the License.
 
 //* import all  ## official ant ##  variables; mixins and styles
-@import "~ant-design-vue/lib/style/themes/default";
-@import "~ant-design-vue/lib/style/core/index";
+@import "~ant-design-vue/es/style/themes/default.less";
+@import "~ant-design-vue/es/style/core/index.less";
 
 //* import all  ## custom ##  variables, mixins and styles
 
diff --git a/ui/src/style/vars.less b/ui/src/style/vars.less
index 6d219a1..670e77d 100644
--- a/ui/src/style/vars.less
+++ b/ui/src/style/vars.less
@@ -505,4 +505,33 @@
 .ant-dropdown-menu-item:hover,
 .ant-dropdown-menu-submenu-title:hover {
   background-color: color(~`colorPalette("@{primary-color}", 1)`) !important;
+}
+
+.ant-tabs.tab-center {
+  > .ant-tabs-nav, > div > .ant-tabs-nav,
+  > .ant-tabs-nav .ant-tabs-nav-wrap,
+  > div > .ant-tabs-nav .ant-tabs-nav-wrap {
+    justify-content: center;
+  }
+}
+
+.ant-tabs-large>.ant-tabs-nav .ant-tabs-tab {
+  padding: 16px;
+}
+
+.ant-steps {
+  &-item-container {
+    &:hover {
+      .ant-steps-item-content .ant-steps-item-description {
+        color: inherit !important;
+      }
+    }
+  }
+
+  &-item-process {
+    .ant-steps-item-content .ant-steps-item-title {
+      color: inherit !important;
+      font-weight: 600;
+    }
+  }
 }
\ No newline at end of file
diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js
index 9b9ce27..5f22307 100644
--- a/ui/src/utils/request.js
+++ b/ui/src/utils/request.js
@@ -193,6 +193,7 @@
   cancel: () => {
     if (!source) sourceToken.init()
     source.cancel()
+    source = null
   }
 }
 
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index cf552e2..0bcf3df 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -17,10 +17,10 @@
 
 <template>
   <div>
-    <a-affix :offsetTop="78">
+    <a-affix :offsetTop="this.$store.getters.shutdownTriggered ? 103 : 78">
       <a-card class="breadcrumb-card" style="z-index: 10">
         <a-row>
-          <a-col :span="device === 'mobile' ? 24 : 12" style="padding-left: 12px">
+          <a-col :span="device === 'mobile' ? 24 : 12" style="padding-left: 12px; margin-top: 10px">
             <breadcrumb :resource="resource">
               <template #end>
                 <a-button
@@ -34,14 +34,14 @@
                 </a-button>
                 <a-switch
                   v-if="!dataView && ['vm', 'volume', 'zone', 'cluster', 'host', 'storagepool', 'managementserver'].includes($route.name)"
-                  style="margin-left: 8px"
+                  style="margin-left: 8px; margin-bottom: 3px"
                   :checked-children="$t('label.metrics')"
                   :un-checked-children="$t('label.metrics')"
                   :checked="$store.getters.metrics"
                   @change="(checked, event) => { $store.dispatch('SetMetrics', checked) }"/>
                 <a-switch
                   v-if="!projectView && hasProjectId"
-                  style="margin-left: 8px"
+                  style="margin-left: 8px; margin-bottom: 3px"
                   :checked-children="$t('label.projects')"
                   :un-checked-children="$t('label.projects')"
                   :checked="$store.getters.listAllProjects"
@@ -53,14 +53,8 @@
                   <a-select
                     v-if="!dataView && filters && filters.length > 0"
                     :placeholder="$t('label.filterby')"
-                    :value="$route.query.filter || (projectView && $route.name === 'vm' ||
-                      ['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) &&
-                      ['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool'].includes($route.name)
-                        ? 'all' : ['publicip'].includes($route.name)
-                        ? 'allocated' : ['account', 'guestnetwork', 'guestvlans'].includes($route.name)
-                        ? 'all' : ['volume'].includes($route.name)
-                        ? 'user' : 'self')"
-                    style="min-width: 120px; margin-left: 10px"
+                    :value="filterValue"
+                    style="min-width: 120px; margin-left: 10px; margin-top: -4px"
                     @change="changeFilter"
                     showSearch
                     optionFilterProp="label"
@@ -70,7 +64,7 @@
                     <template #suffixIcon><filter-outlined class="ant-select-suffix" /></template>
                     <a-select-option
                       v-if="['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) &&
-                      ['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool'].includes($route.name) ||
+                      ['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool', 'kubernetes'].includes($route.name) ||
                       ['account'].includes($route.name)"
                       key="all"
                       :label="$t('label.all')">
@@ -90,7 +84,7 @@
           </a-col>
           <a-col
             :span="device === 'mobile' ? 24 : 12"
-            :style="device === 'mobile' ? { float: 'right', 'margin-top': '12px', 'margin-bottom': '-6px', display: 'table' } : { float: 'right', display: 'table', 'margin-bottom': '-6px' }" >
+            :style="device === 'mobile' ? { float: 'right', 'margin-top': '12px', 'margin-bottom': '-6px', display: 'table' } : { float: 'right', display: 'table', 'margin-bottom': '-4px' }" >
             <slot name="action" v-if="dataView && $route.path.startsWith('/publicip')"></slot>
             <action-button
               v-else
@@ -105,6 +99,7 @@
             <search-view
               v-if="!dataView"
               :searchFilters="searchFilters"
+              style="min-width: 120px; margin-left: 10px; margin-top: 5px"
               :searchParams="searchParams"
               :apiName="apiName"
               @search="onSearch"
@@ -252,11 +247,14 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }"
                 >
-                  <a-select-option key="" >{{ }}</a-select-option>
-                  <a-select-option v-for="(opt, optIndex) in currentAction.mapping[field.name].options" :key="optIndex">
+                  <a-select-option key="" label="">{{ }}</a-select-option>
+                  <a-select-option
+                    v-for="(opt, optIndex) in currentAction.mapping[field.name].options"
+                    :key="optIndex"
+                    :label="opt">
                     {{ opt }}
                   </a-select-option>
                 </a-select>
@@ -269,12 +267,15 @@
                   :loading="field.loading"
                   :placeholder="field.description"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }"
                   v-focus="fieldIndex === firstIndex"
                 >
-                  <a-select-option key="">{{ }}</a-select-option>
-                  <a-select-option v-for="(opt, optIndex) in field.opts" :key="optIndex">
+                  <a-select-option key="" label="">{{ }}</a-select-option>
+                  <a-select-option
+                    v-for="(opt, optIndex) in field.opts"
+                    :key="optIndex"
+                    :label="opt.name || opt.description || opt.traffictype || opt.publicip">
                     {{ opt.name || opt.description || opt.traffictype || opt.publicip }}
                   </a-select-option>
                 </a-select>
@@ -343,10 +344,13 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }"
                 >
-                  <a-select-option v-for="(opt, optIndex) in field.opts" :key="optIndex">
+                  <a-select-option
+                    v-for="(opt, optIndex) in field.opts"
+                    :key="optIndex"
+                    :label="opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description">
                     {{ opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description }}
                   </a-select-option>
                 </a-select>
@@ -389,44 +393,46 @@
       </a-modal>
     </div>
 
-    <div v-if="dataView" style="margin-top: -10px">
-      <slot name="resource" v-if="$route.path.startsWith('/quotasummary') || $route.path.startsWith('/publicip')"></slot>
-      <resource-view
-        v-else
-        :resource="resource"
-        :loading="loading"
-        :tabs="$route.meta.tabs" />
-    </div>
-    <div class="row-element" v-else>
-      <list-view
-        :loading="loading"
-        :columns="columns"
-        :items="items"
-        :actions="actions"
-        :columnKeys="columnKeys"
-        :selectedColumns="selectedColumns"
-        ref="listview"
-        @update-selected-columns="updateSelectedColumns"
-        @selection-change="onRowSelectionChange"
-        @refresh="this.fetchData"
-        @edit-tariff-action="(showAction, record) => $emit('edit-tariff-action', showAction, record)"/>
-      <a-pagination
-        class="row-element"
-        style="margin-top: 10px"
-        size="small"
-        :current="page"
-        :pageSize="pageSize"
-        :total="itemCount"
-        :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
-        :pageSizeOptions="pageSizeOptions"
-        @change="changePage"
-        @showSizeChange="changePageSize"
-        showSizeChanger
-        showQuickJumper>
-        <template #buildOptionText="props">
-          <span>{{ props.value }} / {{ $t('label.page') }}</span>
-        </template>
-      </a-pagination>
+    <div :style="this.$store.getters.shutdownTriggered ? 'margin-top: 25px;' : null">
+      <div v-if="dataView" style="margin-top: -10px">
+        <slot name="resource" v-if="$route.path.startsWith('/quotasummary') || $route.path.startsWith('/publicip')"></slot>
+        <resource-view
+          v-else
+          :resource="resource"
+          :loading="loading"
+          :tabs="$route.meta.tabs" />
+      </div>
+      <div class="row-element" v-else>
+        <list-view
+          :loading="loading"
+          :columns="columns"
+          :items="items"
+          :actions="actions"
+          :columnKeys="columnKeys"
+          :selectedColumns="selectedColumns"
+          ref="listview"
+          @update-selected-columns="updateSelectedColumns"
+          @selection-change="onRowSelectionChange"
+          @refresh="fetchData"
+          @edit-tariff-action="(showAction, record) => $emit('edit-tariff-action', showAction, record)"/>
+        <a-pagination
+          class="row-element"
+          style="margin-top: 10px"
+          size="small"
+          :current="page"
+          :pageSize="pageSize"
+          :total="itemCount"
+          :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
+          :pageSizeOptions="pageSizeOptions"
+          @change="changePage"
+          @showSizeChange="changePageSize"
+          showSizeChanger
+          showQuickJumper>
+          <template #buildOptionText="props">
+            <span>{{ props.value }} / {{ $t('label.page') }}</span>
+          </template>
+        </a-pagination>
+      </div>
     </div>
     <bulk-action-progress
       :showGroupActionModal="showGroupActionModal"
@@ -664,6 +670,25 @@
       return [...new Set(sizes)].sort(function (a, b) {
         return a - b
       }).map(String)
+    },
+    filterValue () {
+      if (this.$route.query.filter) {
+        return this.$route.query.filter
+      }
+      const routeName = this.$route.name
+      if ((this.projectView && routeName === 'vm') || (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) && ['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool'].includes(routeName)) || ['account', 'guestnetwork', 'guestvlans', 'guestos', 'guestoshypervisormapping', 'kubernetes'].includes(routeName)) {
+        return 'all'
+      }
+      if (['publicip'].includes(routeName)) {
+        return 'allocated'
+      }
+      if (['volume'].includes(routeName)) {
+        return 'user'
+      }
+      if (['event'].includes(routeName)) {
+        return 'active'
+      }
+      return 'self'
     }
   },
   methods: {
@@ -806,7 +831,6 @@
         })
       }
 
-      const customRender = {}
       for (var columnKey of this.columnKeys) {
         let key = columnKey
         let title = columnKey === 'cidr' && this.columnKeys.includes('ip6cidr') ? 'ipv4.cidr' : columnKey
@@ -814,18 +838,16 @@
           if ('customTitle' in columnKey && 'field' in columnKey) {
             key = columnKey.field
             title = columnKey.customTitle
-            customRender[key] = columnKey[key]
           } else {
             key = Object.keys(columnKey)[0]
             title = Object.keys(columnKey)[0]
-            customRender[key] = columnKey[key]
           }
         }
         this.columns.push({
+          key: key,
           title: this.$t('label.' + String(title).toLowerCase()),
           dataIndex: key,
-          slots: { customRender: key },
-          sorter: function (a, b) { return genericCompare(a[this.dataIndex] || '', b[this.dataIndex] || '') }
+          sorter: function (a, b) { return genericCompare(a[key] || '', b[key] || '') }
         })
         this.selectedColumns.push(key)
       }
@@ -847,6 +869,7 @@
           this.$t('label.linklocalip'), this.$t('label.size'), this.$t('label.sizegb'), this.$t('label.current'),
           this.$t('label.created'), this.$t('label.order')].includes(column.title)
       })
+      this.chosenColumns.splice(this.chosenColumns.length - 1, 1)
 
       if (['listTemplates', 'listIsos'].includes(this.apiName) && this.dataView) {
         delete params.showunique
@@ -954,12 +977,6 @@
 
         for (let idx = 0; idx < this.items.length; idx++) {
           this.items[idx].key = idx
-          for (const key in customRender) {
-            const func = customRender[key]
-            if (func && typeof func === 'function') {
-              this.items[idx][key] = func(this.items[idx])
-            }
-          }
           if (this.$route.path.startsWith('/ldapsetting')) {
             this.items[idx].id = this.items[idx].hostname
           }
@@ -1090,6 +1107,14 @@
                 description: self.$t('label.confirmpassword.description')
               }
             }
+            if (arg === 'ostypeid') {
+              return {
+                type: 'uuid',
+                name: 'ostypeid',
+                required: true,
+                description: self.$t('label.select.guest.os.type')
+              }
+            }
             return paramFields.filter(function (param) {
               return param.name.toLowerCase() === arg.toLowerCase()
             })[0]
@@ -1283,9 +1308,9 @@
           this.bulkColumns = this.chosenColumns
           this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' }))
           this.bulkColumns.splice(0, 0, {
+            key: 'status',
             dataIndex: 'status',
             title: this.$t('label.operation.status'),
-            slots: { customRender: 'status' },
             filters: [
               { text: 'In Progress', value: 'InProgress' },
               { text: 'Success', value: 'success' },
@@ -1537,13 +1562,17 @@
       }
 
       this.columns = this.allColumns.filter(x => this.selectedColumns.includes(x.dataIndex))
-      this.columns.push({
-        dataIndex: 'dropdownFilter',
-        slots: {
-          filterDropdown: 'filterDropdown',
-          filterIcon: 'filterIcon'
-        }
-      })
+      const filterColumn = {
+        key: 'filtercolumn',
+        dataIndex: 'filtercolumn',
+        title: '',
+        customFilterDropdown: true,
+        width: 5
+      }
+      if (this.columns.length === 0) {
+        filterColumn.width = 'auto'
+      }
+      this.columns.push(filterColumn)
       if (!this.$store.getters.customColumns[this.$store.getters.userInfo.id]) {
         this.$store.getters.customColumns[this.$store.getters.userInfo.id] = {}
       }
@@ -1616,6 +1645,24 @@
         } else if (filter === 'allocatedonly') {
           query.allocatedonly = 'true'
         }
+      } else if (this.$route.name === 'event') {
+        if (filter === 'archived') {
+          query.archived = true
+        } else {
+          delete query.archived
+        }
+      } else if (this.$route.name === 'guestoshypervisormapping') {
+        if (filter === 'all') {
+          delete query.hypervisor
+        } else {
+          query.hypervisor = filter
+        }
+      } else if (this.$route.name === 'kubernetes') {
+        if (filter === 'all') {
+          delete query.clustertype
+        } else {
+          query.clustertype = filter === 'cloud.managed' ? 'CloudManaged' : 'ExternalManaged'
+        }
       }
       query.filter = filter
       query.page = '1'
@@ -1641,6 +1688,10 @@
               query.templatetype = value
             } else if (this.$route.name === 'globalsetting') {
               query.name = value
+            } else if (this.$route.name === 'guestoshypervisormapping') {
+              query.hypervisor = value
+            } else if (this.$route.name === 'guestos') {
+              query.description = value
             } else {
               query.keyword = value
             }
@@ -1786,7 +1837,6 @@
           this.rules[field.name].push(rule)
           break
         default:
-          console.log('hererere')
           rule.required = field.required
           rule.message = this.$t('message.error.required.input')
           this.rules[field.name].push(rule)
diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue
index 34f5a70..56ac141 100644
--- a/ui/src/views/auth/Login.vue
+++ b/ui/src/views/auth/Login.vue
@@ -26,6 +26,7 @@
     v-ctrl-enter="handleSubmit"
   >
     <a-tabs
+      class="tab-center"
       :activeKey="customActiveKey"
       size="large"
       :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }"
@@ -48,9 +49,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
-            <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase">
+            <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase" :label="item.name">
               <template #prefix>
                 <database-outlined />
               </template>
@@ -113,9 +114,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase">
+            <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase" :label="item.name">
               <template #prefix>
                 <database-outlined />
               </template>
@@ -129,9 +130,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="(idp, idx) in idps" :key="idx" :value="idp.id">
+            <a-select-option v-for="(idp, idx) in idps" :key="idx" :value="idp.id" :label="idp.orgName">
               {{ idp.orgName }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/AssignInstance.vue b/ui/src/views/compute/AssignInstance.vue
index 9acc821..ed3efbc 100644
--- a/ui/src/views/compute/AssignInstance.vue
+++ b/ui/src/views/compute/AssignInstance.vue
@@ -35,9 +35,9 @@
           v-model:value="selectedAccountType"
           v-focus="true"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }">
           <a-select-option :value="$t('label.account')">{{ $t('label.account') }}</a-select-option>
           <a-select-option :value="$t('label.project')">{{ $t('label.project') }}</a-select-option>
@@ -71,7 +71,7 @@
             @change="changeAccount"
             v-model:value="selectedAccount"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
               return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
diff --git a/ui/src/views/compute/AttachIso.vue b/ui/src/views/compute/AttachIso.vue
index 20b9e4e..ba54219 100644
--- a/ui/src/views/compute/AttachIso.vue
+++ b/ui/src/views/compute/AttachIso.vue
@@ -31,9 +31,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
-            <a-select-option v-for="iso in isos" :key="iso.id">
+            <a-select-option v-for="iso in isos" :key="iso.id" :label="iso.displaytext || iso.name">
               {{ iso.displaytext || iso.name }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/AutoScaleDownPolicyTab.vue b/ui/src/views/compute/AutoScaleDownPolicyTab.vue
index ecbc584..422d1ab 100644
--- a/ui/src/views/compute/AutoScaleDownPolicyTab.vue
+++ b/ui/src/views/compute/AutoScaleDownPolicyTab.vue
@@ -36,12 +36,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-                      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }" >
           <a-select-option
             v-for="(scalepolicy, index) in this.policies"
             :value="scalepolicy.id"
-            :key="index">
+            :key="index"
+            :label="scalepolicy.displaytext || scalepolicy.name" >
             {{ scalepolicy.name || scalepolicy.id }}
           </a-select-option>
         </a-select>
@@ -109,28 +110,24 @@
       :dataSource="policy.conditions"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #name="{ record }">
-        {{ record.name }}
-      </template>
-      <template #relationaloperator="{ record }">
-        {{ getOperator(record.relationaloperator) }}
-      </template>
-      <template #threshold="{ record }">
-        {{ record.threshold }}
-      </template>
-      <template #actions="{ record }">
-        <tooltip-button
-          :tooltip="$t('label.edit')"
-          :disabled="!('updateCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
-          icon="edit-outlined"
-          @onClick="() => openUpdateConditionModal(record)" />
-        <tooltip-button
-          :tooltip="$t('label.delete')"
-          :disabled="!('deleteCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="deleteConditionFromAutoScalePolicy(record.id)" />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'relationaloperator'">
+          {{ getOperator(record.relationaloperator) }}
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.edit')"
+            :disabled="!('updateCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
+            icon="edit-outlined"
+            @onClick="() => openUpdateConditionModal(record)" />
+          <tooltip-button
+            :tooltip="$t('label.delete')"
+            :disabled="!('deleteCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="deleteConditionFromAutoScalePolicy(record.id)" />
+        </template>
       </template>
     </a-table>
 
@@ -147,11 +144,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-focus="true"
             v-model:value="newCondition.counterid">
-            <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+            <a-select-option
+              v-for="(counter, index) in countersList"
+              :value="counter.id"
+              :key="index"
+              :label="counter.name" >
               {{ counter.name }}
             </a-select-option>
           </a-select>
@@ -165,11 +166,11 @@
           <a-select
             v-model:value="newCondition.relationaloperator"
             style="width: 100%;"
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="LT">{{ getOperator('LT') }}</a-select-option>
+            <a-select-option value="LT" >{{ getOperator('LT') }}</a-select-option>
           </a-select>
           <span class="error-text">{{ $t('label.required') }}</span>
         </div>
@@ -216,11 +217,11 @@
           <a-select
             v-model:value="updateConditionDetails.relationaloperator"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="LT">{{ getOperator('LT') }}</a-select-option>
+            <a-select-option value="LT" >{{ getOperator('LT') }}</a-select-option>
           </a-select>
         </div>
         <div class="update-condition__item">
@@ -277,10 +278,14 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-model:value="newPolicy.counterid">
-            <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+            <a-select-option
+              v-for="(counter, index) in countersList"
+              :value="counter.id"
+              :key="index"
+              :label="counter.name">
               {{ counter.name }}
             </a-select-option>
           </a-select>
@@ -293,11 +298,11 @@
           <a-select
             v-model:value="newPolicy.relationaloperator"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="LT">{{ getOperator('LT') }}</a-select-option>
+            <a-select-option value="LT" >{{ getOperator('LT') }}</a-select-option>
           </a-select>
         </div>
         <div class="update-condition__item">
@@ -338,7 +343,7 @@
   },
   data () {
     return {
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       loading: true,
       policies: [],
       isEditable: false,
@@ -377,15 +382,15 @@
         },
         {
           title: this.$t('label.relationaloperator'),
-          slots: { customRender: 'relationaloperator' }
+          key: 'relationaloperator'
         },
         {
           title: this.$t('label.threshold'),
-          slots: { customRender: 'threshold' }
+          dataIndex: 'threshold'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ]
     }
diff --git a/ui/src/views/compute/AutoScaleLoadBalancing.vue b/ui/src/views/compute/AutoScaleLoadBalancing.vue
index f6e29b3..6091689 100644
--- a/ui/src/views/compute/AutoScaleLoadBalancing.vue
+++ b/ui/src/views/compute/AutoScaleLoadBalancing.vue
@@ -33,16 +33,37 @@
       :dataSource="lbRules"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #algorithm="{ record }">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'algorithm'">
         {{ returnAlgorithmName(record.algorithm) }}
-      </template>
-      <template #protocol="{record}">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #stickiness="{record}">
-        <a-button @click="() => openStickinessModal(record.id)">
-          {{ returnStickinessLabel(record.id) }}
-        </a-button>
+        </template>
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'stickiness'">
+          <a-button @click="() => openStickinessModal(record.id)">
+            {{ returnStickinessLabel(record.id) }}
+          </a-button>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(record)" />
+            <tooltip-button :tooltip="$t('label.edit.tags')" :disabled="!('updateLoadBalancerRule' in $store.getters.apis)" icon="tag-outlined" @onClick="() => openTagsModal(record.id)" />
+            <a-popconfirm
+              :title="$t('label.delete') + '?'"
+              @confirm="handleDeleteRule(record)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')"
+            >
+              <tooltip-button
+                :tooltip="$t('label.delete')"
+                :disabled="!('deleteLoadBalancerRule' in $store.getters.apis) || record.autoscalevmgroup"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined" />
+            </a-popconfirm>
+          </div>
+        </template>
       </template>
       <template #expandedRowRender="{ record }">
         <div class="rule-instance-list">
@@ -67,25 +88,6 @@
           </div>
         </div>
       </template>
-      <template #actions="{record}">
-        <div class="actions">
-          <tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(record)" />
-          <tooltip-button :tooltip="$t('label.edit.tags')" :disabled="!('updateLoadBalancerRule' in $store.getters.apis)" icon="tag-outlined" @onClick="() => openTagsModal(record.id)" />
-          <a-popconfirm
-            :title="$t('label.delete') + '?'"
-            @confirm="handleDeleteRule(record)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')"
-          >
-            <tooltip-button
-              :tooltip="$t('label.delete')"
-              :disabled="!('deleteLoadBalancerRule' in $store.getters.apis) || record.autoscalevmgroup"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined" />
-          </a-popconfirm>
-        </div>
-      </template>
     </a-table>
     <a-modal
       v-if="tagsModalVisible"
@@ -169,12 +171,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="LbCookie">{{ $t('label.lb.cookie') }}</a-select-option>
-            <a-select-option value="AppCookie">{{ $t('label.app.cookie') }}</a-select-option>
-            <a-select-option value="SourceBased">{{ $t('label.source.based') }}</a-select-option>
-            <a-select-option value="none">{{ $t('label.none') }}</a-select-option>
+            <a-select-option value="LbCookie" :label="$t('label.lb.cookie')">{{ $t('label.lb.cookie') }}</a-select-option>
+            <a-select-option value="AppCookie" :label="$t('label.lb.cookie')">{{ $t('label.app.cookie') }}</a-select-option>
+            <a-select-option value="SourceBased" :label="$t('label.lb.cookie')">{{ $t('label.source.based') }}</a-select-option>
+            <a-select-option value="none" :label="$t('label.lb.cookie')">{{ $t('label.none') }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item
@@ -261,9 +263,9 @@
           <a-select
             v-model:value="editRuleDetails.algorithm"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="roundrobin">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option>
             <a-select-option value="leastconn">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option>
@@ -275,9 +277,9 @@
           <a-select
             v-model:value="editRuleDetails.protocol"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="tcp-proxy">{{ $t('label.tcp.proxy') }}</a-select-option>
             <a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
@@ -355,11 +357,11 @@
         },
         {
           title: this.$t('label.algorithm'),
-          slots: { customRender: 'algorithm' }
+          key: 'algorithm'
         },
         {
           title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol'
         },
         {
           title: this.$t('label.state'),
@@ -367,11 +369,11 @@
         },
         {
           title: this.$t('label.action.configure.stickiness'),
-          slots: { customRender: 'stickiness' }
+          key: 'stickiness'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ],
       tiers: {
@@ -382,13 +384,13 @@
         {
           title: this.$t('label.name'),
           dataIndex: 'name',
-          slots: { customRender: 'name' },
+          key: 'name',
           width: 220
         },
         {
           title: this.$t('label.state'),
           dataIndex: 'state',
-          slots: { customRender: 'state' }
+          key: 'state'
         },
         {
           title: this.$t('label.displayname'),
@@ -404,8 +406,8 @@
         },
         {
           title: this.$t('label.select'),
-          dataIndex: 'action',
-          slots: { customRender: 'action' },
+          dataIndex: 'actions',
+          key: 'actions',
           width: 80
         }
       ],
diff --git a/ui/src/views/compute/AutoScaleUpPolicyTab.vue b/ui/src/views/compute/AutoScaleUpPolicyTab.vue
index 450b954..4ddd67a 100644
--- a/ui/src/views/compute/AutoScaleUpPolicyTab.vue
+++ b/ui/src/views/compute/AutoScaleUpPolicyTab.vue
@@ -36,12 +36,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-                      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }" >
           <a-select-option
             v-for="(scalepolicy, index) in this.policies"
             :value="scalepolicy.id"
-            :key="index">
+            :key="index"
+            :label="scalepolicy.displaytext || scalepolicy.name">
             {{ scalepolicy.name || scalepolicy.id }}
           </a-select-option>
         </a-select>
@@ -109,28 +110,24 @@
       :dataSource="policy.conditions"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #name="{ record }">
-        {{ record.name }}
-      </template>
-      <template #relationaloperator="{ record }">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'relationaloperator'">
         {{ getOperator(record.relationaloperator) }}
-      </template>
-      <template #threshold="{ record }">
-        {{ record.threshold }}
-      </template>
-      <template #actions="{ record }">
-        <tooltip-button
-          :tooltip="$t('label.edit')"
-          :disabled="!('updateCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
-          icon="edit-outlined"
-          @onClick="() => openUpdateConditionModal(record)" />
-        <tooltip-button
-          :tooltip="$t('label.delete')"
-          :disabled="!('deleteCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="deleteConditionFromAutoScalePolicy(record.id)" />
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.edit')"
+            :disabled="!('updateCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
+            icon="edit-outlined"
+            @onClick="() => openUpdateConditionModal(record)" />
+          <tooltip-button
+            :tooltip="$t('label.delete')"
+            :disabled="!('deleteCondition' in $store.getters.apis) || resource.state !== 'DISABLED'"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="deleteConditionFromAutoScalePolicy(record.id)" />
+        </template>
       </template>
     </a-table>
 
@@ -147,11 +144,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-focus="true"
             v-model:value="newCondition.counterid">
-            <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+            <a-select-option
+              v-for="(counter, index) in countersList"
+              :value="counter.id"
+              :key="index"
+              :label="counter.name">>
               {{ counter.name }}
             </a-select-option>
           </a-select>
@@ -165,9 +166,9 @@
           <a-select
             v-model:value="newCondition.relationaloperator"
             style="width: 100%;"
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="GT">{{ getOperator('GT') }}</a-select-option>
           </a-select>
@@ -216,9 +217,9 @@
           <a-select
             v-model:value="updateConditionDetails.relationaloperator"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="GT">{{ getOperator('GT') }}</a-select-option>
           </a-select>
@@ -277,10 +278,14 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-model:value="newPolicy.counterid">
-            <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+            <a-select-option
+              v-for="(counter, index) in countersList"
+              :value="counter.id"
+              :key="index"
+              :label="counter.name">
               {{ counter.name }}
             </a-select-option>
           </a-select>
@@ -293,9 +298,9 @@
           <a-select
             v-model:value="newPolicy.relationaloperator"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="GT">{{ getOperator('GT') }}</a-select-option>
           </a-select>
@@ -338,7 +343,7 @@
   },
   data () {
     return {
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       loading: true,
       policies: [],
       isEditable: false,
@@ -377,15 +382,15 @@
         },
         {
           title: this.$t('label.relationaloperator'),
-          slots: { customRender: 'relationaloperator' }
+          key: 'relationaloperator'
         },
         {
           title: this.$t('label.threshold'),
-          slots: { customRender: 'threshold' }
+          dataIndex: 'threshold'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ]
     }
diff --git a/ui/src/views/compute/AutoScaleVmProfile.vue b/ui/src/views/compute/AutoScaleVmProfile.vue
index 4594775..705bfb5 100644
--- a/ui/src/views/compute/AutoScaleVmProfile.vue
+++ b/ui/src/views/compute/AutoScaleVmProfile.vue
@@ -35,11 +35,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-focus="true"
             v-model:value="autoscaleuserid">
-            <a-select-option v-for="(user, index) in usersList" :value="user.id" :key="index">
+            <a-select-option
+              v-for="(user, index) in usersList"
+              :value="user.id"
+              :key="index"
+              :label="user.username">
               {{ user.username }}
             </a-select-option>
           </a-select>
@@ -90,9 +94,9 @@
           <a-select
             style="width: 100%"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-focus="true"
             v-model:value="newParam.name">
@@ -126,13 +130,8 @@
       :dataSource="allParams"
       :pagination="false"
       :rowKey="record => record.name">
-      <template #name="{ record }">
-        {{ record.name }}
-      </template>
-      <template #threshold="{ record }">
-        {{ record.threshold }}
-      </template>
-      <template #actions="{ record }">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'actions'">
         <a-popconfirm
           :title="$t('label.delete') + '?'"
           @confirm="deleteParam(record.name)"
@@ -146,6 +145,7 @@
             :danger="true"
             icon="delete-outlined" />
         </a-popconfirm>
+        </template>
       </template>
     </a-table>
 
@@ -166,11 +166,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
             v-focus="true"
             v-model:value="autoscaleuserid">
-            <a-select-option v-for="(user, index) in usersList" :value="user.id" :key="index">
+            <a-select-option
+              v-for="(user, index) in usersList"
+              :value="user.id"
+              :key="index"
+              :label="user.username">
               {{ user.username }}
             </a-select-option>
           </a-select>
@@ -194,11 +198,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
             v-focus="true"
             v-model:value="templateid">
-            <a-select-option v-for="(template, index) in templatesList" :value="template.id" :key="index">
+            <a-select-option
+              v-for="(template, index) in templatesList"
+              :value="template.id"
+              :key="index"
+              :label="template.name">
               {{ template.name }}
             </a-select-option>
           </a-select>
@@ -214,11 +222,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
             v-focus="true"
             v-model:value="serviceofferingid">
-            <a-select-option v-for="(offering, index) in serviceOfferingsList" :value="offering.id" :key="index">
+            <a-select-option
+              v-for="(offering, index) in serviceOfferingsList"
+              :value="offering.id"
+              :key="index"
+              :label="offering.name">
               {{ offering.name }}
             </a-select-option>
           </a-select>
@@ -263,7 +275,7 @@
   },
   data () {
     return {
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       loading: true,
       editProfileModalVisible: false,
       profileid: null,
@@ -292,8 +304,8 @@
           dataIndex: 'value'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ]
     }
diff --git a/ui/src/views/compute/CreateAutoScaleVmGroup.vue b/ui/src/views/compute/CreateAutoScaleVmGroup.vue
index 4dcb295..3797077 100644
--- a/ui/src/views/compute/CreateAutoScaleVmGroup.vue
+++ b/ui/src/views/compute/CreateAutoScaleVmGroup.vue
@@ -42,19 +42,17 @@
                               v-model:value="form.zoneid"
                               @change="onSelectZoneId(zoneItem.id)">
                               <a-col :span="8">
-                                <a-card-grid style="width:200px;" :title="zoneItem.name" :hoverable="false">
-                                  <a-radio :value="zoneItem.id">
-                                    <div>
+                                <a-card style="width:200px;" :hoverable="false">
+                                  <a-radio :value="zoneItem.id" />
+                                  <div :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-30px', marginBottom: '10px'}">
                                       <resource-icon
                                         v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
                                         :image="zoneItem.icon.base64image"
-                                        size="36"
-                                        style="marginTop: -30px; marginLeft: 60px" />
-                                      <global-outlined v-else :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-40px'}"/>
+                                        size="36" />
+                                      <global-outlined v-else />
                                     </div>
-                                  </a-radio>
                                   <a-card-meta title="" :description="zoneItem.name" style="text-align:center; paddingTop: 10px;" />
-                                </a-card-grid>
+                                </a-card>
                               </a-col>
                             </a-radio-group>
                           </div>
@@ -152,7 +150,7 @@
                         }"
                         @change="onSelectTemplateConfigurationId"
                       >
-                        <a-select-option v-for="opt in templateConfigurations" :key="opt.id">
+                        <a-select-option v-for="opt in templateConfigurations" :key="opt.id" :label="opt.name || opt.description">
                           {{ opt.name || opt.description }}
                         </a-select-option>
                       </a-select>
@@ -420,11 +418,11 @@
                         <span v-else-if="property.type && property.type==='string' && property.qualifiers && property.qualifiers.startsWith('ValueMap')">
                           <a-select
                             showSearch
-                            optionFilterProp="label"
+                            optionFilterProp="value"
                             v-model:value="form['properties.' + escapePropertyKey(property.key)]"
                             :placeholder="property.description"
                             :filterOption="(input, option) => {
-                              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                             }"
                           >
                             <a-select-option v-for="opt in getPropertyQualifiers(property.qualifiers, 'select')" :key="opt">
@@ -463,11 +461,12 @@
                       showSearch
                       optionFilterProp="label"
                       :filterOption="(input, option) => {
-                        return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                       }" >
                       <a-select-option
                         v-for="policy in this.scaleUpPolicies"
-                        :key="policy.id">
+                        :key="policy.id"
+                        :label="policy.name">
                         {{ policy.name }}
                       </a-select-option>
                     </a-select>
@@ -516,11 +515,15 @@
                         showSearch
                         optionFilterProp="label"
                         :filterOption="(input, option) => {
-                          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                          return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                         }"
                         v-focus="true"
                         v-model:value="newScaleUpCondition.counterid">
-                        <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+                        <a-select-option
+                          v-for="(counter, index) in countersList"
+                          :value="counter.id"
+                          :key="index"
+                          :label="counter.name">
                           {{ counter.name }}
                         </a-select-option>
                       </a-select>
@@ -534,9 +537,9 @@
                       <a-select
                         v-model:value="newScaleUpCondition.relationaloperator"
                         style="width: 100%;"
-                        optionFilterProp="label"
+                        optionFilterProp="value"
                         :filterOption="(input, option) => {
-                          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                          return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                         }" >
                         <a-select-option value="GT">{{ getOperator('GT') }}</a-select-option>
                       </a-select>
@@ -568,20 +571,16 @@
                       :dataSource="scaleUpConditions"
                       :pagination="false"
                       :rowKey="record => record.counterid">
-                      <template #countername="{ record }">
-                        {{ record.countername }}
-                      </template>
-                      <template #relationaloperator="{ record }">
-                        {{ getOperator(record.relationaloperator) }}
-                      </template>
-                      <template #threshold="{ record }">
-                        {{ record.threshold }}
-                      </template>
-                      <template #actions="{ record }">
+                      <template #bodyCell="{ column, record }">
+                        <template v-if="column.key === 'relationaloperator'">
+                          {{ getOperator(record.relationaloperator) }}
+                        </template>
+                        <template v-if="column.key === 'actions'">
                           <a-button ref="submit" type="primary" :danger="true" @click="deleteScaleUpCondition(record.counterid)">
                             <template #icon><delete-outlined /></template>
                             {{ $t('label.delete') }}
                           </a-button>
+                        </template>
                       </template>
                     </a-table>
                   </div>
@@ -603,11 +602,12 @@
                       showSearch
                       optionFilterProp="label"
                       :filterOption="(input, option) => {
-                        return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                       }" >
                       <a-select-option
                         v-for="policy in this.scaleDownPolicies"
-                        :key="policy.id">
+                        :key="policy.id"
+                        :label="policy.name">
                         {{ policy.name }}
                       </a-select-option>
                     </a-select>
@@ -656,11 +656,15 @@
                         showSearch
                         optionFilterProp="label"
                         :filterOption="(input, option) => {
-                          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                          return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                         }"
                         v-focus="true"
                         v-model:value="newScaleDownCondition.counterid">
-                        <a-select-option v-for="(counter, index) in countersList" :value="counter.id" :key="index">
+                        <a-select-option
+                          v-for="(counter, index) in countersList"
+                          :value="counter.id"
+                          :key="index"
+                          :label="counter.name">
                           {{ counter.name }}
                         </a-select-option>
                       </a-select>
@@ -674,9 +678,9 @@
                       <a-select
                         v-model:value="newScaleDownCondition.relationaloperator"
                         style="width: 100%;"
-                        optionFilterProp="label"
+                        optionFilterProp="value"
                         :filterOption="(input, option) => {
-                          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                          return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                         }" >
                         <a-select-option value="LT">{{ getOperator('LT') }}</a-select-option>
                       </a-select>
@@ -699,7 +703,7 @@
                     </div>
                   </div>
                   <a-divider/>
-                  <div>
+                  <div style="display: block">
                     <a-table
                       size="small"
                       style="overflow-y: auto"
@@ -708,20 +712,16 @@
                       :dataSource="scaleDownConditions"
                       :pagination="false"
                       :rowKey="record => record.counterid">
-                      <template #countername="{ record }">
-                        {{ record.countername }}
-                      </template>
-                      <template #relationaloperator="{ record }">
-                        {{ getOperator(record.relationaloperator) }}
-                      </template>
-                      <template #threshold="{ record }">
-                        {{ record.threshold }}
-                      </template>
-                      <template #actions="{ record }">
-                        <a-button ref="submit" type="primary" :danger="true" @click="deleteScaleDownCondition(record.counterid)">
-                          <template #icon><delete-outlined /></template>
-                          {{ $t('label.delete') }}
-                        </a-button>
+                      <template #bodyCell="{ column, record }">
+                        <template v-if="column.key === 'relationaloperator'">
+                          {{ getOperator(record.relationaloperator) }}
+                        </template>
+                        <template v-if="column.key === 'actions'">
+                          <a-button ref="submit" type="primary" :danger="true" @click="deleteScaleDownCondition(record.counterid)">
+                            <template #icon><delete-outlined /></template>
+                            {{ $t('label.delete') }}
+                          </a-button>
+                        </template>
                       </template>
                     </a-table>
                   </div>
@@ -790,11 +790,15 @@
                         showSearch
                         optionFilterProp="label"
                         :filterOption="(input, option) => {
-                          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                          return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                         }"
                         v-focus="true"
                         v-model:value="form.autoscaleuserid">
-                        <a-select-option v-for="(user, index) in usersList" :value="user.id" :key="index">
+                        <a-select-option
+                          v-for="(user, index) in usersList"
+                          :value="user.id"
+                          :key="index"
+                          :label="user.username">
                           {{ user.username }}
                         </a-select-option>
                       </a-select>
@@ -1079,15 +1083,15 @@
         },
         {
           title: this.$t('label.relationaloperator'),
-          slots: { customRender: 'relationaloperator' }
+          key: 'relationaloperator'
         },
         {
           title: this.$t('label.threshold'),
-          slots: { customRender: 'threshold' }
+          dataIndex: 'threshold'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ],
       scaleDownPolicies: [],
@@ -1107,15 +1111,15 @@
         },
         {
           title: this.$t('label.relationaloperator'),
-          slots: { customRender: 'relationaloperator' }
+          key: 'relationaloperator'
         },
         {
           title: this.$t('label.threshold'),
-          slots: { customRender: 'threshold' }
+          dataIndex: 'threshold'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          title: this.$t('label.actions'),
+          key: 'actions'
         }
       ],
       usersList: [],
diff --git a/ui/src/views/compute/CreateKubernetesCluster.vue b/ui/src/views/compute/CreateKubernetesCluster.vue
index 9ca7b0a..5c725b7 100644
--- a/ui/src/views/compute/CreateKubernetesCluster.vue
+++ b/ui/src/views/compute/CreateKubernetesCluster.vue
@@ -75,12 +75,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="kubernetesVersionLoading"
             :placeholder="apiParams.kubernetesversionid.description"
             @change="val => { handleKubernetesVersionChange(kubernetesVersions[val]) }">
-            <a-select-option v-for="(opt, optIndex) in kubernetesVersions" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in kubernetesVersions" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -95,11 +95,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="serviceOfferingLoading"
             :placeholder="apiParams.serviceofferingid.description">
-            <a-select-option v-for="(opt, optIndex) in serviceOfferings" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in serviceOfferings" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -122,11 +122,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="networkLoading"
             :placeholder="apiParams.networkid.description">
-            <a-select-option v-for="(opt, optIndex) in networks" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in networks" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -171,11 +171,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="keyPairLoading"
             :placeholder="apiParams.keypair.description">
-            <a-select-option v-for="(opt, optIndex) in keyPairs" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in keyPairs" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -284,7 +284,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.kubecluster.name') }],
-        description: [{ required: true, message: this.$t('message.error.cluster.description') }],
         zoneid: [{ required: true, message: this.$t('message.error.zone.for.cluster') }],
         kubernetesversionid: [{ required: true, message: this.$t('message.error.version.for.cluster') }],
         serviceofferingid: [{ required: true, message: this.$t('message.error.serviceoffering.for.cluster') }],
@@ -460,7 +459,8 @@
           zoneid: this.zones[values.zoneid].id,
           kubernetesversionid: this.kubernetesVersions[values.kubernetesversionid].id,
           serviceofferingid: this.serviceOfferings[values.serviceofferingid].id,
-          size: values.size
+          size: values.size,
+          clustertype: 'CloudManaged'
         }
         if (this.isValidValueForKey(values, 'noderootdisksize') && values.noderootdisksize > 0) {
           params.noderootdisksize = values.noderootdisksize
diff --git a/ui/src/views/compute/CreateSSHKeyPair.vue b/ui/src/views/compute/CreateSSHKeyPair.vue
index 164fbe1..0f879cb 100644
--- a/ui/src/views/compute/CreateSSHKeyPair.vue
+++ b/ui/src/views/compute/CreateSSHKeyPair.vue
@@ -62,12 +62,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="domainLoading"
             :placeholder="apiParams.domainid.description"
             @change="val => { handleDomainChanged(domains[val]) }">
-            <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex" :label=" opt.path || opt.name || opt.description || ''">
               {{ opt.path || opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/CreateSnapshotWizard.vue b/ui/src/views/compute/CreateSnapshotWizard.vue
index bcd7d0f..0da4299 100644
--- a/ui/src/views/compute/CreateSnapshotWizard.vue
+++ b/ui/src/views/compute/CreateSnapshotWizard.vue
@@ -37,11 +37,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
             <a-select-option
               v-for="volume in listVolumes"
-              :key="volume.id">
+              :key="volume.id"
+              :label="volume.name">
               {{ volume.name }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index c637696..507b902e 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -42,19 +42,17 @@
                               v-model:value="form.zoneid"
                               @change="onSelectZoneId(zoneItem.id)">
                               <a-col :span="8">
-                                <a-card-grid style="width:200px;" :title="zoneItem.name" :hoverable="false">
-                                  <a-radio :value="zoneItem.id">
-                                    <div>
+                                <a-card style="width:200px;" :hoverable="false">
+                                  <a-radio :value="zoneItem.id" />
+                                  <div :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-30px', marginBottom: '10px'}">
                                       <resource-icon
                                         v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
                                         :image="zoneItem.icon.base64image"
-                                        size="36"
-                                        style="marginTop: -30px; marginLeft: 60px" />
-                                      <global-outlined v-else :style="{fontSize: '36px', marginLeft: '60px', marginTop: '-40px'}"/>
+                                        size="36" />
+                                      <global-outlined v-else/>
                                     </div>
-                                  </a-radio>
                                   <a-card-meta title="" :description="zoneItem.name" style="text-align:center; paddingTop: 10px;" />
-                                </a-card-grid>
+                                </a-card>
                               </a-col>
                             </a-radio-group>
                           </div>
@@ -221,7 +219,7 @@
                         }"
                         @change="onSelectTemplateConfigurationId"
                       >
-                        <a-select-option v-for="opt in templateConfigurations" :key="opt.id">
+                        <a-select-option v-for="opt in templateConfigurations" :key="opt.id" :label="opt.name || opt.description">
                           {{ opt.name || opt.description }}
                         </a-select-option>
                       </a-select>
@@ -389,7 +387,10 @@
                 :status="zoneSelected ? 'process' : 'wait'"
                 v-if="zone && zone.networktype !== 'Basic'">
                 <template #description>
-                  <div v-if="zoneSelected">
+                  <div v-if="zoneSelected" style="margin-top: 5px">
+                    <div style="margin-bottom: 10px">
+                      {{ $t('message.network.selection') }}
+                    </div>
                     <div v-if="vm.templateid && templateNics && templateNics.length > 0">
                       <instance-nics-network-select-list-view
                         :nics="templateNics"
@@ -483,11 +484,11 @@
                         <span v-else-if="property.type && property.type==='string' && property.qualifiers && property.qualifiers.startsWith('ValueMap')">
                           <a-select
                             showSearch
-                            optionFilterProp="label"
+                            optionFilterProp="value"
                             v-model:value="form['properties.' + escapePropertyKey(property.key)]"
                             :placeholder="property.description"
                             :filterOption="(input, option) => {
-                              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                             }"
                           >
                             <a-select-option v-for="opt in getPropertyQualifiers(property.qualifiers, 'select')" :key="opt">
@@ -528,7 +529,7 @@
                           showSearch
                           optionFilterProp="label"
                           :filterOption="filterOption">
-                          <a-select-option v-for="bootType in options.bootTypes" :key="bootType.id">
+                          <a-select-option v-for="bootType in options.bootTypes" :key="bootType.id" :label="bootType.description">
                             {{ bootType.description }}
                           </a-select-option>
                         </a-select>
@@ -539,7 +540,7 @@
                           showSearch
                           optionFilterProp="label"
                           :filterOption="filterOption">
-                          <a-select-option v-for="bootMode in options.bootModes" :key="bootMode.id">
+                          <a-select-option v-for="bootMode in options.bootModes" :key="bootMode.id" :label="bootMode.description">
                             {{ bootMode.description }}
                           </a-select-option>
                         </a-select>
@@ -582,8 +583,10 @@
                                 :dataSource="templateUserDataParams"
                                 :pagination="false"
                                 :rowKey="record => record.key">
-                                <template #value="{ record }">
-                                  <a-input v-model:value="templateUserDataValues[record.key]" />
+                                <template #bodyCell="{ column, record }">
+                                  <template v-if="column.key === 'value'">
+                                    <a-input v-model:value="templateUserDataValues[record.key]" />
+                                  </template>
                                 </template>
                               </a-table>
                             </a-input-group>
@@ -605,8 +608,10 @@
                                 :dataSource="templateUserDataParams"
                                 :pagination="false"
                                 :rowKey="record => record.key">
-                                <template #value="{ record }">
-                                  <a-input v-model:value="templateUserDataValues[record.key]" />
+                                <template #bodyCell="{ column, record }">
+                                  <template v-if="column.key === 'value'">
+                                    <a-input v-model:value="templateUserDataValues[record.key]" />
+                                  </template>
                                 </template>
                               </a-table>
                             </a-input-group>
@@ -654,8 +659,10 @@
                                                 :dataSource="userDataParams"
                                                 :pagination="false"
                                                 :rowKey="record => record.key">
-                                                <template #value="{ record }">
-                                                  <a-input v-model:value="userDataValues[record.key]" />
+                                                <template #bodyCell="{ column, record }">
+                                                  <template v-if="column.key === 'value'">
+                                                    <a-input v-model:value="userDataValues[record.key]" />
+                                                  </template>
                                                 </template>
                                               </a-table>
                                             </a-input-group>
@@ -690,6 +697,23 @@
                         @select-affinity-group-item="($event) => updateAffinityGroups($event)"
                         @handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"/>
                     </a-form-item>
+                    <a-form-item name="nicmultiqueuenumber" ref="nicmultiqueuenumber" v-if="vm.templateid && ['KVM'].includes(hypervisor)">
+                      <template #label>
+                        <tooltip-label :title="$t('label.nicmultiqueuenumber')" :tooltip="$t('label.nicmultiqueuenumber.tooltip')"/>
+                      </template>
+                      <a-input-number
+                        style="width: 100%;"
+                        v-model:value="form.nicmultiqueuenumber" />
+                    </a-form-item>
+                    <a-form-item name="nicpackedvirtqueuesenabled" ref="nicpackedvirtqueuesenabled" v-if="vm.templateid && ['KVM'].includes(hypervisor)">
+                      <template #label>
+                        <tooltip-label :title="$t('label.nicpackedvirtqueuesenabled')" :tooltip="$t('label.nicpackedvirtqueuesenabled.tooltip')"/>
+                      </template>
+                      <a-switch
+                        v-model:checked="form.nicpackedvirtqueuesenabled"
+                        :checked="nicpackedvirtqueuesenabled"
+                        @change="val => { nicpackedvirtqueuesenabled = val }"/>
+                    </a-form-item>
                     <a-form-item name="iothreadsenabled" ref="iothreadsenabled" v-if="vm.templateid && ['KVM'].includes(hypervisor)">
                       <template #label>
                         <tooltip-label :title="$t('label.iothreadsenabled')" :tooltip="$t('label.iothreadsenabled.tooltip')"/>
@@ -709,7 +733,7 @@
                         v-model:value="form.iodriverpolicy"
                         optionFilterProp="label"
                         :filterOption="filterOption">
-                        <a-select-option v-for="iodriverpolicy in options.ioPolicyTypes" :key="iodriverpolicy.id">
+                        <a-select-option v-for="iodriverpolicy in options.ioPolicyTypes" :key="iodriverpolicy.id" :label="iodriverpolicy.description">
                           {{ iodriverpolicy.description }}
                         </a-select-option>
                       </a-select>
@@ -966,7 +990,7 @@
         {
           title: this.$t('label.value'),
           dataIndex: 'value',
-          slots: { customRender: 'value' }
+          key: 'value'
         }
       ],
       userDataValues: {},
@@ -1671,7 +1695,7 @@
       this.fetchInstaceGroups()
       this.fetchIoPolicyTypes()
       nextTick().then(() => {
-        ['name', 'keyboard', 'boottype', 'bootmode', 'userdata', 'iothreadsenabled', 'iodriverpolicy'].forEach(this.fillValue)
+        ['name', 'keyboard', 'boottype', 'bootmode', 'userdata', 'iothreadsenabled', 'iodriverpolicy', 'nicmultiqueuenumber', 'nicpackedvirtqueues'].forEach(this.fillValue)
         this.form.boottype = this.defaultBootType ? this.defaultBootType : this.options.bootTypes && this.options.bootTypes.length > 0 ? this.options.bootTypes[0].id : undefined
         this.form.bootmode = this.defaultBootMode ? this.defaultBootMode : this.options.bootModes && this.options.bootModes.length > 0 ? this.options.bootModes[0].id : undefined
         this.instanceConfig = toRaw(this.form)
@@ -1884,8 +1908,8 @@
       this.templateUserDataParams = []
 
       api('listUserData', { id: id }).then(json => {
-        const resp = json?.listuserdataresponse?.userdata || []
-        if (resp) {
+        const resp = json.listuserdataresponse.userdata || []
+        if (resp.length > 0) {
           var params = resp[0].params
           if (params) {
             var dataParams = params.split(',')
@@ -1969,6 +1993,8 @@
         deployVmData.dynamicscalingenabled = values.dynamicscalingenabled
         deployVmData.iothreadsenabled = values.iothreadsenabled
         deployVmData.iodriverpolicy = values.iodriverpolicy
+        deployVmData.nicmultiqueuenumber = values.nicmultiqueuenumber
+        deployVmData.nicpackedvirtqueuesenabled = values.nicpackedvirtqueuesenabled
         const isUserdataAllowed = !this.userdataDefaultOverridePolicy || (this.userdataDefaultOverridePolicy === 'ALLOWOVERRIDE' && this.doUserdataOverride) || (this.userdataDefaultOverridePolicy === 'APPEND' && this.doUserdataAppend)
         if (isUserdataAllowed && values.userdata && values.userdata.length > 0) {
           deployVmData.userdata = this.$toBase64AndURIEncoded(values.userdata)
diff --git a/ui/src/views/compute/DestroyVM.vue b/ui/src/views/compute/DestroyVM.vue
index de7b6da..c66c3bd 100644
--- a/ui/src/views/compute/DestroyVM.vue
+++ b/ui/src/views/compute/DestroyVM.vue
@@ -54,9 +54,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="volume in volumes" :key="volume.id">
+              <a-select-option v-for="volume in volumes" :key="volume.id" :label="volume.name">
                 {{ volume.name }}
               </a-select-option>
             </a-select>
@@ -102,12 +102,12 @@
                     showSearch
                     optionFilterProp="label"
                     :filterOption="(input, option) => {
-                      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }"
                     :loading="listVolumes[record.id].loading"
                     :placeholder="$t('label.delete.volumes')"
                     @change="(value) => onChangeVolume(record.id, value)">
-                    <a-select-option v-for="item in listVolumes[record.id].opts" :key="item.id">
+                    <a-select-option v-for="item in listVolumes[record.id].opts" :key="item.id" :label="item.name || item.description">
                       {{ item.name || item.description }}
                     </a-select-option>
                   </a-select>
@@ -317,9 +317,9 @@
       this.selectedItemsProgress = Array.from(this.selectedItems)
       this.selectedItemsProgress = this.selectedItemsProgress.map(v => ({ ...v, status: 'InProgress' }))
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        scopedSlots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/compute/EditVM.vue b/ui/src/views/compute/EditVM.vue
index 9aae2f3..4288bc3 100644
--- a/ui/src/views/compute/EditVM.vue
+++ b/ui/src/views/compute/EditVM.vue
@@ -52,11 +52,11 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }"
           :loading="osTypes.loading"
           v-model:value="form.ostypeid">
-          <a-select-option v-for="(ostype) in osTypes.opts" :key="ostype.id">
+          <a-select-option v-for="(ostype) in osTypes.opts" :key="ostype.id" :label="ostype.description">
             {{ ostype.description }}
           </a-select-option>
         </a-select>
@@ -103,7 +103,7 @@
           }"
           :loading="securitygroups.loading"
           v-focus="true">
-          <a-select-option v-for="securitygroup in securitygroups.opts" :key="securitygroup.id" :label="securitygroup.name">
+          <a-select-option v-for="securitygroup in securitygroups.opts" :key="securitygroup.id" :label="securitygroup.name ||  securitygroup.id">
             <div>
               {{ securitygroup.name ||  securitygroup.id }}
             </div>
diff --git a/ui/src/views/compute/InstanceSchedules.vue b/ui/src/views/compute/InstanceSchedules.vue
new file mode 100644
index 0000000..41694ad
--- /dev/null
+++ b/ui/src/views/compute/InstanceSchedules.vue
@@ -0,0 +1,461 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div>
+    <a-button
+      type="dashed"
+      style="width: 100%; margin-bottom: 10px"
+      @click="showAddModal"
+      :loading="loading"
+      :disabled="!('createVMSchedule' in $store.getters.apis)">
+      <template #icon><plus-outlined/></template> {{ $t('label.schedule.add') }}
+    </a-button>
+    <list-view
+      :loading="tabLoading"
+      :columns="columns"
+      :items="schedules"
+      :columnKeys="columnKeys"
+      :selectedColumns="selectedColumnKeys"
+      ref="listview"
+      @update-selected-columns="updateSelectedColumns"
+      @update-vm-schedule="updateVMSchedule"
+      @remove-vm-schedule="removeVMSchedule"
+      @refresh="this.fetchData"/>
+    <a-pagination
+      class="row-element"
+      style="margin-top: 10px"
+      size="small"
+      :current="page"
+      :pageSize="pageSize"
+      :total="totalCount"
+      :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
+      :pageSizeOptions="pageSizeOptions"
+      @change="changePage"
+      @showSizeChange="changePage"
+      showSizeChanger
+      showQuickJumper>
+      <template #buildOptionText="props">
+        <span>{{ props.value }} / {{ $t('label.page') }}</span>
+      </template>
+    </a-pagination>
+  </div>
+
+  <a-modal
+    :visible="showModal"
+    :title="$t('label.schedule')"
+    :maskClosable="false"
+    :closable="true"
+    :footer="null"
+    @cancel="closeModal">
+    <a-form
+      layout="vertical"
+      :ref="formRef"
+      :model="form"
+      :rules="rules"
+      @finish="submitForm"
+      v-ctrl-enter="submitForm">
+      <a-form-item name="description" ref="description">
+        <template #label>
+          <tooltip-label :title="$t('label.description')" :tooltip="apiParams.description.description"/>
+        </template>
+        <a-input
+          v-model:value="form.description"
+          v-focus="true" />
+      </a-form-item>
+      <a-form-item name="action" ref="action">
+        <template #label>
+          <tooltip-label :title="$t('label.action')" :tooltip="apiParams.action.description"/>
+        </template>
+        <a-radio-group
+          v-model:value="form.action"
+          button-style="solid"
+          :disabled="isEdit">
+          <a-radio-button v-for="action in actions" :key="action.id" :value="action.value">
+            {{ $t(action.label) }}
+          </a-radio-button>
+        </a-radio-group>
+      </a-form-item>
+      <a-form-item name="schedule" ref="schedule">
+        <template #label>
+          <tooltip-label :title="$t('label.schedule')" :tooltip="apiParams.schedule.description"/>
+        </template>
+        <label>{{ $t('label.advanced.mode') }}</label>
+        <a-switch
+          v-model:checked="form.useCronFormat"
+          >
+        </a-switch>
+        <br/>
+        <span v-if="!form.useCronFormat">
+        <cron-ant
+          v-model="form.schedule"
+          :periods="periods"
+          :button-props="{ type: 'primary', size: 'small', disabled: form.useCronFormat }"
+          @error="error=$event"/>
+      </span>
+      <span v-if="form.useCronFormat">
+        <label>{{ generateHumanReadableSchedule(form.schedule) }}</label>
+        <br/>
+      </span>
+        <a-input
+          :addonBefore="$t('label.cron')"
+          v-model:value="form.schedule"
+          :disabled="!form.useCronFormat"
+          v-focus="true" />
+      </a-form-item>
+      <a-form-item name="timezone" ref="timezone">
+        <template #label>
+          <tooltip-label :title="$t('label.timezone')" :tooltip="apiParams.timezone.description"/>
+        </template>
+        <a-select
+          showSearch
+          v-model:value="form.timezone"
+          optionFilterProp="label"
+          :filterOption="(input, option) => {
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          }"
+          :loading="fetching">
+          <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
+            {{ opt.name || opt.description }}
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+      <a-form-item name="startDate" ref="startDate">
+        <template #label>
+          <tooltip-label :title="$t('label.start.date.and.time')" :tooltip="apiParams.startdate.description"/>
+        </template>
+        <a-date-picker
+          v-model:value="form.startDate"
+          show-time
+          :locale="this.$i18n.locale"
+          :placeholder="$t('message.select.start.date.and.time')"/>
+      </a-form-item>
+      <a-form-item name="endDate" ref="endDate">
+        <template #label>
+          <tooltip-label :title="$t('label.end.date.and.time')" :tooltip="apiParams.enddate.description"/>
+        </template>
+        <a-date-picker
+          v-model:value="form.endDate"
+          show-time
+          :locale="this.$i18n.locale"
+          :placeholder="$t('message.select.end.date.and.time')"/>
+      </a-form-item>
+      <a-form-item name="enabled" ref="enabled">
+        <template #label>
+          <tooltip-label :title="$t('label.enabled')" :tooltip="apiParams.enabled.description"/>
+        </template>
+        <a-switch
+          v-model:checked="form.enabled">
+        </a-switch>
+      </a-form-item>
+      <div :span="24" class="action-button">
+        <a-button
+          :loading="loading"
+          @click="closeModal">
+          {{ $t('label.cancel') }}
+        </a-button>
+        <a-button
+          :loading="loading"
+          ref="submit"
+          type="primary"
+          htmlType="submit">
+          {{ $t('label.ok') }}
+        </a-button>
+      </div>
+    </a-form>
+  </a-modal>
+</template>
+
+<script>
+
+import { reactive, ref, toRaw } from 'vue'
+import { api } from '@/api'
+import ListView from '@/components/view/ListView'
+import Status from '@/components/widgets/Status'
+import TooltipLabel from '@/components/widgets/TooltipLabel'
+import { mixinForm } from '@/utils/mixin'
+import { timeZone } from '@/utils/timezone'
+import debounce from 'lodash/debounce'
+import cronstrue from 'cronstrue/i18n'
+import moment from 'moment-timezone'
+
+export default {
+  name: 'InstanceSchedules',
+  mixins: [mixinForm],
+  components: {
+    Status,
+    ListView,
+    TooltipLabel
+  },
+  props: {
+    virtualmachine: {
+      type: Object,
+      required: true
+    },
+    loading: {
+      type: Boolean,
+      required: true
+    }
+  },
+  data () {
+    this.fetchTimeZone = debounce(this.fetchTimeZone, 800)
+    return {
+      tabLoading: false,
+      columnKeys: ['action', 'enabled', 'description', 'schedule', 'timezone', 'startdate', 'enddate', 'created', 'vmScheduleActions'],
+      selectedColumnKeys: [],
+      columns: [],
+      schedules: [],
+      timeZoneMap: [],
+      actions: [
+        { value: 'START', label: 'label.start' },
+        { value: 'STOP', label: 'label.stop' },
+        { value: 'REBOOT', label: 'label.reboot' },
+        { value: 'FORCE_STOP', label: 'label.force.stop' },
+        { value: 'FORCE_REBOOT', label: 'label.force.reboot' }
+      ],
+      periods: [
+        { id: 'year', value: ['month', 'day', 'dayOfWeek', 'hour', 'minute'] },
+        { id: 'month', value: ['day', 'dayOfWeek', 'hour', 'minute'] },
+        { id: 'week', value: ['dayOfWeek', 'hour', 'minute'] },
+        { id: 'day', value: ['hour', 'minute'] }
+      ],
+      page: 1,
+      pageSize: 20,
+      totalCount: 0,
+      showModal: false,
+      isSubmitted: false,
+      isEdit: false,
+      error: '',
+      pattern: 'YYYY-MM-DD HH:mm:ss'
+    }
+  },
+  beforeCreate () {
+    this.apiParams = this.$getApiParams('createVMSchedule')
+  },
+  computed: {
+    pageSizeOptions () {
+      var sizes = [20, 50, 100, 200, this.$store.getters.defaultListViewPageSize]
+      if (this.device !== 'desktop') {
+        sizes.unshift(10)
+      }
+      return [...new Set(sizes)].sort(function (a, b) {
+        return a - b
+      }).map(String)
+    }
+  },
+  created () {
+    this.selectedColumnKeys = this.columnKeys
+    this.updateColumns()
+    this.pageSize = this.pageSizeOptions[0] * 1
+    this.initForm()
+    this.fetchData()
+    this.fetchTimeZone()
+  },
+  watch: {
+    virtualmachine: {
+      handler () {
+        this.fetchSchedules()
+      }
+    }
+  },
+  methods: {
+    initForm () {
+      this.formRef = ref()
+      this.form = reactive({
+        action: 'START',
+        schedule: '* * * * *',
+        description: '',
+        timezone: 'UTC',
+        startDate: '',
+        endDate: '',
+        enabled: true,
+        useCronFormat: false
+      })
+      this.rules = reactive({
+        schedule: [{ type: 'string', required: true, message: this.$t('message.error.required.input') }],
+        action: [{ type: 'string', required: true, message: this.$t('message.error.required.input') }],
+        timezone: [{ required: true, message: `${this.$t('message.error.select')}` }],
+        startDate: [{ required: false, message: `${this.$t('message.error.select')}` }],
+        endDate: [{ required: false, message: `${this.$t('message.error.select')}` }]
+      })
+    },
+    createVMSchedule (schedule) {
+      this.resetForm()
+      this.showAddModal()
+    },
+    removeVMSchedule (schedule) {
+      api('deleteVMSchedule', {
+        id: schedule.id,
+        virtualmachineid: this.virtualmachine.id
+      }).then(() => {
+        if (this.totalCount - 1 === this.pageSize * (this.page - 1)) {
+          this.page = this.page - 1 > 0 ? this.page - 1 : 1
+        }
+        const message = `${this.$t('label.removing')} ${schedule.description}`
+        this.$message.success(message)
+      }).catch(error => {
+        console.error(error)
+        this.$message.error(this.$t('message.error.remove.vm.schedule'))
+        this.$notification.error({
+          message: this.$t('label.error'),
+          description: this.$t('message.error.remove.vm.schedule')
+        })
+      }).finally(() => {
+        this.fetchData()
+      })
+    },
+    updateVMSchedule (schedule) {
+      this.resetForm()
+      this.isEdit = true
+      Object.assign(this.form, schedule)
+      // Some weird issue when we directly pass in the moment with tz object
+      this.form.startDate = moment(moment(schedule.startdate).tz(schedule.timezone).format(this.pattern))
+      this.form.endDate = schedule.enddate ? moment(moment(schedule.enddate).tz(schedule.timezone).format(this.pattern)) : ''
+      this.showAddModal()
+    },
+    showAddModal () {
+      this.showModal = true
+    },
+    submitForm () {
+      if (this.isSubmitted) return
+      this.isSubmitted = true
+      this.formRef.value.validate().then(() => {
+        const formRaw = toRaw(this.form)
+        const values = this.handleRemoveFields(formRaw)
+        var params = {
+          description: values.description,
+          schedule: values.schedule,
+          timezone: values.timezone,
+          action: values.action,
+          virtualmachineid: this.virtualmachine.id,
+          enabled: values.enabled,
+          startdate: (values.startDate) ? values.startDate.format(this.pattern) : null,
+          enddate: (values.endDate) ? values.endDate.format(this.pattern) : null
+        }
+        let command = null
+        if (this.form.id === null || this.form.id === undefined) {
+          command = 'createVMSchedule'
+        } else {
+          params.id = this.form.id
+          command = 'updateVMSchedule'
+        }
+
+        api(command, params).then(response => {
+          this.$notification.success({
+            message: this.$t('label.schedule'),
+            description: this.$t('message.success.config.vm.schedule')
+          })
+          this.isSubmitted = false
+          this.fetchData()
+          this.closeModal()
+        }).catch(error => {
+          this.$notifyError(error)
+          this.isSubmitted = false
+        })
+      }).catch(error => {
+        this.$notifyError(error)
+        if (error.errorFields !== undefined) {
+          this.formRef.value.scrollToField(error.errorFields[0].name)
+        }
+      }).finally(() => {
+        this.isSubmitted = false
+      })
+    },
+    resetForm () {
+      this.isEdit = false
+      if (this.formRef.value) {
+        this.formRef.value.resetFields()
+      }
+    },
+    fetchTimeZone (value) {
+      this.timeZoneMap = []
+      this.fetching = true
+
+      timeZone(value).then(json => {
+        this.timeZoneMap = json
+        this.fetching = false
+      })
+    },
+    closeModal () {
+      this.resetForm()
+      this.initForm()
+      this.showModal = false
+    },
+    fetchData () {
+      this.fetchSchedules()
+    },
+    fetchSchedules () {
+      this.schedules = []
+      if (!this.virtualmachine.id) {
+        return
+      }
+      const params = {
+        page: this.page,
+        pagesize: this.pageSize,
+        virtualmachineid: this.virtualmachine.id,
+        listall: true
+      }
+      this.tabLoading = true
+      api('listVMSchedule', params).then(json => {
+        this.schedules = []
+        this.totalCount = json?.listvmscheduleresponse?.count || 0
+        this.schedules = json?.listvmscheduleresponse?.vmschedule || []
+        this.tabLoading = false
+      })
+    },
+    changePage (page, pageSize) {
+      this.page = page
+      this.pageSize = pageSize
+      this.fetchData()
+    },
+    updateSelectedColumns (key) {
+      if (this.selectedColumnKeys.includes(key)) {
+        this.selectedColumnKeys = this.selectedColumnKeys.filter(x => x !== key)
+      } else {
+        this.selectedColumnKeys.push(key)
+      }
+      this.updateColumns()
+    },
+    generateHumanReadableSchedule (schedule) {
+      return cronstrue.toString(schedule, { locale: this.$i18n.locale, throwExceptionOnParseError: false })
+    },
+    updateColumns () {
+      this.columns = []
+      for (var columnKey of this.columnKeys) {
+        if (!this.selectedColumnKeys.includes(columnKey)) continue
+        this.columns.push({
+          key: columnKey,
+          // If columnKey is 'enabled', then title is 'state'
+          // If columnKey is 'startdate', then the title is `start.date.and.time`
+          // else title is columnKey
+          title: columnKey === 'enabled'
+            ? this.$t('label.state')
+            : columnKey === 'startdate'
+              ? this.$t('label.start.date.and.time')
+              : columnKey === 'enddate'
+                ? this.$t('label.end.date.and.time')
+                : this.$t('label.' + String(columnKey).toLowerCase()),
+          dataIndex: columnKey
+        })
+      }
+      if (this.columns.length > 0) {
+        this.columns[this.columns.length - 1].customFilterDropdown = true
+      }
+    }
+  }
+}
+</script>
diff --git a/ui/src/views/compute/InstanceTab.vue b/ui/src/views/compute/InstanceTab.vue
index 2b1572b..927fa3d 100644
--- a/ui/src/views/compute/InstanceTab.vue
+++ b/ui/src/views/compute/InstanceTab.vue
@@ -116,6 +116,11 @@
           :routerlinks="(record) => { return { name: '/securitygroups/' + record.id } }"
           :showSearch="false"/>
       </a-tab-pane>
+      <a-tab-pane :tab="$t('label.schedules')" key="schedules" v-if="'listVMSchedule' in $store.getters.apis">
+        <InstanceSchedules
+          :virtualmachine="vm"
+          :loading="loading"/>
+      </a-tab-pane>
       <a-tab-pane :tab="$t('label.settings')" key="settings">
         <DetailSettings :resource="dataResource" :loading="loading" />
       </a-tab-pane>
@@ -192,9 +197,9 @@
             :loading="listIps.loading"
             v-focus="editNicResource.type==='Shared'"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
             <a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
               {{ ip.ipaddress }}
@@ -235,9 +240,9 @@
             :loading="listIps.loading"
             v-focus="editNicResource.type==='Shared'"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
             <a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
               {{ ip.ipaddress }}
@@ -290,6 +295,7 @@
 import EventsTab from '@/components/view/EventsTab'
 import DetailSettings from '@/components/view/DetailSettings'
 import NicsTable from '@/views/network/NicsTable'
+import InstanceSchedules from '@/views/compute/InstanceSchedules.vue'
 import ListResourceTable from '@/components/view/ListResourceTable'
 import TooltipButton from '@/components/widgets/TooltipButton'
 import ResourceIcon from '@/components/view/ResourceIcon'
@@ -305,6 +311,7 @@
     EventsTab,
     DetailSettings,
     NicsTable,
+    InstanceSchedules,
     ListResourceTable,
     TooltipButton,
     ResourceIcon,
diff --git a/ui/src/views/compute/KubernetesServiceTab.vue b/ui/src/views/compute/KubernetesServiceTab.vue
index 96a9958..b3556cb6 100644
--- a/ui/src/views/compute/KubernetesServiceTab.vue
+++ b/ui/src/views/compute/KubernetesServiceTab.vue
@@ -25,7 +25,7 @@
       <a-tab-pane :tab="$t('label.details')" key="details">
         <DetailsTab :resource="resource" :loading="loading" />
       </a-tab-pane>
-      <a-tab-pane :tab="$t('label.access')" key="access">
+      <a-tab-pane v-if="resource.clustertype == 'CloudManaged'" :tab="$t('label.access')" key="access">
         <a-card :title="$t('label.kubeconfig.cluster')" :loading="versionLoading">
           <div v-if="clusterConfig !== ''">
             <a-textarea :value="clusterConfig" :rows="5" readonly />
@@ -106,36 +106,37 @@
           :rowKey="item => item.id"
           :pagination="false"
         >
-          <template #name="{ text, record }" :name="text">
-            <router-link :to="{ path: '/vm/' + record.id }">{{ record.name }}</router-link>
-          </template>
-          <template #state="{ text }">
-            <status :text="text ? text : ''" displayText />
-          </template>
-          <template #port="{ text, record, index }" :name="text" :record="record">
-            {{ cksSshStartingPort + index }}
-          </template>
-          <template #action="{record}">
-            <a-tooltip placement="bottom" >
-              <template #title>
-                {{ $t('label.action.delete.node') }}
-              </template>
-              <a-popconfirm
-                :title="$t('message.action.delete.node')"
-                @confirm="deleteNode(record)"
-                :okText="$t('label.yes')"
-                :cancelText="$t('label.no')"
-                :disabled="!['Created', 'Running'].includes(resource.state) || resource.autoscalingenabled"
-              >
-                <a-button
-                  danger
-                  type="primary"
-                  shape="circle"
-                  :disabled="!['Created', 'Running'].includes(resource.state) || resource.autoscalingenabled">
-                  <template #icon><delete-outlined /></template>
-                </a-button>
-              </a-popconfirm>
-            </a-tooltip>
+          <template #bodyCell="{ column, text, record, index }">
+            <template v-if="column.key === 'name'" :name="text">
+              <router-link :to="{ path: '/vm/' + record.id }">{{ record.name }}</router-link>
+            </template>
+            <template v-if="column.key === 'state'">
+              <status :text="text ? text : ''" displayText />
+            </template>
+            <template v-if="column.key === 'port'" :name="text" :record="record">
+              {{ cksSshStartingPort + index }}
+            </template>
+            <template v-if="column.key === 'actions'">
+              <a-tooltip placement="bottom" >
+                <template #title>
+                  {{ $t('label.action.delete.node') }}
+                </template>
+                <a-popconfirm
+                  :title="$t('message.action.delete.node')"
+                  @confirm="deleteNode(record)"
+                  :okText="$t('label.yes')"
+                  :cancelText="$t('label.no')"
+                  :disabled="!['Created', 'Running'].includes(resource.state) || resource.autoscalingenabled"
+                >
+                  <a-button
+                    type="danger"
+                    shape="circle"
+                    :disabled="!['Created', 'Running'].includes(resource.state) || resource.autoscalingenabled">
+                    <template #icon><delete-outlined /></template>
+                  </a-button>
+                </a-popconfirm>
+              </a-tooltip>
+            </template>
           </template>
         </a-table>
       </a-tab-pane>
@@ -214,14 +215,14 @@
   created () {
     this.vmColumns = [
       {
+        key: 'name',
         title: this.$t('label.name'),
-        dataIndex: 'name',
-        slots: { customRender: 'name' }
+        dataIndex: 'name'
       },
       {
+        key: 'state',
         title: this.$t('label.state'),
-        dataIndex: 'state',
-        slots: { customRender: 'state' }
+        dataIndex: 'state'
       },
       {
         title: this.$t('label.instancename'),
@@ -232,9 +233,9 @@
         dataIndex: 'ipaddress'
       },
       {
+        key: 'port',
         title: this.$t('label.ssh.port'),
-        dataIndex: 'port',
-        slots: { customRender: 'port' }
+        dataIndex: 'port'
       },
       {
         title: this.$t('label.zonename'),
@@ -244,6 +245,9 @@
     if (!isAdmin()) {
       this.vmColumns = this.vmColumns.filter(x => x.dataIndex !== 'instancename')
     }
+    if (this.resource.clustertype === 'ExternalManaged') {
+      this.vmColumns = this.vmColumns.filter(x => x.dataIndex !== 'port')
+    }
     this.handleFetchData()
     const self = this
     window.addEventListener('popstate', function () {
@@ -271,9 +275,9 @@
   mounted () {
     if (this.$store.getters.apis.scaleKubernetesCluster.params.filter(x => x.name === 'nodeids').length > 0) {
       this.vmColumns.push({
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        slots: { customRender: 'action' }
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions'
       })
     }
     this.handleFetchData()
diff --git a/ui/src/views/compute/MigrateWizard.vue b/ui/src/views/compute/MigrateWizard.vue
index 9687138..8aad50d 100644
--- a/ui/src/views/compute/MigrateWizard.vue
+++ b/ui/src/views/compute/MigrateWizard.vue
@@ -37,45 +37,47 @@
       :dataSource="hosts"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #name="{ record }">
-        {{ record.name }}
-        <a-tooltip v-if="record.name === $t('label.auto.assign')" :title="$t('message.migrate.instance.host.auto.assign')" placement="top">
-          <info-circle-outlined class="table-tooltip-icon" />
-        </a-tooltip>
-      </template>
-      <template #suitability="{ record }">
-        <check-circle-two-tone
-          class="host-item__suitability-icon"
-          twoToneColor="#52c41a"
-          v-if="record.suitableformigration" />
-        <close-circle-two-tone
-          class="host-item__suitability-icon"
-          twoToneColor="#f5222d"
-          v-else />
-      </template>
-      <template #memused="{ record }">
-        <span v-if="record.memoryused">
-          {{ $bytesToHumanReadableSize(record.memoryused) }}
-        </span>
-      </template>
-      <template #memoryallocatedpercentage="{ record }">
-        {{ record.memoryallocatedpercentage }}
-      </template>
-      <template #cluster="{ record }">
-        {{ record.clustername }}
-      </template>
-      <template #pod="{ record }">
-        {{ record.podname }}
-      </template>
-      <template #requiresstoragemigration="{ record }">
-        {{ record.requiresStorageMotion ? $t('label.yes') : $t('label.no') }}
-      </template>
-      <template #select="{record}">
-        <a-radio
-          class="host-item__radio"
-          @click="handleSelectedHostChange(record)"
-          :checked="record.id === selectedHost.id"
-          :disabled="!record.suitableformigration"></a-radio>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          {{ record.name }}
+          <a-tooltip v-if="record.name === $t('label.auto.assign')" :title="$t('message.migrate.instance.host.auto.assign')" placement="top">
+            <info-circle-outlined class="table-tooltip-icon" />
+          </a-tooltip>
+        </template>
+        <template v-if="column.key === 'suitability'">
+          <check-circle-two-tone
+            class="host-item__suitability-icon"
+            twoToneColor="#52c41a"
+            v-if="record.suitableformigration" />
+          <close-circle-two-tone
+            class="host-item__suitability-icon"
+            twoToneColor="#f5222d"
+            v-else />
+        </template>
+        <template v-if="column.key === 'memused'">
+          <span v-if="record.memoryused">
+            {{ $bytesToHumanReadableSize(record.memoryused) }}
+          </span>
+        </template>
+        <template v-if="column.key === 'memoryallocatedpercentage'">
+          {{ record.memoryallocatedpercentage }}
+        </template>
+        <template v-if="column.key === 'cluster'">
+          {{ record.clustername }}
+        </template>
+        <template v-if="column.key === 'pod'">
+          {{ record.podname }}
+        </template>
+        <template v-if="column.key === 'requiresstoragemigration'">
+          {{ record.requiresStorageMotion ? $t('label.yes') : $t('label.no') }}
+        </template>
+        <template v-if="column.key === 'select'">
+          <a-radio
+            class="host-item__radio"
+            @click="handleSelectedHostChange(record)"
+            :checked="record.id === selectedHost.id"
+            :disabled="!record.suitableformigration"></a-radio>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -149,44 +151,45 @@
       pageSize: 10,
       columns: [
         {
-          title: this.$t('label.hostid'),
-          slots: { customRender: 'name' }
+          key: 'name',
+          title: this.$t('label.hostid')
         },
         {
-          title: this.$t('label.suitability'),
-          slots: { customRender: 'suitability' }
+          key: 'suitability',
+          title: this.$t('label.suitability')
         },
         {
           title: this.$t('label.cpuused'),
           dataIndex: 'cpuused'
         },
         {
-          title: this.$t('label.memoryallocated'),
-          slots: { customRender: 'memoryallocatedpercentage' }
+          key: 'memoryallocatedpercentage',
+          title: this.$t('label.memoryallocated')
         },
         {
-          title: this.$t('label.memused'),
-          slots: { customRender: 'memused' }
+          key: 'memused',
+          title: this.$t('label.memused')
         },
         {
-          title: this.$t('label.cluster'),
-          slots: { customRender: 'cluster' }
+          key: 'cluster',
+          title: this.$t('label.cluster')
         },
         {
-          title: this.$t('label.pod'),
-          slots: { customRender: 'pod' }
+          key: 'pod',
+          title: this.$t('label.pod')
         },
         {
-          title: this.$t('label.storage.migration.required'),
-          slots: { customRender: 'requiresstoragemigration' }
+          key: 'requiresstoragemigration',
+          title: this.$t('label.storage.migration.required')
         },
         {
-          title: this.$t('label.select'),
-          slots: { customRender: 'select' }
+          key: 'select',
+          title: this.$t('label.select')
         }
       ],
       migrateWithStorage: false,
-      volumeToPoolSelection: []
+      volumeToPoolSelection: [],
+      volumes: []
     }
   },
   created () {
@@ -246,6 +249,7 @@
     handleSelectedHostChange (host) {
       if (host.id === -1) {
         this.migrateWithStorage = false
+        this.fetchVolumes()
       }
       this.selectedHost = host
       this.selectedVolumeForStoragePoolSelection = {}
@@ -257,6 +261,31 @@
     handleVolumeToPoolChange (volumeToPool) {
       this.volumeToPoolSelection = volumeToPool
     },
+    fetchVolumes () {
+      this.loading = true
+      this.volumes = []
+      api('listVolumes', {
+        listAll: true,
+        virtualmachineid: this.resource.id
+      }).then(response => {
+        this.volumes = response.listvolumesresponse.volume
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    requiresStorageMigration () {
+      if (this.selectedHost.requiresStorageMotion || this.volumeToPoolSelection.length > 0) {
+        return true
+      }
+      if (this.selectedHost.id === -1 && this.volumes && this.volumes.length > 0) {
+        for (var volume of this.volumes) {
+          if (volume.storagetype === 'local') {
+            return true
+          }
+        }
+      }
+      return false
+    },
     handleKeyboardSubmit () {
       if (this.selectedHost.id) {
         this.submitForm()
@@ -269,7 +298,7 @@
       if (this.loading) return
       this.loading = true
       const migrateApi = this.isUserVm
-        ? (this.selectedHost.requiresStorageMotion || this.volumeToPoolSelection.length > 0)
+        ? this.requiresStorageMigration()
           ? 'migrateVirtualMachineWithVolume'
           : 'migrateVirtualMachine'
         : 'migrateSystemVm'
diff --git a/ui/src/views/compute/RegisterUserData.vue b/ui/src/views/compute/RegisterUserData.vue
index a8e5577..a1322a1 100644
--- a/ui/src/views/compute/RegisterUserData.vue
+++ b/ui/src/views/compute/RegisterUserData.vue
@@ -51,9 +51,9 @@
             mode="tags"
             v-model:value="form.params"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="apiParams.params.description">
             <a-select-option v-for="opt in params" :key="opt">
@@ -71,12 +71,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
               :loading="domainLoading"
             :placeholder="apiParams.domainid.description"
             @change="val => { handleDomainChanged(domains[val]) }">
-            <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex">
+            <a-select-option
+              v-for="(opt, optIndex) in domains"
+              :key="optIndex"
+              :label="opt.path || opt.name || opt.description || ''">
               {{ opt.path || opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/ResetSshKeyPair.vue b/ui/src/views/compute/ResetSshKeyPair.vue
index 12c0e6c..7babb95 100644
--- a/ui/src/views/compute/ResetSshKeyPair.vue
+++ b/ui/src/views/compute/ResetSshKeyPair.vue
@@ -42,8 +42,10 @@
           @handle-search-filter="handleTableChange"
           style="overflow-y: auto" >
 
-          <template #account><user-outlined /> {{ $t('label.account') }}</template>
-          <template #domain><block-outlined /> {{ $t('label.domain') }}</template>
+          <template #headerCell="{ column }">
+            <template v-if="column.key === 'account'"><user-outlined /> {{ $t('label.account') }}</template>
+            <template v-if="column.key === 'domain'"><block-outlined /> {{ $t('label.domain') }}</template>
+          </template>
 
         </a-table>
       </div>
@@ -81,13 +83,13 @@
           width: '40%'
         },
         {
+          key: 'account',
           dataIndex: 'account',
-          slots: { title: 'account' },
           width: '30%'
         },
         {
+          key: 'domain',
           dataIndex: 'domain',
-          slots: { title: 'domain' },
           width: '30%'
         }
       ],
diff --git a/ui/src/views/compute/ResetUserData.vue b/ui/src/views/compute/ResetUserData.vue
index 46561f1..7be1dad 100644
--- a/ui/src/views/compute/ResetUserData.vue
+++ b/ui/src/views/compute/ResetUserData.vue
@@ -41,8 +41,10 @@
               :dataSource="templateUserDataParams"
               :pagination="false"
               :rowKey="record => record.key">
-              <template #value="{ record }">
-                <a-input v-model:value="templateUserDataValues[record.key]" />
+              <template #bodyCell="{ column, record }">
+                <template v-if="column.key === 'value'">
+                  <a-input v-model:value="templateUserDataValues[record.key]" />
+                </template>
               </template>
             </a-table>
           </a-input-group>
@@ -87,8 +89,10 @@
                               :dataSource="userDataParams"
                               :pagination="false"
                               :rowKey="record => record.key">
-                              <template #value="{ record }">
-                                <a-input v-model:value="userDataValues[record.key]" />
+                              <template #bodyCell="{ column, record }">
+                                <template v-if="column.key === 'value'">
+                                  <a-input v-model:value="userDataValues[record.key]" />
+                                </template>
                               </template>
                             </a-table>
                           </a-input-group>
@@ -152,12 +156,12 @@
         },
         {
           dataIndex: 'account',
-          slots: { title: 'account' },
+          title: this.$t('account'),
           width: '30%'
         },
         {
           dataIndex: 'domain',
-          slots: { title: 'domain' },
+          title: this.$t('domain'),
           width: '30%'
         }
       ],
@@ -188,7 +192,7 @@
         {
           title: this.$t('label.value'),
           dataIndex: 'value',
-          slots: { customRender: 'value' }
+          key: 'value'
         }
       ],
       userDataValues: {},
@@ -301,7 +305,7 @@
       this.userDataParams = []
       api('listUserData', { id: id }).then(json => {
         const resp = json?.listuserdataresponse?.userdata || []
-        if (resp) {
+        if (resp.length > 0) {
           var params = resp[0].params
           if (params) {
             var dataParams = params.split(',')
diff --git a/ui/src/views/compute/ScaleKubernetesCluster.vue b/ui/src/views/compute/ScaleKubernetesCluster.vue
index f9af2ef..8d73ac8 100644
--- a/ui/src/views/compute/ScaleKubernetesCluster.vue
+++ b/ui/src/views/compute/ScaleKubernetesCluster.vue
@@ -38,11 +38,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="serviceOfferingLoading"
             :placeholder="apiParams.serviceofferingid.description">
-            <a-select-option v-for="(opt, optIndex) in serviceOfferings" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in serviceOfferings" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/StartVirtualMachine.vue b/ui/src/views/compute/StartVirtualMachine.vue
index 4440153..7438ebc 100644
--- a/ui/src/views/compute/StartVirtualMachine.vue
+++ b/ui/src/views/compute/StartVirtualMachine.vue
@@ -38,13 +38,13 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="podsLoading"
               :placeholder="apiParams.podid.description"
               @change="handlePodChange"
               v-focus="$store.getters.userInfo.roletype === 'Admin'">
-              <a-select-option v-for="pod in pods" :key="pod.id">
+              <a-select-option v-for="pod in pods" :key="pod.id" :label="pod.name">
                 {{ pod.name }}
               </a-select-option>
             </a-select>
@@ -59,12 +59,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="clustersLoading"
               :placeholder="apiParams.clusterid.description"
               @change="handleClusterChange">
-              <a-select-option v-for="cluster in clusters" :key="cluster.id">
+              <a-select-option v-for="cluster in clusters" :key="cluster.id" :label="cluster.name">
                 {{ cluster.name }}
               </a-select-option>
             </a-select>
@@ -79,11 +79,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="hostsLoading"
               :placeholder="apiParams.hostid.description">
-              <a-select-option v-for="host in hosts" :key="host.id">
+              <a-select-option v-for="host in hosts" :key="host.id" :label="host.name">
                 {{ host.name }}
               </a-select-option>
             </a-select>
diff --git a/ui/src/views/compute/UpgradeKubernetesCluster.vue b/ui/src/views/compute/UpgradeKubernetesCluster.vue
index 02d4ffe..22cfb0f 100644
--- a/ui/src/views/compute/UpgradeKubernetesCluster.vue
+++ b/ui/src/views/compute/UpgradeKubernetesCluster.vue
@@ -38,12 +38,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="kubernetesVersionLoading"
             :placeholder="apiParams.kubernetesversionid.description"
             v-focus="true" >
-            <a-select-option v-for="(opt, optIndex) in this.kubernetesVersions" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in this.kubernetesVersions" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/compute/backup/BackupSchedule.vue b/ui/src/views/compute/backup/BackupSchedule.vue
index 914a112..26c655a 100644
--- a/ui/src/views/compute/backup/BackupSchedule.vue
+++ b/ui/src/views/compute/backup/BackupSchedule.vue
@@ -24,49 +24,51 @@
       :rowKey="record => record.virtualmachineid"
       :pagination="false"
       :loading="loading">
-      <template #icon="{ text, record }" :name="text">
-        <label class="interval-icon">
-          <span v-if="record.intervaltype==='HOURLY'">
-            <clock-circle-outlined />
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'icon'" :name="text">
+          <label class="interval-icon">
+            <span v-if="record.intervaltype==='HOURLY'">
+              <clock-circle-outlined />
+            </span>
+            <span class="custom-icon icon-daily" v-else-if="record.intervaltype==='DAILY'">
+              <calendar-outlined />
+            </span>
+            <span class="custom-icon icon-weekly" v-else-if="record.intervaltype==='WEEKLY'">
+              <calendar-outlined />
+            </span>
+            <span class="custom-icon icon-monthly" v-else-if="record.intervaltype==='MONTHLY'">
+              <calendar-outlined />
+            </span>
+          </label>
+        </template>
+        <template v-if="column.key === 'time'" :name="text">
+          <label class="interval-content">
+            <span v-if="record.intervaltype==='HOURLY'">{{ record.schedule + ' ' + $t('label.min.past.hour') }}</span>
+            <span v-else>{{ record.schedule.split(':')[1] + ':' + record.schedule.split(':')[0] }}</span>
+          </label>
+        </template>
+        <template v-if="column.key === 'interval'" :name="text">
+          <span v-if="record.intervaltype==='WEEKLY'">
+            {{ `${$t('label.every')} ${$t(listDayOfWeek[record.schedule.split(':')[2] - 1])}` }}
           </span>
-          <span class="custom-icon icon-daily" v-else-if="record.intervaltype==='DAILY'">
-            <calendar-outlined />
+          <span v-else-if="record.intervaltype==='MONTHLY'">
+            {{ `${$t('label.day')} ${record.schedule.split(':')[2]} ${$t('label.of.month')}` }}
           </span>
-          <span class="custom-icon icon-weekly" v-else-if="record.intervaltype==='WEEKLY'">
-            <calendar-outlined />
-          </span>
-          <span class="custom-icon icon-monthly" v-else-if="record.intervaltype==='MONTHLY'">
-            <calendar-outlined />
-          </span>
-        </label>
-      </template>
-      <template #time="{ text, record }" :name="text">
-        <label class="interval-content">
-          <span v-if="record.intervaltype==='HOURLY'">{{ record.schedule + ' ' + $t('label.min.past.hour') }}</span>
-          <span v-else>{{ record.schedule.split(':')[1] + ':' + record.schedule.split(':')[0] }}</span>
-        </label>
-      </template>
-      <template #interval="{ text, record }" :name="text">
-        <span v-if="record.intervaltype==='WEEKLY'">
-          {{ `${$t('label.every')} ${$t(listDayOfWeek[record.schedule.split(':')[2] - 1])}` }}
-        </span>
-        <span v-else-if="record.intervaltype==='MONTHLY'">
-          {{ `${$t('label.day')} ${record.schedule.split(':')[2]} ${$t('label.of.month')}` }}
-        </span>
-      </template>
-      <template #timezone="{ text, record }" :name="text">
-        <label>{{ getTimeZone(record.timezone) }}</label>
-      </template>
-      <template #action="{ text, record }" class="account-button-action" :name="text">
-        <tooltip-button
-          tooltipPlacement="top"
-          :tooltip="$t('label.delete')"
-          type="primary"
-          :danger="true"
-          icon="close-outlined"
-          size="small"
-          :loading="actionLoading"
-          @onClick="handleClickDelete(record)"/>
+        </template>
+        <template v-if="column.key === 'timezone'" :name="text">
+          <label>{{ getTimeZone(record.timezone) }}</label>
+        </template>
+        <template v-if="column.key === 'actions'" class="account-button-action" :name="text">
+          <tooltip-button
+            tooltipPlacement="top"
+            :tooltip="$t('label.delete')"
+            type="primary"
+            :danger="true"
+            icon="close-outlined"
+            size="small"
+            :loading="actionLoading"
+            @onClick="handleClickDelete(record)"/>
+        </template>
       </template>
     </a-table>
   </div>
@@ -107,31 +109,31 @@
     columns () {
       return [
         {
+          key: 'icon',
           title: '',
           dataIndex: 'icon',
-          width: 30,
-          slots: { customRender: 'icon' }
+          width: 30
         },
         {
+          key: 'time',
           title: this.$t('label.time'),
-          dataIndex: 'schedule',
-          slots: { customRender: 'time' }
+          dataIndex: 'schedule'
         },
         {
+          key: 'interval',
           title: '',
-          dataIndex: 'interval',
-          slots: { customRender: 'interval' }
+          dataIndex: 'interval'
         },
         {
+          key: 'timezone',
           title: this.$t('label.timezone'),
-          dataIndex: 'timezone',
-          slots: { customRender: 'timezone' }
+          dataIndex: 'timezone'
         },
         {
-          title: this.$t('label.action'),
-          dataIndex: 'action',
-          width: 80,
-          slots: { customRender: 'action' }
+          key: 'actions',
+          title: this.$t('label.actions'),
+          dataIndex: 'actions',
+          width: 80
         }
       ]
     }
diff --git a/ui/src/views/compute/backup/FormSchedule.vue b/ui/src/views/compute/backup/FormSchedule.vue
index d1512623..3b8db9d 100644
--- a/ui/src/views/compute/backup/FormSchedule.vue
+++ b/ui/src/views/compute/backup/FormSchedule.vue
@@ -70,7 +70,8 @@
                 <a-time-picker
                   use12Hours
                   format="h:mm A"
-                  v-model:value="form.timeSelect" />
+                  v-model:value="form.timeSelect"
+                  style="width: 100%;" />
               </a-form-item>
             </a-col>
             <a-col :md="24" :lg="12" v-if="form.intervaltype==='weekly'">
@@ -80,9 +81,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
-                  <a-select-option v-for="(opt, optIndex) in dayOfWeek" :key="optIndex">
+                  <a-select-option v-for="(opt, optIndex) in dayOfWeek" :key="optIndex" :label="opt.name || opt.description">
                     {{ opt.name || opt.description }}
                   </a-select-option>
                 </a-select>
@@ -95,9 +96,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }">
-                  <a-select-option v-for="opt in dayOfMonth" :key="opt.name">
+                  <a-select-option v-for="opt in dayOfMonth" :key="opt.name" :label="opt.name || opt.description">
                     {{ opt.name }}
                   </a-select-option>
                 </a-select>
@@ -110,10 +111,10 @@
                   v-model:value="form.timezone"
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }"
                   :loading="fetching">
-                  <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+                  <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
                     {{ opt.name || opt.description }}
                   </a-select-option>
                 </a-select>
diff --git a/ui/src/views/compute/wizard/ComputeOfferingSelection.vue b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
index fb1ab66..aa97f36 100644
--- a/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
+++ b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -32,8 +32,10 @@
       size="middle"
       :scroll="{ y: 225 }"
     >
-      <template #cpuTitle><appstore-outlined /> {{ $t('label.cpu') }}</template>
-      <template #ramTitle><bulb-outlined /> {{ $t('label.memory') }}</template>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'cpu'"><appstore-outlined /> {{ $t('label.cpu') }}</template>
+        <template v-if="column.key === 'ram'"><bulb-outlined /> {{ $t('label.memory') }}</template>
+      </template>
     </a-table>
 
     <div style="display: block; text-align: right;">
@@ -109,18 +111,19 @@
       filter: '',
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
           title: this.$t('label.serviceofferingid'),
           width: '40%'
         },
         {
+          key: 'cpu',
           dataIndex: 'cpu',
-          slots: { title: 'cpuTitle' },
           width: '30%'
         },
         {
+          key: 'ram',
           dataIndex: 'ram',
-          slots: { title: 'ramTitle' },
           width: '30%'
         }
       ],
diff --git a/ui/src/views/compute/wizard/DiskOfferingSelection.vue b/ui/src/views/compute/wizard/DiskOfferingSelection.vue
index 6262795..ee103aa 100644
--- a/ui/src/views/compute/wizard/DiskOfferingSelection.vue
+++ b/ui/src/views/compute/wizard/DiskOfferingSelection.vue
@@ -32,18 +32,23 @@
       size="middle"
       :scroll="{ y: 225 }"
     >
-      <template #diskSizeTitle><hdd-outlined /> {{ $t('label.disksize') }}</template>
-      <template #iopsTitle><rocket-outlined /> {{ $t('label.minmaxiops') }}</template>
-      <template #diskSize="{ record }">
-        <div v-if="record.isCustomized">{{ $t('label.iscustomized') }}</div>
-        <div v-else-if="record.diskSize">{{ record.diskSize }} GB</div>
-        <div v-else>-</div>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'diskSize'"><hdd-outlined /> {{ $t('label.disksize') }}</template>
+        <template v-if="column.key === 'iops'"><rocket-outlined /> {{ $t('label.minmaxiops') }}</template>
       </template>
-      <template #iops="{ record }">
-        <span v-if="record.miniops && record.maxiops">{{ record.miniops }} - {{ record.maxiops }}</span>
-        <span v-else-if="record.miniops && !record.maxiops">{{ record.miniops }}</span>
-        <span v-else-if="!record.miniops && record.maxiops">{{ record.maxiops }}</span>
-        <span v-else>-</span>
+
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'diskSize'">
+          <div v-if="record.isCustomized">{{ $t('label.iscustomized') }}</div>
+          <div v-else-if="record.diskSize">{{ record.diskSize }} GB</div>
+          <div v-else>-</div>
+        </template>
+        <template v-if="column.key === 'iops'">
+          <span v-if="record.miniops && record.maxiops">{{ record.miniops }} - {{ record.maxiops }}</span>
+          <span v-else-if="record.miniops && !record.maxiops">{{ record.miniops }}</span>
+          <span v-else-if="!record.miniops && record.maxiops">{{ record.maxiops }}</span>
+          <span v-else>-</span>
+        </template>
       </template>
     </a-table>
 
@@ -108,19 +113,20 @@
       filter: '',
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
           title: this.$t('label.diskoffering'),
           width: '40%'
         },
         {
+          key: 'diskSize',
           dataIndex: 'disksize',
-          width: '30%',
-          slots: { customRender: 'diskSize', title: 'diskSizeTitle' }
+          width: '30%'
         },
         {
+          key: 'iops',
           dataIndex: 'iops',
-          width: '30%',
-          slots: { customRender: 'iops', title: 'iopsTitle' }
+          width: '30%'
         }
       ],
       selectedRowKeys: ['0'],
diff --git a/ui/src/views/compute/wizard/LoadBalancerSelection.vue b/ui/src/views/compute/wizard/LoadBalancerSelection.vue
index f29b229..e2ffb99 100644
--- a/ui/src/views/compute/wizard/LoadBalancerSelection.vue
+++ b/ui/src/views/compute/wizard/LoadBalancerSelection.vue
@@ -32,9 +32,9 @@
       :rowSelection="rowSelection"
       size="middle"
       :scroll="{ y: 225 }">
-      <template #publicip><environment-outlined /> {{ $t('label.publicip') }}</template>
-      <template #publicport>{{ $t('label.publicport') }}</template>
-      <template #privateport>{{ $t('label.privateport') }}</template>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'publicip'"><environment-outlined /> {{ $t('label.publicip') }}</template>
+      </template>
     </a-table>
 
     <div style="display: block; text-align: right;">
@@ -113,6 +113,7 @@
           width: '40%'
         },
         {
+          key: 'publicip',
           title: this.$t('label.publicip'),
           dataIndex: 'publicip'
         },
diff --git a/ui/src/views/compute/wizard/MultiDiskSelection.vue b/ui/src/views/compute/wizard/MultiDiskSelection.vue
index 55c30c3..5dd8466 100644
--- a/ui/src/views/compute/wizard/MultiDiskSelection.vue
+++ b/ui/src/views/compute/wizard/MultiDiskSelection.vue
@@ -26,44 +26,46 @@
       :rowSelection="rowSelection"
       :scroll="{ y: 225 }" >
 
-      <template #name="{ record }">
-        <span>{{ record.displaytext || record.name }}</span>
-        <div v-if="record.meta">
-          <div v-for="meta in record.meta" :key="meta.key">
-            <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          <span>{{ record.displaytext || record.name }}</span>
+          <div v-if="record.meta">
+            <div v-for="meta in record.meta" :key="meta.key">
+              <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+            </div>
           </div>
-        </div>
-      </template>
-      <template #offering="{ record }">
-        <span
-          style="width: 50%"
-          v-if="validOfferings[record.id] && validOfferings[record.id].length > 0">
-          <check-box-select-pair
-            v-if="selectedCustomDiskOffering!=null"
-            layout="vertical"
-            :resourceKey="record.id"
-            :selectOptions="validOfferings[record.id]"
-            :checkBoxLabel="autoSelectLabel"
-            :defaultCheckBoxValue="true"
-            :reversed="true"
-            @handle-checkselectpair-change="updateOfferingCheckPairSelect" />
-          <a-select
-            v-else
-            @change="updateOfferingSelect($event, record.id)"
-            :defaultValue="validOfferings[record.id][0].id"
-            showSearch
-            optionFilterProp="label"
-            :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-            }" >
-            <a-select-option v-for="offering in validOfferings[record.id]" :key="offering.id">
-              {{ offering.displaytext }}
-            </a-select-option>
-          </a-select>
-        </span>
-        <span v-else style="width: 50%">
-          {{ $t('label.no.matching.offering') }}
-        </span>
+        </template>
+        <template v-if="column.key === 'offering'">
+          <span
+            style="width: 50%"
+            v-if="validOfferings[record.id] && validOfferings[record.id].length > 0">
+            <check-box-select-pair
+              v-if="selectedCustomDiskOffering!=null"
+              layout="vertical"
+              :resourceKey="record.id"
+              :selectOptions="validOfferings[record.id]"
+              :checkBoxLabel="autoSelectLabel"
+              :defaultCheckBoxValue="true"
+              :reversed="true"
+              @handle-checkselectpair-change="updateOfferingCheckPairSelect" />
+            <a-select
+              v-else
+              @change="updateOfferingSelect($event, record.id)"
+              :defaultValue="validOfferings[record.id][0].id"
+              showSearch
+              optionFilterProp="label"
+              :filterOption="(input, option) => {
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              }" >
+              <a-select-option v-for="offering in validOfferings[record.id]" :key="offering.id" :label="offering.displaytext">
+                {{ offering.displaytext }}
+              </a-select-option>
+            </a-select>
+          </span>
+          <span v-else style="width: 50%">
+            {{ $t('label.no.matching.offering') }}
+          </span>
+        </template>
       </template>
     </a-table>
   </div>
@@ -108,14 +110,14 @@
     return {
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
-          title: this.$t('label.data.disk'),
-          slots: { customRender: 'name' }
+          title: this.$t('label.data.disk')
         },
         {
+          key: 'offering',
           dataIndex: 'offering',
-          title: this.$t('label.data.disk.offering'),
-          slots: { customRender: 'offering' }
+          title: this.$t('label.data.disk.offering')
         }
       ],
       loading: false,
diff --git a/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
index bdbd23e..f2397d5 100644
--- a/ui/src/views/compute/wizard/MultiNetworkSelection.vue
+++ b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
@@ -26,41 +26,46 @@
       :rowSelection="rowSelection"
       :scroll="{ y: 225 }" >
 
-      <template #name="{record}">
-        <span>{{ record.displaytext || record.name }}</span>
-        <div v-if="record.meta">
-          <div v-for="meta in record.meta" :key="meta.key">
-            <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          <span>{{ record.displaytext || record.name }}</span>
+          <div v-if="record.meta">
+            <div v-for="meta in record.meta" :key="meta.key">
+              <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+            </div>
           </div>
-        </div>
-      </template>
-      <template #network="{record}">
-        <a-select
-          v-if="validNetworks[record.id] && validNetworks[record.id].length > 0"
-          :defaultValue="validNetworks[record.id][0].id"
-          @change="val => handleNetworkChange(record, val)"
-          showSearch
-          optionFilterProp="label"
-          :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-          }" >
-          <a-select-option v-for="network in validNetworks[record.id]" :key="network.id">
-            {{ network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '') }}
-          </a-select-option>
-        </a-select>
-        <span v-else>
-          {{ $t('label.no.matching.network') }}
-        </span>
-      </template>
-      <template #ipaddress="{record}">
-        <check-box-input-pair
-          layout="vertical"
-          :resourceKey="record.id"
-          :checkBoxLabel="$t('label.auto.assign.random.ip')"
-          :defaultCheckBoxValue="true"
-          :reversed="true"
-          :visible="(indexNum > 0 && ipAddressesEnabled[record.id])"
-          @handle-checkinputpair-change="setIpAddress" />
+        </template>
+        <template v-if="column.key === 'network'">
+          <a-select
+            v-if="validNetworks[record.id] && validNetworks[record.id].length > 0"
+            :defaultValue="validNetworks[record.id][0].id"
+            @change="val => handleNetworkChange(record, val)"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option
+              v-for="network in validNetworks[record.id]"
+              :key="network.id"
+              :label="network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '')">
+              {{ network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '') }}
+            </a-select-option>
+          </a-select>
+          <span v-else>
+            {{ $t('label.no.matching.network') }}
+          </span>
+        </template>
+        <template v-if="column.key === 'ipaddress'">
+          <check-box-input-pair
+            layout="vertical"
+            :resourceKey="record.id"
+            :checkBoxLabel="$t('label.auto.assign.random.ip')"
+            :defaultCheckBoxValue="true"
+            :reversed="true"
+            :visible="(indexNum > 0 && ipAddressesEnabled[record.id])"
+            @handle-checkinputpair-change="setIpAddress" />
+        </template>
       </template>
     </a-table>
   </div>
@@ -102,19 +107,19 @@
     return {
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
-          title: this.$t('label.nic'),
-          slots: { customRender: 'name' }
+          title: this.$t('label.nic')
         },
         {
+          key: 'network',
           dataIndex: 'network',
-          title: this.$t('label.network'),
-          slots: { customRender: 'network' }
+          title: this.$t('label.network')
         },
         {
+          key: 'ipaddress',
           dataIndex: 'ipaddress',
-          title: this.$t('label.ipaddress'),
-          slots: { customRender: 'ipaddress' }
+          title: this.$t('label.ipaddress')
         }
       ],
       loading: false,
diff --git a/ui/src/views/compute/wizard/NetworkConfiguration.vue b/ui/src/views/compute/wizard/NetworkConfiguration.vue
index a31f7f4..4165276 100644
--- a/ui/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/ui/src/views/compute/wizard/NetworkConfiguration.vue
@@ -29,42 +29,46 @@
       :rowKey="record => record.id"
       size="middle"
       :scroll="{ y: 225 }">
-      <template #name="{ text, record }">
-        <div>{{ text }}</div>
-        <small v-if="record.type!=='L2'">{{ $t('label.cidr') + ': ' + record.cidr }}</small>
-      </template>
-      <template #ipAddress="{ record }" v-if="!this.autoscale">
-        <a-form-item
-          style="display: block"
-          v-if="record.type !== 'L2'"
-          :name="'ipAddress' + record.id">
-          <a-input
-            style="width: 150px;"
-            v-model:value="form['ipAddress' + record.id]"
-            :placeholder="record.cidr"
-            @change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)">
-            <template #suffix>
-              <a-tooltip :title="getIpRangeDescription(record)">
-                <info-circle-outlined style="color: rgba(0,0,0,.45)" />
-              </a-tooltip>
-            </template>
-          </a-input>
-        </a-form-item>
-      </template>
-      <template #macAddress="{ record }" v-if="!this.autoscale">
-        <a-form-item style="display: block" :name="'macAddress' + record.id">
-          <a-input
-            style="width: 150px;"
-            :placeholder="$t('label.macaddress')"
-            v-model:value="form[`macAddress` + record.id]"
-            @change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)">
-            <template #suffix>
-              <a-tooltip :title="$t('label.macaddress.example')">
-                <info-circle-outlined style="color: rgba(0,0,0,.45)" />
-              </a-tooltip>
-            </template>
-          </a-input>
-        </a-form-item>
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'name'">
+          <div>{{ text }}</div>
+          <small v-if="record.type!=='L2'">{{ $t('label.cidr') + ': ' + record.cidr }}</small>
+        </template>
+        <template  v-if="!this.autoscale">
+          <template v-if="column.key === 'ipAddress'">
+            <a-form-item
+              style="display: block"
+              v-if="record.type !== 'L2'"
+              :name="'ipAddress' + record.id">
+              <a-input
+                style="width: 150px;"
+                v-model:value="form['ipAddress' + record.id]"
+                :placeholder="record.cidr"
+                @change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)">
+                <template #suffix>
+                  <a-tooltip :title="getIpRangeDescription(record)">
+                    <info-circle-outlined style="color: rgba(0,0,0,.45)" />
+                  </a-tooltip>
+                </template>
+              </a-input>
+            </a-form-item>
+          </template>
+          <template v-if="column.key === 'macAddress'">
+            <a-form-item style="display: block" :name="'macAddress' + record.id">
+              <a-input
+                style="width: 150px;"
+                :placeholder="$t('label.macaddress')"
+                v-model:value="form[`macAddress` + record.id]"
+                @change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)">
+                <template #suffix>
+                  <a-tooltip :title="$t('label.macaddress.example')">
+                    <info-circle-outlined style="color: rgba(0,0,0,.45)" />
+                  </a-tooltip>
+                </template>
+              </a-input>
+            </a-form-item>
+          </template>
+        </template>
       </template>
     </a-table>
   </a-form>
@@ -97,22 +101,22 @@
       networks: [],
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
           title: this.$t('label.defaultnetwork'),
-          width: '30%',
-          slots: { customRender: 'name' }
+          width: '30%'
         },
         {
+          key: 'ipAddress',
           dataIndex: 'ip',
           title: this.$t('label.ip'),
-          width: '30%',
-          slots: { customRender: 'ipAddress' }
+          width: '30%'
         },
         {
+          key: 'macAddress',
           dataIndex: 'mac',
           title: this.$t('label.macaddress'),
-          width: '30%',
-          slots: { customRender: 'macAddress' }
+          width: '30%'
         }
       ],
       selectedRowKeys: [],
diff --git a/ui/src/views/compute/wizard/NetworkSelection.vue b/ui/src/views/compute/wizard/NetworkSelection.vue
index 96a6d28..66a932c 100644
--- a/ui/src/views/compute/wizard/NetworkSelection.vue
+++ b/ui/src/views/compute/wizard/NetworkSelection.vue
@@ -34,14 +34,16 @@
       :rowSelection="rowSelection"
       :scroll="{ y: 225 }"
     >
-      <template #name="{record}">
-        <resource-icon
-          v-if="record.icon"
-          :image="record.icon.base64image"
-          size="1x"
-          style="margin-right: 5px"/>
-        <apartment-outlined v-else style="margin-right: 5px" />
-        {{ record.name }}
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          <resource-icon
+            v-if="record.icon"
+            :image="record.icon.base64image"
+            size="1x"
+            style="margin-right: 5px"/>
+          <apartment-outlined v-else style="margin-right: 5px" />
+          {{ record.name }}
+        </template>
       </template>
       <template #expandedRowRender="{ record }">
         <a-list
@@ -173,29 +175,35 @@
           }
         })
       }
+      const vpcCol = {
+        key: 'vpcName',
+        dataIndex: 'vpcName',
+        title: this.$t('label.vpc'),
+        width: '30%'
+      }
+      if (vpcFilter.length > 0) {
+        vpcCol.filters = vpcFilter
+        vpcCol.filteredValue = _.get(this.filteredInfo, 'id')
+        vpcCol.onFilter = (value, record) => {
+          return record.vpcid === value
+        }
+      }
       return [
         {
+          key: 'name',
           dataIndex: 'name',
           title: this.$t('label.networks'),
-          slots: { customRender: 'name' },
           width: '40%'
         },
         {
+          key: 'type',
           dataIndex: 'type',
           title: this.$t('label.guestiptype'),
           width: '15%'
         },
+        vpcCol,
         {
-          dataIndex: 'vpcName',
-          title: this.$t('label.vpc'),
-          width: '20%',
-          filters: vpcFilter,
-          filteredValue: _.get(this.filteredInfo, 'id'),
-          onFilter: (value, record) => {
-            return record.vpcid === value
-          }
-        },
-        {
+          key: 'supportsvmautoscaling',
           dataIndex: 'supportsvmautoscaling',
           title: this.$t('label.supportsvmautoscaling'),
           width: '25%'
diff --git a/ui/src/views/compute/wizard/SshKeyPairSelection.vue b/ui/src/views/compute/wizard/SshKeyPairSelection.vue
index 848c1a2..f6dda72 100644
--- a/ui/src/views/compute/wizard/SshKeyPairSelection.vue
+++ b/ui/src/views/compute/wizard/SshKeyPairSelection.vue
@@ -31,8 +31,10 @@
       :pagination="false"
       size="middle"
       :scroll="{ y: 225 }">
-      <template #account><user-outlined /> {{ $t('label.account') }}</template>
-      <template #domain><block-outlined /> {{ $t('label.domain') }}</template>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'account'"><user-outlined /> {{ $t('label.account') }}</template>
+        <template v-if="column.key === 'domain'"><block-outlined /> {{ $t('label.domain') }}</template>
+      </template>
     </a-table>
     <div style="display: block; text-align: right;">
       <a-pagination
@@ -87,18 +89,19 @@
       filter: '',
       columns: [
         {
+          key: 'name',
           dataIndex: 'name',
           title: this.$t('label.sshkeypairs'),
           width: '40%'
         },
         {
+          key: 'account',
           dataIndex: 'account',
-          slots: { title: 'account' },
           width: '30%'
         },
         {
+          key: 'domain',
           dataIndex: 'domain',
-          slots: { title: 'domain' },
           width: '30%'
         }
       ],
diff --git a/ui/src/views/compute/wizard/UserDataSelection.vue b/ui/src/views/compute/wizard/UserDataSelection.vue
index 4dfc14e..87e0bb3 100644
--- a/ui/src/views/compute/wizard/UserDataSelection.vue
+++ b/ui/src/views/compute/wizard/UserDataSelection.vue
@@ -32,8 +32,10 @@
       size="middle"
       :scroll="{ y: 225 }"
     >
-      <template #account><user-outlined /> {{ $t('label.account') }}</template>
-      <template #domain><block-outlined /> {{ $t('label.domain') }}</template>
+      <template #headerCell="{ column }">
+        <template v-if="column.key === 'account'"><user-outlined /> {{ $t('label.account') }}</template>
+        <template v-if="column.key === 'domain'"><block-outlined /> {{ $t('label.domain') }}</template>
+      </template>
     </a-table>
   </div>
 </template>
@@ -81,13 +83,13 @@
           width: '40%'
         },
         {
+          key: 'account',
           dataIndex: 'account',
-          slots: { title: 'account' },
           width: '30%'
         },
         {
+          key: 'domain',
           dataIndex: 'domain',
-          slots: { title: 'domain' },
           width: '30%'
         }
       ],
diff --git a/ui/src/views/dashboard/CapacityDashboard.vue b/ui/src/views/dashboard/CapacityDashboard.vue
index 1dc67a7..c634500 100644
--- a/ui/src/views/dashboard/CapacityDashboard.vue
+++ b/ui/src/views/dashboard/CapacityDashboard.vue
@@ -280,7 +280,7 @@
       this.listCapacity(this.zoneSelected)
     },
     filterZone (input, option) {
-      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
     }
   }
 }
diff --git a/ui/src/views/dashboard/SetupTwoFaAtLogin.vue b/ui/src/views/dashboard/SetupTwoFaAtLogin.vue
index a710afb..2300a7a 100644
--- a/ui/src/views/dashboard/SetupTwoFaAtLogin.vue
+++ b/ui/src/views/dashboard/SetupTwoFaAtLogin.vue
@@ -38,10 +38,6 @@
           <a-form-item v-ctrl-enter="submitPin" ref="selectedProvider" name="selectedProvider">
              <a-select
               v-model:value="form.selectedProvider"
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }"
               @change="val => { handleSelectChange(val) }">
               <a-select-option
                 v-for="(opt) in providers"
diff --git a/ui/src/views/dashboard/UsageDashboard.vue b/ui/src/views/dashboard/UsageDashboard.vue
index 4264a90..28a892a 100644
--- a/ui/src/views/dashboard/UsageDashboard.vue
+++ b/ui/src/views/dashboard/UsageDashboard.vue
@@ -169,7 +169,7 @@
   methods: {
     fetchData () {
       this.stats = [{}, {}, {}, {}, {}, {}]
-      api('listVirtualMachines', { state: 'Running', listall: true }).then(json => {
+      api('listVirtualMachines', { state: 'Running', listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listvirtualmachinesresponse) {
           count = json.listvirtualmachinesresponse.count
@@ -177,7 +177,7 @@
         var tileColor = this.$config.theme['@dashboard-tile-runningvms-bg'] || '#dfe9cc'
         this.stats.splice(0, 1, { name: this.$t('label.running.vms'), count: count, icon: 'desktop-outlined', bgcolor: tileColor, path: '/vm', query: { state: 'running', filter: 'running' } })
       })
-      api('listVirtualMachines', { state: 'Stopped', listall: true }).then(json => {
+      api('listVirtualMachines', { state: 'Stopped', listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listvirtualmachinesresponse) {
           count = json.listvirtualmachinesresponse.count
@@ -185,7 +185,7 @@
         var tileColor = this.$config.theme['@dashboard-tile-stoppedvms-bg'] || '#edcbce'
         this.stats.splice(1, 1, { name: this.$t('label.stopped.vms'), count: count, icon: 'poweroff-outlined', bgcolor: tileColor, path: '/vm', query: { state: 'stopped', filter: 'stopped' } })
       })
-      api('listVirtualMachines', { listall: true }).then(json => {
+      api('listVirtualMachines', { listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listvirtualmachinesresponse) {
           count = json.listvirtualmachinesresponse.count
@@ -193,7 +193,7 @@
         var tileColor = this.$config.theme['@dashboard-tile-totalvms-bg'] || '#ffffff'
         this.stats.splice(2, 1, { name: this.$t('label.total.vms'), count: count, icon: 'number-outlined', bgcolor: tileColor, path: '/vm' })
       })
-      api('listVolumes', { listall: true }).then(json => {
+      api('listVolumes', { listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listvolumesresponse) {
           count = json.listvolumesresponse.count
@@ -201,7 +201,7 @@
         var tileColor = this.$config.theme['@dashboard-tile-totalvolumes-bg'] || '#ffffff'
         this.stats.splice(3, 1, { name: this.$t('label.total.volume'), count: count, icon: 'database-outlined', bgcolor: tileColor, path: '/volume' })
       })
-      api('listNetworks', { listall: true }).then(json => {
+      api('listNetworks', { listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listnetworksresponse) {
           count = json.listnetworksresponse.count
@@ -209,7 +209,7 @@
         var tileColor = this.$config.theme['@dashboard-tile-totalnetworks-bg'] || '#ffffff'
         this.stats.splice(4, 1, { name: this.$t('label.total.network'), count: count, icon: 'apartment-outlined', bgcolor: tileColor, path: '/guestnetwork' })
       })
-      api('listPublicIpAddresses', { listall: true }).then(json => {
+      api('listPublicIpAddresses', { listall: true, retrieveonlyresourcecount: true }).then(json => {
         var count = 0
         if (json && json.listpublicipaddressesresponse) {
           count = json.listpublicipaddressesresponse.count
diff --git a/ui/src/views/iam/AddAccount.vue b/ui/src/views/iam/AddAccount.vue
index 88bf848..467a4a2 100644
--- a/ui/src/views/iam/AddAccount.vue
+++ b/ui/src/views/iam/AddAccount.vue
@@ -37,9 +37,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
-            <a-select-option v-for="role in roles" :key="role.id">
+            <a-select-option v-for="role in roles" :key="role.id" :label="role.name + ' (' + role.type + ')'">
               {{ role.name + ' (' + role.type + ')' }}
             </a-select-option>
           </a-select>
@@ -143,9 +143,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
-            <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+            <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -173,9 +173,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }">
-              <a-select-option v-for="idp in idps" :key="idp.id">
+              <a-select-option v-for="idp in idps" :key="idp.id" :label="idp.orgName">
                 {{ idp.orgName }}
               </a-select-option>
             </a-select>
diff --git a/ui/src/views/iam/AddLdapAccount.vue b/ui/src/views/iam/AddLdapAccount.vue
index ecf8cb0..fadb538 100644
--- a/ui/src/views/iam/AddLdapAccount.vue
+++ b/ui/src/views/iam/AddLdapAccount.vue
@@ -59,9 +59,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }">
-                <a-select-option v-for="opt in filters" :key="opt.id" >
+                <a-select-option v-for="opt in filters" :key="opt.id" :label="opt.name">
                   {{ opt.name }}
                 </a-select-option>
               </a-select>
@@ -73,9 +73,9 @@
                 :loading="domainLoading"
                 @change="fetchListLdapUsers($event)"
                 showSearch
-                optionFilterProp="label"
+                optionFilterProp="value"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
                 <a-select-option v-for="opt in listDomains" :key="opt.name">
                   {{ opt.name }}
@@ -94,9 +94,9 @@
                 :placeholder="apiParams.roleid.description"
                 :loading="roleLoading"
                 showSearch
-                optionFilterProp="label"
+                optionFilterProp="value"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }">
                 <a-select-option v-for="opt in listRoles" :key="opt.name">
                   {{ opt.name }}
@@ -111,9 +111,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }">
-                <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+                <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -142,9 +142,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }">
-                  <a-select-option v-for="(idp, idx) in listIdps" :key="idx">
+                  <a-select-option v-for="(idp, idx) in listIdps" :key="idx" :label="idp.orgName">
                     {{ idp.orgName }}
                   </a-select-option>
                 </a-select>
@@ -212,26 +212,26 @@
     this.listIdps = []
     this.columns = [
       {
+        key: 'name',
         title: this.$t('label.name'),
         dataIndex: 'name',
-        width: 120,
-        slots: { customRender: 'name' }
+        width: 120
       },
       {
+        key: 'username',
         title: this.$t('label.username'),
         dataIndex: 'username',
-        width: 120,
-        slots: { customRender: 'username' }
+        width: 120
       },
       {
+        key: 'email',
         title: this.$t('label.email'),
-        dataIndex: 'email',
-        slots: { customRender: 'email' }
+        dataIndex: 'email'
       },
       {
+        key: 'conflictingusersource',
         title: this.$t('label.user.conflict'),
-        dataIndex: 'conflictingusersource',
-        slots: { customRender: 'conflictingusersource' }
+        dataIndex: 'conflictingusersource'
       }
     ]
     this.filters = [
@@ -487,9 +487,9 @@
     },
     handleEntityRule () {
       if (this.form.samlEnable) {
-        this.rules.push({
-          samlEntity: [{ required: true, message: `${this.$t('message.error.select')}` }]
-        })
+        this.rules.samlEntity = [{ required: true, message: `${this.$t('message.error.select')}` }]
+      } else {
+        delete this.rules.samlEntity
       }
     }
   }
diff --git a/ui/src/views/iam/AddUser.vue b/ui/src/views/iam/AddUser.vue
index c4b1a12..49bca32 100644
--- a/ui/src/views/iam/AddUser.vue
+++ b/ui/src/views/iam/AddUser.vue
@@ -140,9 +140,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+            <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -161,9 +161,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="idp in idps" :key="idp.id">
+              <a-select-option v-for="idp in idps" :key="idp.id" :label="idp.orgName">
                 {{ idp.orgName }}
               </a-select-option>
             </a-select>
diff --git a/ui/src/views/iam/ConfigureSamlSsoAuth.vue b/ui/src/views/iam/ConfigureSamlSsoAuth.vue
index 4f2e540..15e2b78 100644
--- a/ui/src/views/iam/ConfigureSamlSsoAuth.vue
+++ b/ui/src/views/iam/ConfigureSamlSsoAuth.vue
@@ -36,9 +36,9 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option v-for="idp in idps" :key="idp.id">
+          <a-select-option v-for="idp in idps" :key="idp.id" :label="idp.orgName">
             {{ idp.orgName }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/iam/CreateRole.vue b/ui/src/views/iam/CreateRole.vue
index ea3d380..d0ec8be 100644
--- a/ui/src/views/iam/CreateRole.vue
+++ b/ui/src/views/iam/CreateRole.vue
@@ -67,9 +67,9 @@
             v-model:value="form.type"
             :placeholder="apiParams.type.description"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option v-for="role in defaultRoles" :key="role">
               {{ role }}
@@ -87,12 +87,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="role in roles"
               :value="role.id"
-              :key="role.id">
+              :key="role.id"
+              :label="role.name">
               {{ role.name }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/iam/DomainActionForm.vue b/ui/src/views/iam/DomainActionForm.vue
index fe55c51..4d7c727 100644
--- a/ui/src/views/iam/DomainActionForm.vue
+++ b/ui/src/views/iam/DomainActionForm.vue
@@ -65,9 +65,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="(opt, optIndex) in action.mapping[field.name].options" :key="optIndex">
+                <a-select-option v-for="(opt, optIndex) in action.mapping[field.name].options" :key="optIndex" :label="opt">
                   {{ opt }}
                 </a-select-option>
               </a-select>
@@ -79,11 +79,14 @@
                 :loading="field.loading"
                 :placeholder="field.description"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-focus="fieldIndex === firstIndex"
               >
-                <a-select-option v-for="(opt, optIndex) in field.opts" :key="optIndex">
+                <a-select-option
+                  v-for="(opt, optIndex) in field.opts"
+                  :key="optIndex"
+                  :label="opt.name || opt.description || opt.traffictype || opt.publicip">
                   {{ opt.name || opt.description || opt.traffictype || opt.publicip }}
                 </a-select-option>
               </a-select>
@@ -97,9 +100,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="(opt, optIndex) in field.opts" :key="optIndex">
+                <a-select-option
+                  v-for="(opt, optIndex) in field.opts"
+                  :key="optIndex"
+                  :label="opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description">
                   {{ opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description }}
                 </a-select-option>
               </a-select>
diff --git a/ui/src/views/iam/DomainView.vue b/ui/src/views/iam/DomainView.vue
index 2b15d8b..997f900 100644
--- a/ui/src/views/iam/DomainView.vue
+++ b/ui/src/views/iam/DomainView.vue
@@ -56,13 +56,13 @@
         :loading="loading"
         :tabs="$route.meta.tabs" />
       <tree-view
-        v-else
         :key="treeViewKey"
         :treeData="treeData"
         :treeSelected="treeSelected"
         :treeStore="domainStore"
         :loading="loading"
         :tabs="$route.meta.tabs"
+        :treeDeletedKey="treeDeletedKey"
         @change-resource="changeResource"
         @change-tree-store="changeDomainStore"/>
     </div>
@@ -109,7 +109,8 @@
       showAction: false,
       action: {},
       dataView: false,
-      domainStore: {}
+      domainStore: {},
+      treeDeletedKey: null
     }
   },
   computed: {
@@ -194,6 +195,7 @@
       })
     },
     execAction (action) {
+      this.treeDeletedKey = action.api === 'deleteDomain' ? this.resource.key : null
       this.actionData = []
       this.action = action
       this.action.params = store.getters.apis[this.action.api].params
@@ -286,9 +288,8 @@
 
       rootItem[0].title = rootItem[0].title ? rootItem[0].title : rootItem[0].name
       rootItem[0].key = rootItem[0].id ? rootItem[0].id : 0
-      rootItem[0].slots = {
-        icon: 'leaf'
-      }
+      rootItem[0].resourceIcon = rootItem[0].icon || {}
+      delete rootItem[0].icon
 
       if (!rootItem[0].haschild) {
         rootItem[0].isLeaf = true
diff --git a/ui/src/views/iam/EditUser.vue b/ui/src/views/iam/EditUser.vue
index 9940b20..e082fd1 100644
--- a/ui/src/views/iam/EditUser.vue
+++ b/ui/src/views/iam/EditUser.vue
@@ -74,9 +74,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+            <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/iam/ImportRole.vue b/ui/src/views/iam/ImportRole.vue
index a4811fa..9919127 100644
--- a/ui/src/views/iam/ImportRole.vue
+++ b/ui/src/views/iam/ImportRole.vue
@@ -31,7 +31,7 @@
           <a-upload-dragger
             :multiple="false"
             :fileList="fileList"
-            :remove="handleRemove"
+            @remove="handleRemove"
             :beforeUpload="beforeUpload"
             @change="handleChange"
             v-model:value="form.file">
@@ -70,9 +70,9 @@
             v-model:value="form.type"
             :placeholder="apiParams.type.description"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option v-for="role in defaultRoles" :key="role">
               {{ role }}
diff --git a/ui/src/views/iam/PermissionEditable.vue b/ui/src/views/iam/PermissionEditable.vue
index 10add51..22007d8 100644
--- a/ui/src/views/iam/PermissionEditable.vue
+++ b/ui/src/views/iam/PermissionEditable.vue
@@ -23,10 +23,10 @@
     showSearch
     optionFilterProp="label"
     :filterOption="(input, option) => {
-      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
     }" >
-    <a-select-option value="allow">{{ $t('label.allow') }}</a-select-option>
-    <a-select-option value="deny">{{ $t('label.deny') }}</a-select-option>
+    <a-select-option value="allow" :label="$t('label.allow')">{{ $t('label.allow') }}</a-select-option>
+    <a-select-option value="deny" :label="$t('label.deny')">{{ $t('label.deny') }}</a-select-option>
   </a-select>
 </template>
 
diff --git a/ui/src/views/iam/RolePermissionTab.vue b/ui/src/views/iam/RolePermissionTab.vue
index 7a0a622..49a9e98 100644
--- a/ui/src/views/iam/RolePermissionTab.vue
+++ b/ui/src/views/iam/RolePermissionTab.vue
@@ -69,7 +69,6 @@
         handle=".drag-handle"
         animation="200"
         ghostClass="drag-ghost"
-        tag="transition-group"
         :component-data="{type: 'transition'}"
         item-key="id">
         <template #item="{element}">
diff --git a/ui/src/views/iam/SSLCertificateTab.vue b/ui/src/views/iam/SSLCertificateTab.vue
index 6a3e161..4015e20 100644
--- a/ui/src/views/iam/SSLCertificateTab.vue
+++ b/ui/src/views/iam/SSLCertificateTab.vue
@@ -28,23 +28,25 @@
           :pagination="false"
           v-if="!quickview"
         >
-          <template #action="{ record }" class="cert-button-action">
-            <tooltip-button
-              tooltipPlacement="top"
-              :tooltip="$t('label.quickview')"
-              type="primary"
-              icon="eye-outlined"
-              size="small"
-              @onClick="onQuickView(record.id)" />
-            <tooltip-button
-              tooltipPlacement="top"
-              :tooltip="$t('label.delete.sslcertificate')"
-              :disabled="!('deleteSslCert' in $store.getters.apis)"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined"
-              size="small"
-              @onClick="onShowConfirm(record)" />
+          <template #bodyCell="{ column, record }">
+            <template v-if="column.key === 'actions'" class="cert-button-action">
+              <tooltip-button
+                tooltipPlacement="top"
+                :tooltip="$t('label.quickview')"
+                type="primary"
+                icon="eye-outlined"
+                size="small"
+                @onClick="onQuickView(record.id)" />
+              <tooltip-button
+                tooltipPlacement="top"
+                :tooltip="$t('label.delete.sslcertificate')"
+                :disabled="!('deleteSslCert' in $store.getters.apis)"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined"
+                size="small"
+                @onClick="onShowConfirm(record)" />
+            </template>
           </template>
         </a-table>
 
@@ -128,22 +130,22 @@
   created () {
     this.columns = [
       {
+        key: 'name',
         title: this.$t('label.name'),
-        dataIndex: 'name',
-        slots: { customRender: 'name' }
+        dataIndex: 'name'
       },
       {
+        key: 'id',
         title: this.$t('label.certificateid'),
         dataIndex: 'id',
-        width: 450,
-        slots: { customRender: 'id' }
+        width: 450
       },
       {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
         fixed: 'right',
-        width: 80,
-        slots: { customRender: 'action' }
+        width: 80
       }
     ]
     this.detailColumn = ['name', 'certificate', 'certchain']
diff --git a/ui/src/views/iam/SetupTwoFaAtUserProfile.vue b/ui/src/views/iam/SetupTwoFaAtUserProfile.vue
index 6f4f729..df6466a 100644
--- a/ui/src/views/iam/SetupTwoFaAtUserProfile.vue
+++ b/ui/src/views/iam/SetupTwoFaAtUserProfile.vue
@@ -29,10 +29,6 @@
           <a-form-item v-ctrl-enter="submitPin" ref="selectedProvider" name="selectedProvider">
              <a-select
               v-model:value="form.selectedProvider"
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }"
               @change="val => { handleSelectChange(val) }">
               <a-select-option
                 v-for="(opt) in providers"
diff --git a/ui/src/views/image/IsoZones.vue b/ui/src/views/image/IsoZones.vue
index 7718b1c..326f102 100644
--- a/ui/src/views/image/IsoZones.vue
+++ b/ui/src/views/image/IsoZones.vue
@@ -35,43 +35,45 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.zoneid">
-      <template #zonename="{record}">
-        <span v-if="fetchZoneIcon(record.zoneid)">
-          <resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
-        </span>
-        <global-outlined v-else style="margin-right: 5px" />
-        <span> {{ record.zonename }} </span>
-      </template>
-      <template #isready="{ record }">
-        <span v-if="record.isready">{{ $t('label.yes') }}</span>
-        <span v-else>{{ $t('label.no') }}</span>
-      </template>
-      <template #action="{ record }">
-        <span style="margin-right: 5px">
-          <tooltip-button
-            :tooltip="$t('label.action.copy.iso')"
-            :disabled="!('copyIso' in $store.getters.apis && record.isready)"
-            icon="copy-outlined"
-            :loading="copyLoading"
-            @click="showCopyIso(record)" />
-        </span>
-        <span style="margin-right: 5px">
-          <a-popconfirm
-            v-if="'deleteIso' in $store.getters.apis"
-            placement="topRight"
-            :title="$t('message.action.delete.iso')"
-            :ok-text="$t('label.yes')"
-            :cancel-text="$t('label.no')"
-            :loading="deleteLoading"
-            @confirm="deleteIso(record)"
-          >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'zonename'">
+          <span v-if="fetchZoneIcon(record.zoneid)">
+            <resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
+          </span>
+          <global-outlined v-else style="margin-right: 5px" />
+          <span> {{ record.zonename }} </span>
+        </template>
+        <template v-if="column.key === 'isready'">
+          <span v-if="record.isready">{{ $t('label.yes') }}</span>
+          <span v-else>{{ $t('label.no') }}</span>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <span style="margin-right: 5px">
             <tooltip-button
-              :tooltip="$t('label.action.delete.iso')"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined" />
-          </a-popconfirm>
-        </span>
+              :tooltip="$t('label.action.copy.iso')"
+              :disabled="!('copyIso' in $store.getters.apis && record.isready)"
+              icon="copy-outlined"
+              :loading="copyLoading"
+              @click="showCopyIso(record)" />
+          </span>
+          <span style="margin-right: 5px">
+            <a-popconfirm
+              v-if="'deleteIso' in $store.getters.apis"
+              placement="topRight"
+              :title="$t('message.action.delete.iso')"
+              :ok-text="$t('label.yes')"
+              :cancel-text="$t('label.no')"
+              :loading="deleteLoading"
+              @confirm="deleteIso(record)"
+            >
+              <tooltip-button
+                :tooltip="$t('label.action.delete.iso')"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined" />
+            </a-popconfirm>
+          </span>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -221,27 +223,27 @@
     this.initForm()
     this.columns = [
       {
+        key: 'zonename',
         title: this.$t('label.zonename'),
-        dataIndex: 'zonename',
-        slots: { customRender: 'zonename' }
+        dataIndex: 'zonename'
       },
       {
         title: this.$t('label.status'),
         dataIndex: 'status'
       },
       {
+        key: 'isready',
         title: this.$t('label.isready'),
-        dataIndex: 'isready',
-        slots: { customRender: 'isready' }
+        dataIndex: 'isready'
       }
     ]
     if (this.isActionPermitted()) {
       this.columns.push({
+        key: 'actions',
         title: '',
-        dataIndex: 'action',
+        dataIndex: 'actions',
         fixed: 'right',
-        width: 100,
-        slots: { customRender: 'action' }
+        width: 100
       })
     }
 
@@ -355,9 +357,9 @@
     deleteIsos (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/image/RegisterOrUploadIso.vue b/ui/src/views/image/RegisterOrUploadIso.vue
index cf1435a..c56dcd8 100644
--- a/ui/src/views/image/RegisterOrUploadIso.vue
+++ b/ui/src/views/image/RegisterOrUploadIso.vue
@@ -50,7 +50,7 @@
           <a-upload-dragger
             :multiple="false"
             :fileList="fileList"
-            :remove="handleRemove"
+            @remove="handleRemove"
             :beforeUpload="beforeUpload"
             v-model:value="form.file">
             <p class="ant-upload-drag-icon">
@@ -111,11 +111,11 @@
             }"
             :loading="osTypeLoading"
             :placeholder="apiParams.ostypeid.description">
-            <a-select-option :value="opt.id" v-for="(opt, optIndex) in osTypes" :key="optIndex" :label="opt.name || opt.description">
+            <a-select-option :value="opt.id" v-for="(opt, optIndex) in osTypes" :key="optIndex" :label="opt.name">
               <span>
                 <resource-icon v-if="opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
                 <global-outlined v-else style="margin-right: 5px" />
-                {{ opt.name || opt.description }}
+                {{ opt.name }}
               </span>
             </a-select-option>
           </a-select>
@@ -131,12 +131,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-model:value="userdataid"
                 :placeholder="linkUserDataParams.userdataid.description"
                 :loading="userdata.loading">
-                <a-select-option v-for="opt in userdata.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdata.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -148,13 +148,14 @@
                 <tooltip-label :title="$t('label.userdatapolicy')" :tooltip="$t('label.userdatapolicy.tooltip')"/>
               </template>
               <a-select
+                showSearch
                 v-model:value="userdatapolicy"
                 :placeholder="linkUserDataParams.userdatapolicy.description"
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id" :label="opt.id || opt.description">
                   {{ opt.id || opt.description }}
                 </a-select-option>
               </a-select>
@@ -260,7 +261,6 @@
         url: [{ required: true, message: this.$t('label.upload.iso.from.local') }],
         file: [{ required: true, message: this.$t('message.error.required.input') }],
         name: [{ required: true, message: this.$t('message.error.required.input') }],
-        displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
         zoneid: [{ required: true, message: this.$t('message.error.select') }],
         ostypeid: [{ required: true, message: this.$t('message.error.select') }]
       })
diff --git a/ui/src/views/image/RegisterOrUploadTemplate.vue b/ui/src/views/image/RegisterOrUploadTemplate.vue
index e4d4687..4ed33af 100644
--- a/ui/src/views/image/RegisterOrUploadTemplate.vue
+++ b/ui/src/views/image/RegisterOrUploadTemplate.vue
@@ -44,7 +44,7 @@
             <a-upload-dragger
               :multiple="false"
               :fileList="fileList"
-              :remove="handleRemove"
+              @remove="handleRemove"
               :beforeUpload="beforeUpload"
               v-model:value="form.file">
               <p class="ant-upload-drag-icon">
@@ -128,9 +128,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="(opt, optIndex) in hyperVisor.opts" :key="optIndex">
+                <a-select-option v-for="(opt, optIndex) in hyperVisor.opts" :key="optIndex" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -145,9 +145,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in format.opts" :key="opt.id">
+                <a-select-option v-for="opt in format.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -191,9 +191,9 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in rootDisk.opts" :key="opt.id">
+                <a-select-option v-for="opt in rootDisk.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -206,10 +206,10 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 :placeholder="$t('label.nicadaptertype')">
-                <a-select-option v-for="opt in nicAdapterType.opts" :key="opt.id">
+                <a-select-option v-for="opt in nicAdapterType.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -226,10 +226,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="$t('label.keyboard')">
-            <a-select-option v-for="opt in keyboardType.opts" :key="opt.id">
+            <a-select-option v-for="opt in keyboardType.opts" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -243,12 +243,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-model:value="form.ostypeid"
             :loading="osTypes.loading"
             :placeholder="apiParams.ostypeid.description">
-            <a-select-option v-for="opt in osTypes.opts" :key="opt.id">
+            <a-select-option v-for="opt in osTypes.opts" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -263,12 +263,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-model:value="userdataid"
                 :placeholder="linkUserDataParams.userdataid.description"
                 :loading="userdata.loading">
-                <a-select-option v-for="opt in userdata.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdata.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -280,13 +280,14 @@
                 <tooltip-label :title="$t('label.userdatapolicy')" :tooltip="$t('label.userdatapolicy.tooltip')"/>
               </template>
               <a-select
+                showSearch
                 v-model:value="userdatapolicy"
                 :placeholder="linkUserDataParams.userdatapolicy.description"
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id" :label="opt.id || opt.description">
                   {{ opt.id || opt.description }}
                 </a-select-option>
               </a-select>
@@ -441,7 +442,6 @@
         url: [{ required: true, message: this.$t('message.error.required.input') }],
         file: [{ required: true, message: this.$t('message.error.required.input') }],
         name: [{ required: true, message: this.$t('message.error.required.input') }],
-        displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
         zoneids: [
           { type: 'array', required: true, message: this.$t('message.error.select') },
           {
@@ -454,7 +454,6 @@
         ostypeid: [{ required: true, message: this.$t('message.error.select') }],
         groupenabled: [{ type: 'array' }]
       })
-      console.log(this.form)
     },
     fetchData () {
       this.fetchZone()
@@ -576,7 +575,7 @@
         const listOsTypes = json.listostypesresponse.ostype
         this.osTypes.opts = listOsTypes
         this.defaultOsType = this.osTypes.opts[1].description
-        this.defaultOsId = this.osTypes.opts[1].id
+        this.defaultOsId = this.osTypes.opts[1].name
       }).finally(() => {
         this.osTypes.loading = false
       })
diff --git a/ui/src/views/image/TemplateZones.vue b/ui/src/views/image/TemplateZones.vue
index d4811c0..8d7a3e2 100644
--- a/ui/src/views/image/TemplateZones.vue
+++ b/ui/src/views/image/TemplateZones.vue
@@ -35,20 +35,39 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.zoneid">
-      <template #zonename="{record}">
-        <span v-if="fetchZoneIcon(record.zoneid)">
-          <resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
-        </span>
-        <global-outlined v-else style="margin-right: 5px" />
-        <span> {{ record.zonename }} </span>
-      </template>
-      <template #isready="{ record }">
-        <span v-if="record.isready">{{ $t('label.yes') }}</span>
-        <span v-else>{{ $t('label.no') }}</span>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'zonename'">
+          <span v-if="fetchZoneIcon(record.zoneid)">
+            <resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
+          </span>
+          <global-outlined v-else style="margin-right: 5px" />
+          <span> {{ record.zonename }} </span>
+        </template>
+        <template v-if="column.key === 'isready'">
+          <span v-if="record.isready">{{ $t('label.yes') }}</span>
+          <span v-else>{{ $t('label.no') }}</span>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            style="margin-right: 5px"
+            :disabled="!('copyTemplate' in $store.getters.apis && record.isready)"
+            :title="$t('label.action.copy.template')"
+            icon="copy-outlined"
+            :loading="copyLoading"
+            @onClick="showCopyTemplate(record)" />
+          <tooltip-button
+            style="margin-right: 5px"
+            :disabled="!('deleteTemplate' in $store.getters.apis)"
+            :title="$t('label.action.delete.template')"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="onShowDeleteModal(record)"/>
+        </template>
       </template>
       <template #expandedRowRender="{ record }">
         <a-table
-          style="marginLeft: -50px; marginTop: 10px; marginBottom: 10px"
+          style="margin: 10px 0;"
           :columns="innerColumns"
           :data-source="record.downloaddetails"
           :pagination="false"
@@ -56,23 +75,6 @@
           :rowKey="record => record.zoneid">
         </a-table>
       </template>
-      <template #action="{ record }">
-        <tooltip-button
-          style="margin-right: 5px"
-          :disabled="!('copyTemplate' in $store.getters.apis && record.isready)"
-          :title="$t('label.action.copy.template')"
-          icon="copy-outlined"
-          :loading="copyLoading"
-          @onClick="showCopyTemplate(record)" />
-        <tooltip-button
-          style="margin-right: 5px"
-          :disabled="!('deleteTemplate' in $store.getters.apis)"
-          :title="$t('label.action.delete.template')"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="onShowDeleteModal(record)"/>
-      </template>
     </a-table>
     <a-pagination
       class="row-element"
@@ -171,12 +173,12 @@
           size="middle"
           :columns="selectedColumns"
           :dataSource="selectedItems"
-          :rowKey="(record, idx) => record.zoneid || record.name"
+          :rowKey="record => record.zoneid || record.name"
           :pagination="true"
           style="overflow-y: auto">
         </a-table>
         <a-spin :spinning="deleteLoading">
-          <a-form-item :label="$t('label.isforced')" style="margin-bottom: 0;">
+          <a-form-item ref="forcedDelete" name="forcedDelete" :label="$t('label.isforced')" style="margin-bottom: 0;">
             <a-switch v-model:checked="forcedDelete" v-focus="true"></a-switch>
           </a-form-item>
           <div :span="24" class="action-button">
@@ -260,18 +262,18 @@
   created () {
     this.columns = [
       {
+        key: 'zonename',
         title: this.$t('label.zonename'),
-        dataIndex: 'zonename',
-        slots: { customRender: 'zonename' }
+        dataIndex: 'zonename'
       },
       {
         title: this.$t('label.status'),
         dataIndex: 'status'
       },
       {
+        key: 'isready',
         title: this.$t('label.isready'),
-        dataIndex: 'isready',
-        slots: { customRender: 'isready' }
+        dataIndex: 'isready'
       }
     ]
     this.innerColumns = [
@@ -290,10 +292,10 @@
     ]
     if (this.isActionPermitted()) {
       this.columns.push({
+        key: 'actions',
         title: '',
-        dataIndex: 'action',
-        width: 100,
-        slots: { customRender: 'action' }
+        dataIndex: 'actions',
+        width: 100
       })
     }
 
@@ -422,9 +424,9 @@
     deleteTemplates (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/image/UpdateISO.vue b/ui/src/views/image/UpdateISO.vue
index 2fa7979..4d7140d 100644
--- a/ui/src/views/image/UpdateISO.vue
+++ b/ui/src/views/image/UpdateISO.vue
@@ -48,12 +48,15 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-model:value="form.ostypeid"
             :loading="osTypes.loading"
             :placeholder="apiParams.ostypeid.description">
-            <a-select-option v-for="opt in osTypes.opts" :key="opt.id">
+            <a-select-option
+              v-for="opt in osTypes.opts"
+              :key="opt.id"
+              :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -69,12 +72,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-model:value="userdataid"
                 :placeholder="linkUserDataParams.userdataid.description"
                 :loading="userdata.loading">
-                <a-select-option v-for="opt in userdata.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdata.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -86,13 +89,17 @@
                 <tooltip-label :title="$t('label.userdatapolicy')" :tooltip="$t('label.userdatapolicy.tooltip')"/>
               </template>
               <a-select
+                showSearch
                 v-model:value="userdatapolicy"
                 :placeholder="linkUserDataParams.userdatapolicy.description"
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id">
+                <a-select-option
+                  v-for="opt in userdatapolicylist.opts"
+                  :key="opt.id"
+                  :label="opt.name || opt.description">
                   {{ opt.id || opt.description }}
                 </a-select-option>
               </a-select>
diff --git a/ui/src/views/image/UpdateKubernetesSupportedVersion.vue b/ui/src/views/image/UpdateKubernetesSupportedVersion.vue
index 9081eb0..8d2b4e4 100644
--- a/ui/src/views/image/UpdateKubernetesSupportedVersion.vue
+++ b/ui/src/views/image/UpdateKubernetesSupportedVersion.vue
@@ -30,12 +30,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="stateLoading"
             :placeholder="apiParams.state.description"
             v-focus="true" >
-            <a-select-option v-for="(opt, optIndex) in states" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in states" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/image/UpdateTemplate.vue b/ui/src/views/image/UpdateTemplate.vue
index 441c685..9b6f754 100644
--- a/ui/src/views/image/UpdateTemplate.vue
+++ b/ui/src/views/image/UpdateTemplate.vue
@@ -92,12 +92,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-model:value="form.ostypeid"
                 :loading="osTypes.loading"
                 :placeholder="apiParams.ostypeid.description">
-                <a-select-option v-for="opt in osTypes.opts" :key="opt.id">
+                <a-select-option v-for="opt in osTypes.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -114,12 +114,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }"
                 v-model:value="userdataid"
                 :placeholder="linkUserDataParams.userdataid.description"
                 :loading="userdata.loading">
-                <a-select-option v-for="opt in userdata.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdata.opts" :key="opt.id" :label="opt.name || opt.description">
                   {{ opt.name || opt.description }}
                 </a-select-option>
               </a-select>
@@ -131,13 +131,14 @@
                 <tooltip-label :title="$t('label.userdatapolicy')" :tooltip="$t('label.userdatapolicy.tooltip')"/>
               </template>
               <a-select
+                showSearch
                 v-model:value="userdatapolicy"
                 :placeholder="linkUserDataParams.userdatapolicy.description"
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
-                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id">
+                <a-select-option v-for="opt in userdatapolicylist.opts" :key="opt.id" :label="opt.id || opt.description">
                   {{ opt.id || opt.description }}
                 </a-select-option>
               </a-select>
@@ -164,9 +165,9 @@
           </span>
           <a-select
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-model:value="form.templatetype"
             :placeholder="apiParams.templatetype.description"
diff --git a/ui/src/views/image/UpdateTemplateIsoPermissions.vue b/ui/src/views/image/UpdateTemplateIsoPermissions.vue
index 05840c6..c7b0f9e 100644
--- a/ui/src/views/image/UpdateTemplateIsoPermissions.vue
+++ b/ui/src/views/image/UpdateTemplateIsoPermissions.vue
@@ -29,9 +29,9 @@
         @change="fetchData"
         v-focus="true"
         showSearch
-        optionFilterProp="label"
+        optionFilterProp="value"
         :filterOption="(input, option) => {
-          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
         }" >
         <a-select-option :value="$t('label.add')">{{ $t('label.add') }}</a-select-option>
         <a-select-option :value="$t('label.remove')">{{ $t('label.remove') }}</a-select-option>
@@ -50,9 +50,9 @@
           :defaultValue="$t('label.account')"
           @change="fetchData"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }">
           <a-select-option :value="$t('label.account')">{{ $t('label.account') }}</a-select-option>
           <a-select-option :value="$t('label.project')">{{ $t('label.project') }}</a-select-option>
diff --git a/ui/src/views/infra/AddPrimaryStorage.vue b/ui/src/views/infra/AddPrimaryStorage.vue
index c1c733d..b30b034 100644
--- a/ui/src/views/infra/AddPrimaryStorage.vue
+++ b/ui/src/views/infra/AddPrimaryStorage.vue
@@ -35,11 +35,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="apiParams.scope.description" >
-            <a-select-option :value="'cluster'"> {{ $t('label.clusterid') }} </a-select-option>
-            <a-select-option :value="'zone'"> {{ $t('label.zoneid') }} </a-select-option>
+            <a-select-option :value="'cluster'" :label="$t('label.clusterid')"> {{ $t('label.clusterid') }} </a-select-option>
+            <a-select-option :value="'zone'" :label="$t('label.zoneid')"> {{ $t('label.zoneid') }} </a-select-option>
           </a-select>
         </a-form-item>
         <div v-if="form.scope === 'zone'">
@@ -50,9 +50,9 @@
             <a-select
               v-model:value="form.hypervisor"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.hypervisor.description" >
               <a-select-option :value="hypervisor" v-for="(hypervisor, idx) in hypervisors" :key="idx">
@@ -93,10 +93,10 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.podid.description">
-              <a-select-option :value="pod.id" v-for="(pod) in pods" :key="pod.id">
+              <a-select-option :value="pod.id" v-for="(pod) in pods" :key="pod.id" :label="pod.name">
                 {{ pod.name }}
               </a-select-option>
             </a-select>
@@ -111,10 +111,10 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.clusterid.description">
-              <a-select-option :value="cluster.id" v-for="cluster in clusters" :key="cluster.id">
+              <a-select-option :value="cluster.id" v-for="cluster in clusters" :key="cluster.id" :label="cluster.name">
                 {{ cluster.name }}
               </a-select-option>
             </a-select>
@@ -127,9 +127,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option :value="host.id" v-for="host in hosts" :key="host.id">
+              <a-select-option :value="host.id" v-for="host in hosts" :key="host.id" :label="host.name">
                 {{ host.name }}
               </a-select-option>
             </a-select>
@@ -148,9 +148,9 @@
           <a-select
             v-model:value="form.protocol"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="apiParams.clusterid.description">
             <a-select-option :value="protocol" v-for="(protocol,idx) in protocols" :key="idx">
@@ -218,9 +218,9 @@
               v-model:value="form.provider"
               @change="updateProviderAndProtocol"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.provider.description">
               <a-select-option :value="provider" v-for="(provider,idx) in providers" :key="idx">
diff --git a/ui/src/views/infra/AddSecondaryStorage.vue b/ui/src/views/infra/AddSecondaryStorage.vue
index 3dbf8cc..4adc4e9 100644
--- a/ui/src/views/infra/AddSecondaryStorage.vue
+++ b/ui/src/views/infra/AddSecondaryStorage.vue
@@ -33,9 +33,9 @@
             v-model:value="form.provider"
             @change="val => { form.provider = val }"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               :value="prov"
diff --git a/ui/src/views/infra/AsyncJobsTab.vue b/ui/src/views/infra/AsyncJobsTab.vue
new file mode 100644
index 0000000..7b514a4
--- /dev/null
+++ b/ui/src/views/infra/AsyncJobsTab.vue
@@ -0,0 +1,104 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <a-table
+    class="table"
+    size="small"
+    :columns="columns"
+    :dataSource="jobs"
+    :rowKey="item => item.id"
+    :pagination="false" >
+    <template #cmd="{ text }">
+      {{ text.split('.').pop() }}
+    </template>
+    <template #account="{ text, record }">
+      <router-link :to="{ path: '/account/' + record.accountid }">{{ text }}</router-link>
+    </template>
+    <template #domainpath="{ text, record }">
+      <router-link :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
+    </template>
+  </a-table>
+</template>
+
+<script>
+import { api } from '@/api'
+import Status from '@/components/widgets/Status'
+
+export default {
+  name: 'AsyncJobsTab',
+  components: {
+    Status
+  },
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  data () {
+    return {
+      jobs: [],
+      columns: [
+        {
+          title: this.$t('label.command'),
+          dataIndex: 'cmd',
+          slots: { customRender: 'cmd' }
+        },
+        {
+          title: this.$t('label.resourcetype'),
+          dataIndex: 'jobinstancetype'
+        },
+        {
+          title: this.$t('label.account'),
+          dataIndex: 'account',
+          slots: { customRender: 'account' }
+        },
+        {
+          title: this.$t('label.domain'),
+          dataIndex: 'domainpath',
+          slots: { customRender: 'domainpath' }
+        },
+        {
+          title: this.$t('label.created'),
+          dataIndex: 'created'
+        }
+      ]
+    }
+  },
+  created () {
+    this.fetchData()
+  },
+  watch: {
+    resource: function (newItem) {
+      this.fetchData()
+    }
+  },
+  methods: {
+    fetchData () {
+      this.jobs = []
+      api('listAsyncJobs', {
+        listall: true,
+        isrecursive: true,
+        managementserverid: this.resource.id
+      }).then(json => {
+        this.jobs = json.listasyncjobsresponse.asyncjobs || []
+      })
+    }
+  }
+}
+</script>
diff --git a/ui/src/views/infra/ClusterAdd.vue b/ui/src/views/infra/ClusterAdd.vue
index 5450db9..862b458 100644
--- a/ui/src/views/infra/ClusterAdd.vue
+++ b/ui/src/views/infra/ClusterAdd.vue
@@ -49,9 +49,9 @@
           v-model:value="hypervisor"
           @change="resetAllFields"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="hv in hypervisorsList"
@@ -69,12 +69,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="pod in podsList"
             :value="pod.id"
-            :key="pod.id">
+            :key="pod.id"
+            :label="pod.name">
             {{ pod.name }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/infra/Confirmation.vue b/ui/src/views/infra/Confirmation.vue
new file mode 100644
index 0000000..96b7f6f
--- /dev/null
+++ b/ui/src/views/infra/Confirmation.vue
@@ -0,0 +1,129 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <a-spin :spinning="loading">
+    <div class="form-layout" v-ctrl-enter="handleSubmit">
+      <a-form
+        :ref="formRef"
+        :model="form"
+        :rules="rules"
+        @finish="handleSubmit"
+      >
+        <a-alert type="error">
+          <template #message>
+            <span v-html="$t(action.currentAction.message)" />
+          </template>
+        </a-alert>
+        <a-alert type="warning" style="margin-top: 10px">
+          <template #message>
+            <span>{{ $t('message.confirm.type') }} "{{ action.currentAction.confirmationText }}"</span>
+          </template>
+        </a-alert>
+        <a-form-item ref="confirmation" name="confirmation" style="margin-top: 10px">
+            <a-input v-model:value="form.confirmation" />
+        </a-form-item>
+      </a-form>
+
+      <div :span="24" class="action-button">
+        <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
+        <a-button type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
+      </div>
+
+    </div>
+  </a-spin>
+</template>
+
+<script>
+
+import { api } from '@/api'
+import { ref, reactive } from 'vue'
+
+export default {
+  name: 'Confirmation',
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    },
+    action: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  inject: ['parentFetchData'],
+  data () {
+    return {
+      loading: false
+    }
+  },
+  created () {
+    this.initForm()
+  },
+  methods: {
+    initForm () {
+      this.formRef = ref()
+      this.form = reactive({})
+      this.rules = reactive({
+        confirmation: [{
+          validator: this.checkConfirmation,
+          message: this.$t('message.error.confirm.text')
+        }]
+      })
+    },
+    async checkConfirmation (rule, value) {
+      if (value && value === 'SHUTDOWN') {
+        return Promise.resolve()
+      }
+      return Promise.reject(rule.message)
+    },
+    handleSubmit (e) {
+      e.preventDefault()
+      if (this.loading) return
+      this.formRef.value.validate().then(() => {
+        this.loading = true
+        const params = { managementserverid: this.resource.id }
+        api(this.action.currentAction.api, params).then(() => {
+          this.$message.success(this.$t(this.action.currentAction.label) + ' : ' + this.resource.name)
+          this.closeAction()
+          this.parentFetchData()
+        }).catch(error => {
+          this.$notifyError(error)
+        }).finally(() => {
+          this.loading = false
+        })
+      })
+    },
+    closeAction () {
+      this.$emit('close-action')
+    }
+  }
+}
+
+</script>
+<style lang="scss" scoped>
+.form-layout {
+  width: 80vw;
+  @media (min-width: 700px) {
+    width: 600px;
+  }
+}
+
+.form {
+  margin: 10px 0;
+}
+</style>
diff --git a/ui/src/views/infra/HostAdd.vue b/ui/src/views/infra/HostAdd.vue
index 063e8dd..def050a 100644
--- a/ui/src/views/infra/HostAdd.vue
+++ b/ui/src/views/infra/HostAdd.vue
@@ -62,13 +62,14 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               @change="fetchClusters">
               <a-select-option
                 v-for="pod in podsList"
                 :value="pod.id"
-                :key="pod.id">
+                :key="pod.id"
+                :label="pod.name">
                 {{ pod.name }}
               </a-select-option>
             </a-select>
@@ -83,13 +84,14 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               @change="handleChangeCluster">
               <a-select-option
                 v-for="cluster in clustersList"
                 :value="cluster.id"
-                :key="cluster.id">
+                :key="cluster.id"
+                :label="cluster.name">
                 {{ cluster.name }}
               </a-select-option>
             </a-select>
@@ -206,9 +208,9 @@
             <a-select
               mode="tags"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               v-model:value="form.hosttags"
               :placeholder="placeholder.hosttags">
diff --git a/ui/src/views/infra/HostEnableDisable.vue b/ui/src/views/infra/HostEnableDisable.vue
new file mode 100644
index 0000000..bc71aa2
--- /dev/null
+++ b/ui/src/views/infra/HostEnableDisable.vue
@@ -0,0 +1,133 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div class="form-layout">
+    <a-form
+      :ref="formRef"
+      :model="form"
+      :rules="rules"
+      @finish="handleSubmit"
+      v-ctrl-enter="handleSubmit"
+      class="form"
+      layout="vertical"
+    >
+      <a-alert type="warning">
+        <template #message>
+          <span v-html="$t('message.confirm.enable.host')" />
+        </template>
+      </a-alert>
+      <div v-show="enableKVMAutoEnableDisableSetting" class="reason">
+        <a-form-item
+          class="form__item"
+          name="reason"
+          ref="reason"
+          :label="'The setting \'enable.kvm.host.auto.enable.disable\' is enabled, ' +
+            ' can specify a reason for ' + (resourcestate === 'Enabled' ? 'disabling' : 'enabling') + ' this host'">
+          <a-textarea
+            v-model:value="form.reason"
+            :placeholder="'(Optional) Reason to ' + (resourcestate === 'Enabled' ? 'disable' : 'enable') + ' this host'"
+            rows="3"
+          />
+        </a-form-item>
+      </div>
+      <div :span="24" class="action-button">
+        <a-button @click="$emit('close-action')">{{ $t('label.cancel') }}</a-button>
+        <a-button type="primary" @click="handleSubmit" ref="submit">{{ $t('label.ok') }}</a-button>
+      </div>
+    </a-form>
+  </div>
+</template>
+
+<script>
+import { ref, reactive, toRaw } from 'vue'
+import { api } from '@/api'
+
+export default {
+  name: 'HostEnableDisable',
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  data () {
+    return {
+      resourcestate: '',
+      allocationstate: '',
+      enableKVMAutoEnableDisableSetting: false
+    }
+  },
+  created () {
+    this.initForm()
+    this.fetchAutoEnableDisableKVMSetting()
+    this.resourcestate = this.resource.resourcestate
+    this.allocationstate = this.resourcestate === 'Enabled' ? 'Disable' : 'Enable'
+  },
+  beforeCreate () {
+  },
+  methods: {
+    initForm () {
+      this.formRef = ref()
+      this.form = reactive({})
+      this.rules = reactive({})
+    },
+    fetchAutoEnableDisableKVMSetting () {
+      if (this.resource.hypervisor !== 'KVM') {
+        return
+      }
+      api('listConfigurations', { name: 'enable.kvm.host.auto.enable.disable', clusterid: this.resource.clusterid }).then(json => {
+        if (json.listconfigurationsresponse.configuration[0]) {
+          this.enableKVMAutoEnableDisableSetting = json.listconfigurationsresponse.configuration[0].value
+        }
+      })
+    },
+    handleSubmit (e) {
+      e.preventDefault()
+      this.formRef.value.validate().then(() => {
+        const values = toRaw(this.form)
+
+        var data = {
+          allocationstate: this.allocationstate,
+          id: this.resource.id
+        }
+        if (values.reason) {
+          data.annotation = values.reason
+        }
+        api('updateHost', data).then(_ => {
+          this.$emit('close-action')
+        })
+      })
+    }
+  }
+}
+
+</script>
+
+<style scoped>
+.reason {
+  padding-top: 20px
+}
+
+.form-layout {
+    width: 30vw;
+
+    @media (min-width: 500px) {
+      width: 450px;
+    }
+  }
+</style>
diff --git a/ui/src/views/infra/InfraSummary.vue b/ui/src/views/infra/InfraSummary.vue
index 941d15b..8411dca 100644
--- a/ui/src/views/infra/InfraSummary.vue
+++ b/ui/src/views/infra/InfraSummary.vue
@@ -149,8 +149,10 @@
     </a-col>
     <template v-for="(section, index) in sections" :key="index">
       <a-col
+        :xs="12"
+        :sm="8"
         :md="6"
-        style="margin-bottom: 12px"
+        :style="{ marginBottom: '12px' }"
         v-if="routes[section]">
         <chart-card :loading="loading">
           <div class="chart-card-inner">
diff --git a/ui/src/views/infra/MigrateData.vue b/ui/src/views/infra/MigrateData.vue
index fa42579..f6c9f1f 100644
--- a/ui/src/views/infra/MigrateData.vue
+++ b/ui/src/views/infra/MigrateData.vue
@@ -37,11 +37,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="store in imageStores"
-              :key="store.id"> {{ store.name || opt.url }}
+              :key="store.id"
+              :label="store.name || opt.url"> {{ store.name || opt.url }}
             </a-select-option>
           </a-select>
         </a-form-item>
@@ -56,11 +57,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="store in destStores"
-              :key="store.id"> {{ store.name || opt.url }}
+              :key="store.id"
+              :label="store.name || opt.url"> {{ store.name || opt.url }}
             </a-select-option>
           </a-select>
         </a-form-item>
@@ -71,10 +73,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="Complete">{{ $t('label.complete') }}</a-select-option>
-            <a-select-option value="Balance">{{ $t('label.balance') }}</a-select-option>
+            <a-select-option value="Complete" :label="$t('label.complete')">{{ $t('label.complete') }}</a-select-option>
+            <a-select-option value="Balance" :label="$t('label.balance')">{{ $t('label.balance') }}</a-select-option>
           </a-select>
         </a-form-item>
         <div :span="24" class="action-button">
diff --git a/ui/src/views/infra/network/DedicatedVLANTab.vue b/ui/src/views/infra/network/DedicatedVLANTab.vue
index 7e59c55..7343b30 100644
--- a/ui/src/views/infra/network/DedicatedVLANTab.vue
+++ b/ui/src/views/infra/network/DedicatedVLANTab.vue
@@ -33,21 +33,23 @@
       :dataSource="items"
       :pagination="false"
       :rowKey="record => record.id">
-      <template #actions="{record}">
-        <a-popconfirm
-          :title="`${$t('label.delete')}?`"
-          @confirm="handleDelete(record)"
-          :okText="$t('label.yes')"
-          :cancelText="$t('label.no')"
-          placement="top"
-        >
-          <tooltip-button
-            :tooltip="$t('label.delete')"
-            :disabled="!('releaseDedicatedGuestVlanRange' in $store.getters.apis)"
-            icon="delete-outlined"
-            type="primary"
-            :danger="true" />
-        </a-popconfirm>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'actions'">
+          <a-popconfirm
+            :title="`${$t('label.delete')}?`"
+            @confirm="handleDelete(record)"
+            :okText="$t('label.yes')"
+            :cancelText="$t('label.no')"
+            placement="top"
+          >
+            <tooltip-button
+              :tooltip="$t('label.delete')"
+              :disabled="!('releaseDedicatedGuestVlanRange' in $store.getters.apis)"
+              icon="delete-outlined"
+              type="primary"
+              :danger="true" />
+          </a-popconfirm>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -91,10 +93,10 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option value="account">{{ $t('label.account') }}</a-select-option>
-              <a-select-option value="project">{{ $t('label.project') }}</a-select-option>
+              <a-select-option value="account" :label="$t('label.account')">{{ $t('label.account') }}</a-select-option>
+              <a-select-option value="project" :label="$t('label.project')">{{ $t('label.project') }}</a-select-option>
             </a-select>
           </a-form-item>
 
@@ -121,7 +123,7 @@
             <a-select
               v-model:value="form.account"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
                 return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
@@ -219,8 +221,8 @@
           dataIndex: 'account'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ]
     }
diff --git a/ui/src/views/infra/network/EditTrafficLabel.vue b/ui/src/views/infra/network/EditTrafficLabel.vue
index f147bab..f8f44d9 100644
--- a/ui/src/views/infra/network/EditTrafficLabel.vue
+++ b/ui/src/views/infra/network/EditTrafficLabel.vue
@@ -39,9 +39,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="type in trafficTypes" :key="type.id">
+            <a-select-option v-for="type in trafficTypes" :key="type.id" :label="type.traffictype">
               {{ type.traffictype }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/infra/network/IpRangesTabGuest.vue b/ui/src/views/infra/network/IpRangesTabGuest.vue
index 4de5fd2..2c575d7 100644
--- a/ui/src/views/infra/network/IpRangesTabGuest.vue
+++ b/ui/src/views/infra/network/IpRangesTabGuest.vue
@@ -33,20 +33,22 @@
       :rowKey="record => record.id + record.prefix"
       :pagination="false"
     >
-      <template #allocated="{ record }">
-        {{ record.usedsubnets + '/' + record.totalsubnets }}
-      </template>
-      <template #actions="{ record }">
-        <div class="actions">
-          <tooltip-button
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.delete.ip.v6.prefix')"
-            type="primary"
-            icon="delete-outlined"
-            :danger="true"
-            @click="handleDeleteIpv6Prefix(record)"
-            :disabled="!('deleteGuestNetworkIpv6Prefix' in $store.getters.apis)" />
-        </div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'allocated'">
+          {{ record.usedsubnets + '/' + record.totalsubnets }}
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.delete.ip.v6.prefix')"
+              type="primary"
+              icon="delete-outlined"
+              :danger="true"
+              @click="handleDeleteIpv6Prefix(record)"
+              :disabled="!('deleteGuestNetworkIpv6Prefix' in $store.getters.apis)" />
+          </div>
+        </template>
       </template>
     </a-table>
     <br>
@@ -69,12 +71,14 @@
       :rowKey="record => record.id"
       :pagination="false"
     >
-      <template #name="{ text, record }">
-        <resource-icon v-if="record.icon" :image="record.icon.base64image" size="1x" style="margin-right: 5px"/>
-        <apartment-outlined v-else style="margin-right: 5px"/>
-        <router-link :to="{ path: '/guestnetwork/' + record.id }">
-          {{ text }}
-        </router-link>
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'name'">
+          <resource-icon v-if="record.icon" :image="record.icon.base64image" size="1x" style="margin-right: 5px"/>
+          <apartment-outlined v-else style="margin-right: 5px"/>
+          <router-link :to="{ path: '/guestnetwork/' + record.id }">
+            {{ text }}
+          </router-link>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -170,9 +174,9 @@
       pageSize: 10,
       columns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
           title: this.$t('label.type'),
@@ -202,12 +206,12 @@
           dataIndex: 'prefix'
         },
         {
-          title: this.$t('label.allocated'),
-          slots: { customRender: 'allocated' }
+          key: 'allocated',
+          title: this.$t('label.allocated')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       addIpv6PrefixModal: false
diff --git a/ui/src/views/infra/network/IpRangesTabManagement.vue b/ui/src/views/infra/network/IpRangesTabManagement.vue
index e7abfa8..f6d0029 100644
--- a/ui/src/views/infra/network/IpRangesTabManagement.vue
+++ b/ui/src/views/infra/network/IpRangesTabManagement.vue
@@ -34,20 +34,22 @@
       :rowKey="record => record.id + record.startip"
       :pagination="false"
     >
-      <template #forsystemvms="{ record }">
-        <a-checkbox v-model:checked="record.forsystemvms" />
-      </template>
-      <template #actions="{ record }">
-        <div class="actions">
-          <tooltip-button
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.remove.ip.range')"
-            :disabled="!('deleteManagementNetworkIpRange' in $store.getters.apis)"
-            icon="delete-outlined"
-            type="primary"
-            :danger="true"
-            @onClick="handleDeleteIpRange(record)" />
-        </div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'forsystemvms'">
+          <a-checkbox v-model:checked="record.forsystemvms" />
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.remove.ip.range')"
+              :disabled="!('deleteManagementNetworkIpRange' in $store.getters.apis)"
+              icon="delete-outlined"
+              type="primary"
+              :danger="true"
+              @onClick="handleDeleteIpRange(record)" />
+          </div>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -90,9 +92,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="item in pods" :key="item.id" :value="item.id">{{ item.name }}</a-select-option>
+            <a-select-option v-for="item in pods" :key="item.id" :value="item.id" :label="item.name">{{ item.name }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item name="gateway" ref="gateway" :label="$t('label.gateway')" class="form__item">
@@ -170,28 +172,28 @@
           dataIndex: 'netmask'
         },
         {
+          key: 'vlan',
           title: this.$t('label.vlan'),
-          dataIndex: 'vlanid',
-          slots: { customRender: 'vlan' }
+          dataIndex: 'vlanid'
         },
         {
+          key: 'startip',
           title: this.$t('label.startip'),
-          dataIndex: 'startip',
-          slots: { customRender: 'startip' }
+          dataIndex: 'startip'
         },
         {
+          key: 'endip',
           title: this.$t('label.endip'),
-          dataIndex: 'endip',
-          slots: { customRender: 'endip' }
+          dataIndex: 'endip'
         },
         {
+          key: 'forsystemvms',
           title: this.$t('label.system.vms'),
-          dataIndex: 'forsystemvms',
-          slots: { customRender: 'forsystemvms' }
+          dataIndex: 'forsystemvms'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ]
     }
diff --git a/ui/src/views/infra/network/IpRangesTabPublic.vue b/ui/src/views/infra/network/IpRangesTabPublic.vue
index 247a8fa..c5505da 100644
--- a/ui/src/views/infra/network/IpRangesTabPublic.vue
+++ b/ui/src/views/infra/network/IpRangesTabPublic.vue
@@ -34,58 +34,60 @@
       :rowKey="record => record.id"
       :pagination="false"
     >
-      <template #gateway="{record}">
-        {{ record.gateway || record.ip6gateway }}
-      </template>
-      <template #cidr="{record}">
-        {{ record.cidr || record.ip6cidr }}
-      </template>
-      <template #startip="{record}">
-        {{ record.startip || record.startipv6 }}
-      </template>
-      <template #endip="{record}">
-        {{ record.endip || record.endipv6 }}
-      </template>
-      <template #account="{record}" v-if="!basicGuestNetwork">
-        <a-button @click="() => handleOpenAccountModal(record)">{{ `[${record.domain}] ${record.account === undefined ? '' : record.account}` }}</a-button>
-      </template>
-      <template #actions="{record}">
-        <div
-          class="actions"
-          style="text-align: right" >
-          <tooltip-button
-            v-if="record.account === 'system' && !basicGuestNetwork && record.gateway && !record.ip6gateway"
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.add.account')"
-            icon="user-add-outlined"
-            @onClick="() => handleOpenAddAccountModal(record)"
-            :disabled="!('dedicatePublicIpRange' in $store.getters.apis)" />
-          <tooltip-button
-            v-if="record.account !== 'system' && !basicGuestNetwork"
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.release.account')"
-            icon="user-delete-outlined"
-            type="primary"
-            :danger="true"
-            @onClick="() => handleRemoveAccount(record.id)"
-            :disabled="!('releasePublicIpRange' in $store.getters.apis)" />
-          <tooltip-button
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.update.ip.range')"
-            icon="edit-outlined"
-            type="primary"
-            :danger="true"
-            @onClick="() => handleUpdateIpRangeModal(record)"
-            :disabled="!('updateVlanIpRange' in $store.getters.apis)" />
-          <tooltip-button
-            tooltipPlacement="bottom"
-            :tooltip="$t('label.remove.ip.range')"
-            icon="delete-outlined"
-            type="primary"
-            :danger="true"
-            @onClick="handleDeleteIpRange(record.id)"
-            :disabled="!('deleteVlanIpRange' in $store.getters.apis)" />
-        </div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'gateway'">
+          {{ record.gateway || record.ip6gateway }}
+        </template>
+        <template v-if="column.key === 'cidr'">
+          {{ record.cidr || record.ip6cidr }}
+        </template>
+        <template v-if="column.key === 'startip'">
+          {{ record.startip || record.startipv6 }}
+        </template>
+        <template v-if="column.key === 'endip'">
+          {{ record.endip || record.endipv6 }}
+        </template>
+        <template v-if="column.key === 'account' && !basicGuestNetwork">
+          <a-button @click="() => handleOpenAccountModal(record)">{{ `[${record.domain}] ${record.account === undefined ? '' : record.account}` }}</a-button>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div
+            class="actions"
+            style="text-align: right" >
+            <tooltip-button
+              v-if="record.account === 'system' && !basicGuestNetwork && record.gateway && !record.ip6gateway"
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.add.account')"
+              icon="user-add-outlined"
+              @onClick="() => handleOpenAddAccountModal(record)"
+              :disabled="!('dedicatePublicIpRange' in $store.getters.apis)" />
+            <tooltip-button
+              v-if="record.account !== 'system' && !basicGuestNetwork"
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.release.account')"
+              icon="user-delete-outlined"
+              type="primary"
+              :danger="true"
+              @onClick="() => handleRemoveAccount(record.id)"
+              :disabled="!('releasePublicIpRange' in $store.getters.apis)" />
+            <tooltip-button
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.update.ip.range')"
+              icon="edit-outlined"
+              type="primary"
+              :danger="true"
+              @onClick="() => handleUpdateIpRangeModal(record)"
+              :disabled="!('updateVlanIpRange' in $store.getters.apis)" />
+            <tooltip-button
+              tooltipPlacement="bottom"
+              :tooltip="$t('label.remove.ip.range')"
+              icon="delete-outlined"
+              type="primary"
+              :danger="true"
+              @onClick="handleDeleteIpRange(record.id)"
+              :disabled="!('deleteVlanIpRange' in $store.getters.apis)" />
+          </div>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -152,12 +154,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="domain in domains"
               :key="domain.id"
-              :value="domain.id">{{ domain.path || domain.name || domain.description }}
+              :value="domain.id"
+              :label="domain.path || domain.name || domain.description">{{ domain.path || domain.name || domain.description }}
             </a-select-option>
           </a-select>
         </div>
@@ -205,10 +208,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             v-focus="true">
-            <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id">{{ pod.name }}</a-select-option>
+            <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id" :label="pod.name">{{ pod.name }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item name="vlan" ref="vlan" :label="$t('label.vlan')" class="form__item" v-if="!basicGuestNetwork">
@@ -255,12 +258,13 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
                 <a-select-option
                   v-for="domain in domains"
                   :key="domain.id"
-                  :value="domain.id">{{ domain.path || domain.name || domain.description }}
+                  :value="domain.id"
+                  :label="domain.path || domain.name || domain.description">{{ domain.path || domain.name || domain.description }}
                 </a-select-option>
               </a-select>
             </a-form-item>
@@ -377,28 +381,28 @@
       pageSize: 10,
       columns: [
         {
-          title: this.$t('label.gateway'),
-          slots: { customRender: 'gateway' }
+          key: 'gateway',
+          title: this.$t('label.gateway')
         },
         {
-          title: this.$t('label.cidr'),
-          slots: { customRender: 'cidr' }
+          key: 'cidr',
+          title: this.$t('label.cidr')
         },
         {
           title: this.$t('label.vlan'),
           dataIndex: 'vlan'
         },
         {
-          title: this.$t('label.startip'),
-          slots: { customRender: 'startip' }
+          key: 'startip',
+          title: this.$t('label.startip')
         },
         {
-          title: this.$t('label.endip'),
-          slots: { customRender: 'endip' }
+          key: 'endip',
+          title: this.$t('label.endip')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ]
     }
@@ -409,8 +413,8 @@
     if (!this.basicGuestNetwork) {
       this.columns.splice(6, 0,
         {
-          title: this.$t('label.account'),
-          slots: { customRender: 'account' }
+          key: 'account',
+          title: this.$t('label.account')
         }
       )
     } else {
diff --git a/ui/src/views/infra/network/IpRangesTabStorage.vue b/ui/src/views/infra/network/IpRangesTabStorage.vue
index 29e146d..c8665d6 100644
--- a/ui/src/views/infra/network/IpRangesTabStorage.vue
+++ b/ui/src/views/infra/network/IpRangesTabStorage.vue
@@ -34,17 +34,19 @@
       :rowKey="record => record.id"
       :pagination="false"
     >
-      <template #name="{record}">
-        <div>{{ returnPodName(record.podid) }}</div>
-      </template>
-      <template #actions="{record}">
-        <tooltip-button
-          :tooltip="$t('label.remove.ip.range')"
-          :disabled="!('deleteStorageNetworkIpRange' in $store.getters.apis)"
-          icon="delete-outlined"
-          type="primary"
-          :danger="true"
-          @onClick="handleDeleteIpRange(record.id)" />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          <div>{{ returnPodName(record.podid) }}</div>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.remove.ip.range')"
+            :disabled="!('deleteStorageNetworkIpRange' in $store.getters.apis)"
+            icon="delete-outlined"
+            type="primary"
+            :danger="true"
+            @onClick="handleDeleteIpRange(record.id)" />
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -88,9 +90,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id">{{ pod.name }}</a-select-option>
+            <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id" :label="pod.name">{{ pod.name }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item name="gateway" ref="gateway" :label="$t('label.gateway')" class="form__item">
@@ -151,8 +153,8 @@
       defaultSelectedPod: null,
       columns: [
         {
-          title: this.$t('label.podid'),
-          slots: { customRender: 'name' }
+          key: 'name',
+          title: this.$t('label.podid')
         },
         {
           title: this.$t('label.gateway'),
@@ -175,8 +177,8 @@
           dataIndex: 'endip'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       page: 1,
diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue
index 184804e..4985389 100644
--- a/ui/src/views/infra/network/ServiceProvidersTab.vue
+++ b/ui/src/views/infra/network/ServiceProvidersTab.vue
@@ -27,8 +27,10 @@
           v-for="item in hardcodedNsps"
           :key="item.title">
           <template #tab>
-            {{ $t(item.title) }}
-            <status :text="item.title in nsps ? nsps[item.title].state : $t('label.disabled')" style="margin-bottom: 6px; margin-left: 6px" />
+            <span>
+              {{ $t(item.title) }}
+              <status :text="item.title in nsps ? nsps[item.title].state : $t('label.disabled')" style="margin-bottom: 6px; margin-left: 6px" />
+            </span>
           </template>
           <provider-item
             v-if="tabKey===item.title"
@@ -109,11 +111,12 @@
                 showSearch
                 optionFilterProp="label"
                 :filterOption="(input, option) => {
-                  return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                 }" >
                 <a-select-option
                   v-for="(opt, idx) in field.opts"
-                  :key="idx">{{ opt.name || opt.description }}</a-select-option>
+                  :key="idx"
+                  :label="opt.name || opt.description">{{ opt.name || opt.description }}</a-select-option>
               </a-select>
             </span>
             <span v-else>
@@ -356,7 +359,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['hostname', 'action']
+              columns: ['hostname', 'actions']
             }
           ]
         },
@@ -415,7 +418,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['hostname', 'action']
+              columns: ['hostname', 'actions']
             }
           ]
         },
@@ -496,7 +499,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['hostname', 'insideportprofile', 'action']
+              columns: ['hostname', 'insideportprofile', 'actions']
             }
           ]
         },
@@ -587,7 +590,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['ipaddress', 'lbdevicestate', 'action']
+              columns: ['ipaddress', 'lbdevicestate', 'actions']
             }
           ]
         },
@@ -737,7 +740,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['ipaddress', 'lbdevicestate', 'action']
+              columns: ['ipaddress', 'lbdevicestate', 'actions']
             }
           ]
         },
@@ -796,7 +799,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['hostname', 'transportzoneuuid', 'l3gatewayserviceuuid', 'l2gatewayserviceuuid', 'action']
+              columns: ['hostname', 'transportzoneuuid', 'l3gatewayserviceuuid', 'l2gatewayserviceuuid', 'actions']
             }
           ]
         },
@@ -855,7 +858,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['name', 'url', 'username', 'action']
+              columns: ['name', 'url', 'username', 'actions']
             }
           ]
         },
@@ -871,7 +874,7 @@
                   value: (record) => { return record.id }
                 }
               },
-              columns: ['account', 'domain', 'enabled', 'project', 'action']
+              columns: ['account', 'domain', 'enabled', 'project', 'actions']
             }
           ]
         },
@@ -930,7 +933,7 @@
                   value: (record) => { return record.physicalnetworkid }
                 }
               },
-              columns: ['ipaddress', 'fwdevicestate', 'type', 'action']
+              columns: ['ipaddress', 'fwdevicestate', 'type', 'actions']
             }
           ]
         },
@@ -1364,5 +1367,15 @@
       }
     }
   }
+
+  &-tab {
+    justify-content: end;
+  }
+
+  &-tab-btn {
+    span {
+      display: flex;
+    }
+  }
 }
 </style>
diff --git a/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue b/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
index fa1173a..7fd7f03 100644
--- a/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
+++ b/ui/src/views/infra/network/providers/AddF5LoadBalancer.vue
@@ -61,11 +61,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option
                 v-for="opt in networkDeviceType"
-                :key="opt.id">{{ $t(opt.description) }}</a-select-option>
+                :key="opt.id"
+                :label="$t(opt.description)">{{ $t(opt.description) }}</a-select-option>
             </a-select>
           </a-form-item>
         </a-col>
diff --git a/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue b/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
index a89990c..e3a051d 100644
--- a/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
+++ b/ui/src/views/infra/network/providers/AddNetscalerLoadBalancer.vue
@@ -61,11 +61,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option
                 v-for="opt in networkDeviceType"
-                :key="opt.id">{{ $t(opt.description) }}</a-select-option>
+                :key="opt.id"
+                :label="$t(opt.description)">{{ $t(opt.description) }}</a-select-option>
             </a-select>
           </a-form-item>
         </a-col>
diff --git a/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue b/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
index 545c32c..1dd9150 100644
--- a/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
+++ b/ui/src/views/infra/network/providers/AddPaloAltoFirewall.vue
@@ -61,11 +61,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option
                 v-for="opt in networkDeviceType"
-                :key="opt.id">{{ $t(opt.description) }}</a-select-option>
+                :key="opt.id"
+                :label="$t(opt.description)">{{ $t(opt.description) }}</a-select-option>
             </a-select>
           </a-form-item>
         </a-col>
diff --git a/ui/src/views/infra/network/providers/ProviderItem.vue b/ui/src/views/infra/network/providers/ProviderItem.vue
index 69d0d1b..125455b 100644
--- a/ui/src/views/infra/network/providers/ProviderItem.vue
+++ b/ui/src/views/infra/network/providers/ProviderItem.vue
@@ -154,25 +154,25 @@
       params.pageSize = this.pageSize
 
       let length = args.columns.length
-      if (args.columns.includes('action')) {
+      if (args.columns.includes('actions')) {
         length--
       }
       const columns = args.columns.map(col => {
-        if (col === 'action') {
+        if (col === 'actions') {
           return {
+            key: col,
             title: this.$t('label.' + col),
             dataIndex: col,
             width: 80,
-            fixed: 'right',
-            slots: { customRender: col }
+            fixed: 'right'
           }
         }
         const width = 100 / (length) + '%'
         return {
+          key: col,
           title: this.$t('label.' + col),
           width: width,
-          dataIndex: col,
-          slots: { customRender: col }
+          dataIndex: col
         }
       })
 
diff --git a/ui/src/views/infra/network/providers/ProviderListView.vue b/ui/src/views/infra/network/providers/ProviderListView.vue
index baa6517..6c107e6 100644
--- a/ui/src/views/infra/network/providers/ProviderListView.vue
+++ b/ui/src/views/infra/network/providers/ProviderListView.vue
@@ -28,69 +28,73 @@
       :rowKey="record => record.id || record.name || record.nvpdeviceid || record.resourceid"
       :pagination="false"
       :scroll="scrollable">
-      <template #name="{text, record}">
-        <span v-if="record.role==='VIRTUAL_ROUTER'">
-          <router-link :to="{ path: '/router' + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
-          <label v-else>{{ text }}</label>
-        </span>
-        <span v-else>{{ text }}</span>
-      </template>
-      <template #hostname="{text, record}">
-        <span v-if="record.role==='VIRTUAL_ROUTER'">
-          <router-link :to="{ path: '/host' + '/' + record.hostid }" v-if="record.hostid">{{ text }}</router-link>
-          <label v-else>{{ text }}</label>
-        </span>
-        <span v-else>{{ text }}</span>
-      </template>
-      <template #zonename="{text, record}">
-        <span v-if="record.role==='VIRTUAL_ROUTER'">
-          <router-link :to="{ path: '/zone' + '/' + record.zoneid }" v-if="record.zoneid">{{ text }}</router-link>
-          <label v-else>{{ text }}</label>
-        </span>
-        <span v-else>{{ text }}</span>
-      </template>
-      <template #action="{record}">
-        <a-tooltip placement="top">
-          <template #title>
-            <span v-if="resource.name==='BigSwitchBcf'">{{ $t('label.delete.bigswitchbcf') }}</span>
-            <span v-else-if="resource.name==='BrocadeVcs'">{{ $t('label.delete.brocadevcs') }}</span>
-            <span v-else-if="resource.name==='NiciraNvp'">{{ $t('label.delete.niciranvp') }}</span>
-            <span v-else-if="resource.name==='Netscaler'">{{ $t('label.delete.netscaler') }}</span>
-            <span v-else-if="resource.name==='Opendaylight'">{{ $t('label.delete.opendaylight.device') }}</span>
-            <span v-else-if="resource.name==='PaloAlto'">{{ $t('label.delete.pa') }}</span>
-            <span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoVnmcResources'">
-              {{ $t('label.delete.ciscovnmc.resource') }}
-            </span>
-            <span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoAsa1000vResources'">
-              {{ $t('label.delete.ciscoasa1000v') }}
-            </span>
-          </template>
-          <tooltip-button
-            v-if="resource.name==='Ovs'"
-            :tooltip="$t('label.configure')"
-            icon="setting-outlined"
-            size="small"
-            :loading="actionLoading"
-            @onClick="onConfigureOvs(record)"/>
-          <tooltip-button
-            v-else
-            :tooltip="$t('label.delete')"
-            type="primary"
-            :danger="true"
-            icon="close-outlined"
-            size="small"
-            :loading="actionLoading"
-            @onClick="onDelete(record)"/>
-        </a-tooltip>
-      </template>
-      <template #lbdevicestate="{text}">
-        <status :text="text ? text : ''" displayText />
-      </template>
-      <template #status="{text}">
-        <status :text="text ? text : ''" displayText />
-      </template>
-      <template #state="{text}">
-        <status :text="text ? text : ''" displayText />
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'name'">
+          <span v-if="record.role==='VIRTUAL_ROUTER'">
+            <router-link :to="{ path: '/router' + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
+            <label v-else>{{ text }}</label>
+          </span>
+          <span v-else>{{ text }}</span>
+        </template>
+        <template v-if="column.key === 'hostname'">
+          <span v-if="record.role==='VIRTUAL_ROUTER'">
+            <router-link :to="{ path: '/host' + '/' + record.hostid }" v-if="record.hostid">{{ text }}</router-link>
+            <label v-else>{{ text }}</label>
+          </span>
+          <span v-else>{{ text }}</span>
+        </template>
+        <template v-if="column.key === 'zonename'">
+          <span v-if="record.role==='VIRTUAL_ROUTER'">
+            <router-link :to="{ path: '/zone' + '/' + record.zoneid }" v-if="record.zoneid">{{ text }}</router-link>
+            <label v-else>{{ text }}</label>
+          </span>
+          <span v-else>{{ text }}</span>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <a-tooltip placement="top">
+            <template #title>
+              <span v-if="resource.name==='BigSwitchBcf'">{{ $t('label.delete.bigswitchbcf') }}</span>
+              <span v-else-if="resource.name==='BrocadeVcs'">{{ $t('label.delete.brocadevcs') }}</span>
+              <span v-else-if="resource.name==='NiciraNvp'">{{ $t('label.delete.niciranvp') }}</span>
+              <span v-else-if="resource.name==='F5BigIp'">{{ $t('label.delete.f5') }}</span>
+              <span v-else-if="resource.name==='JuniperSRX'">{{ $t('label.delete.srx') }}</span>
+              <span v-else-if="resource.name==='Netscaler'">{{ $t('label.delete.netscaler') }}</span>
+              <span v-else-if="resource.name==='Opendaylight'">{{ $t('label.delete.opendaylight.device') }}</span>
+              <span v-else-if="resource.name==='PaloAlto'">{{ $t('label.delete.pa') }}</span>
+              <span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoVnmcResources'">
+                {{ $t('label.delete.ciscovnmc.resource') }}
+              </span>
+              <span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoAsa1000vResources'">
+                {{ $t('label.delete.ciscoasa1000v') }}
+              </span>
+            </template>
+            <tooltip-button
+              v-if="resource.name==='Ovs'"
+              :tooltip="$t('label.configure')"
+              icon="setting-outlined"
+              size="small"
+              :loading="actionLoading"
+              @onClick="onConfigureOvs(record)"/>
+            <tooltip-button
+              v-else
+              :tooltip="$t('label.delete')"
+              type="primary"
+              :danger="true"
+              icon="close-outlined"
+              size="small"
+              :loading="actionLoading"
+              @onClick="onDelete(record)"/>
+          </a-tooltip>
+        </template>
+        <template v-if="column.key === 'lbdevicestate'">
+          <status :text="text ? text : ''" displayText />
+        </template>
+        <template v-if="column.key === 'status'">
+          <status :text="text ? text : ''" displayText />
+        </template>
+        <template v-if="column.key === 'state'">
+          <status :text="text ? text : ''" displayText />
+        </template>
       </template>
     </a-table>
     <a-pagination
diff --git a/ui/src/views/infra/routers/RouterHealthCheck.vue b/ui/src/views/infra/routers/RouterHealthCheck.vue
index abadc12..a6f54f7 100644
--- a/ui/src/views/infra/routers/RouterHealthCheck.vue
+++ b/ui/src/views/infra/routers/RouterHealthCheck.vue
@@ -33,8 +33,10 @@
         :pagination="false"
         :rowKey="record => record.checkname"
         size="large">
-        <template #status="{record}">
-          <status class="status" :text="record.success === true ? 'True' : 'False'" displayText />
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'status'">
+            <status class="status" :text="record.success === true ? 'True' : 'False'" displayText />
+          </template>
         </template>
       </a-table>
 
@@ -110,8 +112,8 @@
           dataIndex: 'checktype'
         },
         {
-          title: this.$t('label.router.health.check.success'),
-          slots: { customRender: 'status' }
+          key: 'status',
+          title: this.$t('label.router.health.check.success')
         },
         {
           title: this.$t('label.router.health.check.last.updated'),
diff --git a/ui/src/views/infra/zone/IpAddressRangeForm.vue b/ui/src/views/infra/zone/IpAddressRangeForm.vue
index b851ad9..2233295 100644
--- a/ui/src/views/infra/zone/IpAddressRangeForm.vue
+++ b/ui/src/views/infra/zone/IpAddressRangeForm.vue
@@ -30,13 +30,15 @@
         :columns="columns"
         :pagination="false"
         style="margin-bottom: 24px; width: 100%" >
-        <template #actions="{ record }">
-          <tooltip-button
-            :tooltip="$t('label.delete')"
-            type="primary"
-            :danger="true"
-            icon="delete-outlined"
-            @onClick="onDelete(record.key)" />
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'actions'">
+            <tooltip-button
+              :tooltip="$t('label.delete')"
+              type="primary"
+              :danger="true"
+              icon="delete-outlined"
+              @onClick="onDelete(record.key)" />
+          </template>
         </template>
         <template #footer>
           <a-form
@@ -193,9 +195,9 @@
           width: 140
         },
         {
+          key: 'actions',
           title: '',
           dataIndex: 'actions',
-          slots: { customRender: 'actions' },
           width: 70
         }
       ],
diff --git a/ui/src/views/infra/zone/StaticInputsForm.vue b/ui/src/views/infra/zone/StaticInputsForm.vue
index e917ad9..77f509a 100644
--- a/ui/src/views/infra/zone/StaticInputsForm.vue
+++ b/ui/src/views/infra/zone/StaticInputsForm.vue
@@ -46,12 +46,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="option in field.options"
               :key="option.id"
               :value="option.id"
+              :label="option.name || option.description"
             >
               {{ option.name || option.description }}
             </a-select-option>
diff --git a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
index 97871d3..e424ddc 100644
--- a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
@@ -29,118 +29,120 @@
       :columns="columns"
       :pagination="false"
       style="margin-bottom: 24px; width: 100%">
-      <template #name="{ text, record, index }">
-        <a-input
-          :disabled="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
-          :value="text"
-          @change="e => onCellChange(record.key, 'name', e.target.value)"
-          v-focus="true">
-          <template #suffix>
-            <a-tooltip
-              v-if="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
-              :title="$t('message.no.support.tungsten.fabric')">
-              <warning-outlined style="color: #f5222d" />
-            </a-tooltip>
-          </template>
-        </a-input>
-      </template>
-      <template #isolationMethod="{ text, record, index }">
-        <a-select
-          :disabled="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
-          style="width: 100%"
-          :defaultValue="text"
-          @change="value => onCellChange(record.key, 'isolationMethod', value)"
-          showSearch
-          optionFilterProp="label"
-          :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-          }" >
-          <a-select-option value="VLAN"> VLAN </a-select-option>
-          <a-select-option value="VXLAN"> VXLAN </a-select-option>
-          <a-select-option value="GRE"> GRE </a-select-option>
-          <a-select-option value="STT"> STT </a-select-option>
-          <a-select-option value="BCF_SEGMENT"> BCF_SEGMENT </a-select-option>
-          <a-select-option value="ODL"> ODL </a-select-option>
-          <a-select-option value="L3VPN"> L3VPN </a-select-option>
-          <a-select-option value="VSP"> VSP </a-select-option>
-          <a-select-option value="VCS"> VCS </a-select-option>
-          <a-select-option value="TF"> TF </a-select-option>
+      <template #bodyCell="{ column, text, record, index }">
+        <template v-if="column.key === 'name'">
+          <a-input
+            :disabled="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
+            :value="text"
+            @change="e => onCellChange(record.key, 'name', e.target.value)"
+            v-focus="true">
+            <template #suffix>
+              <a-tooltip
+                v-if="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
+                :title="$t('message.no.support.tungsten.fabric')">
+                <warning-outlined style="color: #f5222d" />
+              </a-tooltip>
+            </template>
+          </a-input>
+        </template>
+        <template v-if="column.key === 'isolationMethod'">
+          <a-select
+            :disabled="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
+            style="width: 100%"
+            :defaultValue="text"
+            @change="value => onCellChange(record.key, 'isolationMethod', value)"
+            showSearch
+            optionFilterProp="value"
+            :filterOption="(input, option) => {
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }" >
+            <a-select-option value="VLAN"> VLAN </a-select-option>
+            <a-select-option value="VXLAN"> VXLAN </a-select-option>
+            <a-select-option value="GRE"> GRE </a-select-option>
+            <a-select-option value="STT"> STT </a-select-option>
+            <a-select-option value="BCF_SEGMENT"> BCF_SEGMENT </a-select-option>
+            <a-select-option value="ODL"> ODL </a-select-option>
+            <a-select-option value="L3VPN"> L3VPN </a-select-option>
+            <a-select-option value="VSP"> VSP </a-select-option>
+            <a-select-option value="VCS"> VCS </a-select-option>
+            <a-select-option value="TF"> TF </a-select-option>
 
-          <template #suffixIcon>
-            <a-tooltip
-              v-if="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
-              :title="$t('message.no.support.tungsten.fabric')">
-              <warning-outlined style="color: #f5222d" />
-            </a-tooltip>
-          </template>
-        </a-select>
-      </template>
-      <template #traffics="{ record, index }">
-        <div v-for="traffic in record.traffics" :key="traffic.type">
-          <a-tag
-            :color="trafficColors[traffic.type]"
-            style="margin:2px"
-          >
-            {{ traffic.type.toUpperCase() }}
-            <edit-outlined class="traffic-type-action" @click="editTraffic(record.key, traffic, $event)"/>
-            <delete-outlined class="traffic-type-action" @click="deleteTraffic(record.key, traffic, $event)"/>
-          </a-tag>
-        </div>
-        <div v-if="isShowAddTraffic(record.traffics, index)">
-          <div class="traffic-select-item" v-if="addingTrafficForKey === record.key">
-            <a-select
-              :defaultValue="trafficLabelSelected"
-              @change="val => { trafficLabelSelected = val }"
-              style="min-width: 120px;"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }" >
-              <a-select-option
-                v-for="(traffic, idx) in availableTrafficToAdd"
-                :value="traffic"
-                :key="idx"
-                :disabled="isDisabledTraffic(record.traffics, traffic)"
-              >
-                {{ traffic.toUpperCase() }}
-              </a-select-option>
-            </a-select>
-            <tooltip-button
-              :tooltip="$t('label.add')"
-              buttonClass="icon-button"
-              icon="plus-outlined"
-              size="small"
-              @onClick="trafficAdded" />
-            <tooltip-button
-              :tooltip="$t('label.cancel')"
-              buttonClass="icon-button"
-              type="primary"
-              :danger="true"
-              icon="close-outlined"
-              size="small"
-              @onClick="() => { addingTrafficForKey = null }" />
+            <template #suffixIcon>
+              <a-tooltip
+                v-if="tungstenNetworkIndex > -1 && tungstenNetworkIndex !== index"
+                :title="$t('message.no.support.tungsten.fabric')">
+                <warning-outlined style="color: #f5222d" />
+              </a-tooltip>
+            </template>
+          </a-select>
+        </template>
+        <template v-if="column.key === 'traffics'">
+          <div v-for="traffic in record.traffics" :key="traffic.type">
+            <a-tag
+              :color="trafficColors[traffic.type]"
+              style="margin:2px"
+            >
+              {{ traffic.type.toUpperCase() }}
+              <edit-outlined class="traffic-type-action" @click="editTraffic(record.key, traffic, $event)"/>
+              <delete-outlined class="traffic-type-action" @click="deleteTraffic(record.key, traffic, $event)"/>
+            </a-tag>
           </div>
-          <a-tag
-            key="addingTraffic"
-            style="margin:2px;"
-            v-else
-          >
-            <a @click="addingTraffic(record.key, record.traffics)">
-              <plus-outlined />
-              {{ $t('label.add.traffic') }}
-            </a>
-          </a-tag>
-        </div>
-      </template>
-      <template #actions="{ record, index }">
-        <tooltip-button
-          :tooltip="$t('label.delete')"
-          v-if="tungstenNetworkIndex === -1 ? index > 0 : tungstenNetworkIndex !== index"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="onDelete(record)" />
+          <div v-if="isShowAddTraffic(record.traffics, index)">
+            <div class="traffic-select-item" v-if="addingTrafficForKey === record.key">
+              <a-select
+                :defaultValue="trafficLabelSelected"
+                @change="val => { trafficLabelSelected = val }"
+                style="min-width: 120px;"
+                showSearch
+                optionFilterProp="value"
+                :filterOption="(input, option) => {
+                  return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }" >
+                <a-select-option
+                  v-for="(traffic, index) in availableTrafficToAdd"
+                  :value="traffic"
+                  :key="index"
+                  :disabled="isDisabledTraffic(record.traffics, traffic)"
+                >
+                  {{ traffic.toUpperCase() }}
+                </a-select-option>
+              </a-select>
+              <tooltip-button
+                :tooltip="$t('label.add')"
+                buttonClass="icon-button"
+                icon="plus-outlined"
+                size="small"
+                @onClick="trafficAdded" />
+              <tooltip-button
+                :tooltip="$t('label.cancel')"
+                buttonClass="icon-button"
+                type="primary"
+                :danger="true"
+                icon="close-outlined"
+                size="small"
+                @onClick="() => { addingTrafficForKey = null }" />
+            </div>
+            <a-tag
+              key="addingTraffic"
+              style="margin:2px;"
+              v-else
+            >
+              <a @click="addingTraffic(record.key, record.traffics)">
+                <plus-outlined />
+                {{ $t('label.add.traffic') }}
+              </a>
+            </a-tag>
+          </div>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.delete')"
+            v-if="tungstenNetworkIndex === -1 ? index > 0 : tungstenNetworkIndex !== index"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="onDelete(record)" />
+        </template>
       </template>
       <template #footer v-if="isAdvancedZone">
         <a-button
@@ -219,11 +221,17 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option value="nexusdvs">{{ $t('label.vswitch.type.nexusdvs') }}</a-select-option>
-              <a-select-option value="vmwaresvs">{{ $t('label.vswitch.type.vmwaresvs') }}</a-select-option>
-              <a-select-option value="vmwaredvs">{{ $t('label.vswitch.type.vmwaredvs') }}</a-select-option>
+              <a-select-option
+                value="nexusdvs"
+                :label="$t('label.vswitch.type.nexusdvs')">{{ $t('label.vswitch.type.nexusdvs') }}</a-select-option>
+              <a-select-option
+                value="vmwaresvs"
+                :label="$t('label.vswitch.type.vmwaresvs')">{{ $t('label.vswitch.type.vmwaresvs') }}</a-select-option>
+              <a-select-option
+                value="vmwaredvs"
+                :label="$t('label.vswitch.type.vmwaredvs')">{{ $t('label.vswitch.type.vmwaredvs') }}</a-select-option>
             </a-select>
           </a-form-item>
         </span>
@@ -290,29 +298,28 @@
     columns () {
       const columns = []
       columns.push({
+        key: 'name',
         title: this.$t('label.network.name'),
         dataIndex: 'name',
-        width: 175,
-        slots: { customRender: 'name' }
+        width: 175
       })
       columns.push({
+        key: 'isolationMethod',
         title: this.$t('label.isolation.method'),
         dataIndex: 'isolationMethod',
-        width: 150,
-        slots: { customRender: 'isolationMethod' }
+        width: 150
       })
       columns.push({
-        title: this.$t('label.traffic.types'),
         key: 'traffics',
+        title: this.$t('label.traffic.types'),
         dataIndex: 'traffics',
-        width: 250,
-        slots: { customRender: 'traffics' }
+        width: 250
       })
       if (this.isAdvancedZone) {
         columns.push({
+          key: 'actions',
           title: '',
           dataIndex: 'actions',
-          slots: { customRender: 'actions' },
           width: 70
         })
       }
diff --git a/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue b/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
index 42e30a7..8aea3ff 100644
--- a/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
@@ -121,9 +121,9 @@
           :loading="hypervisors === null"
           :disabled="this.isEdgeZone"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option v-for="hypervisor in hypervisors" :key="hypervisor.name">
             {{ hypervisor.name }}
@@ -145,11 +145,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="networkOffering in availableNetworkOfferings"
-              :key="networkOffering.id">
+              :key="networkOffering.id"
+              :label="networkOffering.displaytext || networkOffering.name || networkOffering.description">
               {{ networkOffering.displaytext || networkOffering.name || networkOffering.description }}
             </a-select-option>
           </a-select>
@@ -193,9 +194,9 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option v-for="dom in domains" :key="dom.id">
+          <a-select-option v-for="dom in domains" :key="dom.id" :label="dom.path">
             {{ dom.path }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/network/AclListRulesTab.vue b/ui/src/views/network/AclListRulesTab.vue
index 36f10e9..6675500 100644
--- a/ui/src/views/network/AclListRulesTab.vue
+++ b/ui/src/views/network/AclListRulesTab.vue
@@ -40,7 +40,6 @@
         handle=".drag-handle"
         animation="200"
         ghostClass="drag-ghost"
-        tag="transition-group"
         :component-data="{type: 'transition'}"
         item-key="id">
         <template #item="{element}">
@@ -182,10 +181,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="allow">{{ $t('label.allow') }}</a-select-option>
-            <a-select-option value="deny">{{ $t('label.deny') }}</a-select-option>
+            <a-select-option value="allow" :label="$t('label.allow')">{{ $t('label.allow') }}</a-select-option>
+            <a-select-option value="deny" :label="$t('label.deny')">{{ $t('label.deny') }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item :label="$t('label.protocol')" ref="protocol" name="protocol">
@@ -194,13 +193,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp">{{ capitalise($t('label.tcp')) }}</a-select-option>
-            <a-select-option value="udp">{{ capitalise($t('label.udp')) }}</a-select-option>
-            <a-select-option value="icmp">{{ capitalise($t('label.icmp')) }}</a-select-option>
-            <a-select-option value="all">{{ $t('label.all') }}</a-select-option>
-            <a-select-option value="protocolnumber">{{ $t('label.protocol.number') }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ capitalise($t('label.tcp')) }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ capitalise($t('label.udp')) }}</a-select-option>
+            <a-select-option value="icmp" :label="$t('label.icmp')">{{ capitalise($t('label.icmp')) }}</a-select-option>
+            <a-select-option value="all" :label="$t('label.all')">{{ $t('label.all') }}</a-select-option>
+            <a-select-option value="protocolnumber" :label="$t('label.protocol.number')">{{ $t('label.protocol.number') }}</a-select-option>
           </a-select>
         </a-form-item>
 
@@ -236,10 +235,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="ingress">{{ $t('label.ingress') }}</a-select-option>
-            <a-select-option value="egress">{{ $t('label.egress') }}</a-select-option>
+            <a-select-option value="ingress" :label="$t('label.ingress')">{{ $t('label.ingress') }}</a-select-option>
+            <a-select-option value="egress" :label="$t('label.egress')">{{ $t('label.egress') }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item :label="$t('label.description')" ref="reason" name="reason">
diff --git a/ui/src/views/network/CreateIsolatedNetworkForm.vue b/ui/src/views/network/CreateIsolatedNetworkForm.vue
index 21ab5e3..046510d 100644
--- a/ui/src/views/network/CreateIsolatedNetworkForm.vue
+++ b/ui/src/views/network/CreateIsolatedNetworkForm.vue
@@ -75,12 +75,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="domainLoading"
               :placeholder="apiParams.domainid.description"
               @change="val => { handleDomainChange(domains[val]) }">
-              <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex" :label="opt.path || opt.name || opt.description">
                 {{ opt.path || opt.name || opt.description }}
               </a-select-option>
             </a-select>
@@ -124,12 +124,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="networkOfferingLoading"
               :placeholder="apiParams.networkofferingid.description"
               @change="val => { handleNetworkOfferingChange(networkOfferings[val]) }">
-              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex" :label="opt.displaytext || opt.name || opt.description">
                 {{ opt.displaytext || opt.name || opt.description }}
               </a-select-option>
             </a-select>
@@ -189,12 +189,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="vpcLoading"
               :placeholder="apiParams.vpcid.description"
               @change="val => { selectedVpc = vpcs[val] }">
-              <a-select-option v-for="(opt, optIndex) in vpcs" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in vpcs" :key="optIndex" :label="opt.name || opt.description">
                 {{ opt.name || opt.description }}
               </a-select-option>
             </a-select>
@@ -291,6 +291,36 @@
               </a-col>
             </a-row>
           </div>
+          <a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
+            <template #label>
+              <tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
+            </template>
+            <a-input
+              v-model:value="form.sourcenatipaddress"
+              :placeholder="apiParams.sourcenatipaddress.description"/>
+          </a-form-item>
+          <a-form-item
+            ref="networkdomain"
+            name="networkdomain"
+            v-if="!isObjectEmpty(selectedNetworkOffering) && !selectedNetworkOffering.forvpc">
+            <template #label>
+              <tooltip-label :title="$t('label.networkdomain')" :tooltip="apiParams.networkdomain.description"/>
+            </template>
+            <a-input
+             v-model:value="form.networkdomain"
+              :placeholder="apiParams.networkdomain.description"/>
+          </a-form-item>
+          <a-form-item
+            ref="account"
+            name="account"
+            v-if="accountVisible">
+            <template #label>
+              <tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
+            </template>
+            <a-input
+             v-model:value="form.account"
+              :placeholder="apiParams.account.description"/>
+          </a-form-item>
           <div :span="24" class="action-button">
             <a-button
               :loading="actionLoading"
@@ -397,6 +427,14 @@
         return dnsServices && dnsServices.length === 1
       }
       return false
+    },
+    selectedNetworkOfferingSupportsSourceNat () {
+      if (this.selectedNetworkOffering) {
+        const services = this.selectedNetworkOffering?.service || []
+        const sourcenatService = services.filter(service => service.name === 'SourceNat')
+        return sourcenatService && sourcenatService.length === 1
+      }
+      return false
     }
   },
   methods: {
@@ -596,7 +634,7 @@
           displayText: values.displaytext,
           networkOfferingId: this.selectedNetworkOffering.id
         }
-        var usefulFields = ['gateway', 'netmask', 'startip', 'endip', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'externalid', 'vlan', 'networkdomain']
+        var usefulFields = ['gateway', 'netmask', 'startip', 'startipv4', 'endip', 'endipv4', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'sourcenatipaddress', 'externalid', 'vpcid', 'vlan', 'networkdomain']
         for (var field of usefulFields) {
           if (this.isValidTextValueForKey(values, field)) {
             params[field] = values[field]
diff --git a/ui/src/views/network/CreateL2NetworkForm.vue b/ui/src/views/network/CreateL2NetworkForm.vue
index b5f5763..76695eb 100644
--- a/ui/src/views/network/CreateL2NetworkForm.vue
+++ b/ui/src/views/network/CreateL2NetworkForm.vue
@@ -117,12 +117,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="networkOfferingLoading"
               :placeholder="apiParams.networkofferingid.description"
               @change="val => { handleNetworkOfferingChange(networkOfferings[val]) }">
-              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex" :label="opt.displaytext || opt.name || opt.description">
                 {{ opt.displaytext || opt.name || opt.description }}
               </a-select-option>
             </a-select>
diff --git a/ui/src/views/network/CreateNetworkPermission.vue b/ui/src/views/network/CreateNetworkPermission.vue
index 8872346..037e91e 100644
--- a/ui/src/views/network/CreateNetworkPermission.vue
+++ b/ui/src/views/network/CreateNetworkPermission.vue
@@ -35,9 +35,9 @@
               :loading="accountLoading"
               :placeholder="apiParams.accountids.description"
               showSearch
-              optionFilterProp="children"
+              optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option v-for="(opt, optIndex) in accounts" :key="optIndex" :label="opt.name || opt.description">
                 <span>
@@ -58,9 +58,9 @@
               :loading="projectLoading"
               :placeholder="apiParams.projectids.description"
               showSearch
-              optionFilterProp="children"
+              optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option v-for="(opt, optIndex) in projects" :key="optIndex" :label="opt.name || opt.description">
                 <span>
@@ -131,6 +131,7 @@
   created () {
     this.formRef = ref()
     this.form = reactive({})
+    this.rules = reactive({})
     this.apiParams = this.$getApiParams('createNetworkPermissions')
     this.fetchData()
   },
diff --git a/ui/src/views/network/CreateSharedNetworkForm.vue b/ui/src/views/network/CreateSharedNetworkForm.vue
index 380ced7..93bf693 100644
--- a/ui/src/views/network/CreateSharedNetworkForm.vue
+++ b/ui/src/views/network/CreateSharedNetworkForm.vue
@@ -80,7 +80,7 @@
               :loading="formPhysicalNetworkLoading"
               :placeholder="apiParams.physicalnetworkid.description"
               @change="val => { handlePhysicalNetworkChange(formPhysicalNetworks[val]) }">
-              <a-select-option v-for="(opt, optIndex) in formPhysicalNetworks" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in formPhysicalNetworks" :key="optIndex" :label="opt.name || opt.description">
                 {{ opt.name || opt.description }}
               </a-select-option>
             </a-select>
@@ -164,7 +164,7 @@
               :loading="domainLoading"
               :placeholder="apiParams.domainid.description"
               @change="val => { handleDomainChange(domains[val]) }">
-              <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex" :label="opt.path || opt.name || opt.description">
                 <span>
                   <resource-icon v-if="opt && opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
                   <block-outlined v-else style="margin-right: 5px" />
@@ -193,7 +193,7 @@
               :loading="accountLoading"
               :placeholder="apiParams.account.description"
               @change="val => { handleAccountChange(accounts[val]) }">
-              <a-select-option v-for="(opt, optIndex) in accounts" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in accounts" :key="optIndex" :label="opt.name || opt.description">
                 <span>
                   <resource-icon v-if="opt && opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
                   <user-outlined v-else style="margin-right: 5px" />
@@ -216,7 +216,7 @@
               :loading="projectLoading"
               :placeholder="apiParams.projectid.description"
               @change="val => { handleProjectChange(projects[val]) }">
-              <a-select-option v-for="(opt, optIndex) in projects" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in projects" :key="optIndex" :label="opt.name || opt.description">
                 {{ opt.name || opt.description }}
               </a-select-option>
             </a-select>
@@ -235,7 +235,7 @@
               :loading="networkOfferingLoading"
               :placeholder="apiParams.networkofferingid.description"
               @change="val => { handleNetworkOfferingChange(networkOfferings[val]) }">
-              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex">
+              <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex" :label="opt.displaytext || opt.name || opt.description">
                 {{ opt.displaytext || opt.name || opt.description }}
               </a-select-option>
             </a-select>
diff --git a/ui/src/views/network/CreateVlanIpRange.vue b/ui/src/views/network/CreateVlanIpRange.vue
index 96bbc9c..f819c2e 100644
--- a/ui/src/views/network/CreateVlanIpRange.vue
+++ b/ui/src/views/network/CreateVlanIpRange.vue
@@ -35,9 +35,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id">{{ pod.name }}</a-select-option>
+              <a-select-option v-for="pod in pods" :key="pod.id" :value="pod.id" :label="pod.name">{{ pod.name }}</a-select-option>
             </a-select>
           </a-form-item>
           <a-form-item name="gateway" ref="gateway">
diff --git a/ui/src/views/network/CreateVpc.vue b/ui/src/views/network/CreateVpc.vue
index fb2d110..363dd44 100644
--- a/ui/src/views/network/CreateVpc.vue
+++ b/ui/src/views/network/CreateVpc.vue
@@ -88,10 +88,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             @change="handleVpcOfferingChange" >
-            <a-select-option :value="offering.id" v-for="offering in vpcOfferings" :key="offering.id">
+            <a-select-option :value="offering.id" v-for="offering in vpcOfferings" :key="offering.id" :label="offering.name">
               {{ offering.name }}
             </a-select-option>
           </a-select>
@@ -155,6 +155,14 @@
             </a-form-item>
           </a-col>
         </a-row>
+        <a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
+          <template #label>
+            <tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
+          </template>
+          <a-input
+            v-model:value="form.sourcenatipaddress"
+            :placeholder="apiParams.sourcenatipaddress.description"/>
+        </a-form-item>
         <a-form-item name="start" ref="start">
           <template #label>
             <tooltip-label :title="$t('label.start')" :tooltip="apiParams.start.description"/>
@@ -212,6 +220,14 @@
         return dnsServices && dnsServices.length === 1
       }
       return false
+    },
+    selectedNetworkOfferingSupportsSourceNat () {
+      if (this.selectedVpcOffering) {
+        const services = this.selectedVpcOffering?.service || []
+        const sourcenatService = services.filter(service => service.name === 'SourceNat')
+        return sourcenatService && sourcenatService.length === 1
+      }
+      return false
     }
   },
   methods: {
@@ -222,7 +238,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.required.input') }],
-        displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
         zoneid: [{ required: true, message: this.$t('label.required') }],
         cidr: [{ required: true, message: this.$t('message.error.required.input') }],
         vpcofferingid: [{ required: true, message: this.$t('label.required') }]
@@ -275,6 +290,10 @@
         this.selectedVpcOffering = this.vpcOfferings[0] || {}
       }).finally(() => {
         this.loadingOffering = false
+        if (this.vpcOfferings.length > 0) {
+          this.form.vpcofferingid = 0
+          this.handleVpcOfferingChange(this.vpcOfferings[0].id)
+        }
       })
     },
     handleVpcOfferingChange (value) {
@@ -285,6 +304,7 @@
       for (var offering of this.vpcOfferings) {
         if (offering.id === value) {
           this.selectedVpcOffering = offering
+          this.form.vpcofferingid = offering.id
           return
         }
       }
diff --git a/ui/src/views/network/CreateVpnCustomerGateway.vue b/ui/src/views/network/CreateVpnCustomerGateway.vue
index b890e29..00b9f3b 100644
--- a/ui/src/views/network/CreateVpnCustomerGateway.vue
+++ b/ui/src/views/network/CreateVpnCustomerGateway.vue
@@ -62,9 +62,9 @@
         <a-select
           v-model:value="form.ikeEncryption"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option :value="algo" v-for="(algo, idx) in encryptionAlgo" :key="idx">
             {{ algo }}
@@ -75,9 +75,9 @@
         <a-select
           v-model:value="form.ikeHash"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option :value="h" v-for="(h, idx) in hash" :key="idx">
             {{ h }}
@@ -91,9 +91,9 @@
         <a-select
           v-model:value="form.ikeversion"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option :value="vers" v-for="(vers, idx) in ikeVersions" :key="idx">
             {{ vers }}
@@ -106,9 +106,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option :value="DHGroups[group]" v-for="(group, idx) in Object.keys(DHGroups)" :key="idx">
+          <a-select-option
+            :value="DHGroups[group]"
+            v-for="(group, idx) in Object.keys(DHGroups)"
+            :key="idx"
+            :label="group + '(' + DHGroups[group] + ')'">
             <div v-if="group !== ''">
               {{ group+"("+DHGroups[group]+")" }}
             </div>
@@ -119,9 +123,9 @@
         <a-select
           v-model:value="form.espEncryption"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option :value="algo" v-for="(algo, idx) in encryptionAlgo" :key="idx">
             {{ algo }}
@@ -132,9 +136,9 @@
         <a-select
           v-model:value="form.espHash"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option :value="h" v-for="(h, idx) in hash" :key="idx">
             {{ h }}
@@ -147,9 +151,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option :value="DHGroups[group]" v-for="(group, idx) in Object.keys(DHGroups)" :key="idx">
+          <a-select-option
+            :value="DHGroups[group]"
+            v-for="(group, idx) in Object.keys(DHGroups)"
+            :key="idx"
+            :label="group === '' ? DHGroups[group] : group + '(' + DHGroups[group] + ')'">
             <div v-if="group === ''">
               {{ DHGroups[group] }}
             </div>
diff --git a/ui/src/views/network/EgressRulesTab.vue b/ui/src/views/network/EgressRulesTab.vue
index dcd8054..a0fb708 100644
--- a/ui/src/views/network/EgressRulesTab.vue
+++ b/ui/src/views/network/EgressRulesTab.vue
@@ -45,12 +45,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp">{{ capitalise($t('label.tcp'))  }}</a-select-option>
-            <a-select-option value="udp">{{ capitalise($t('label.udp')) }}</a-select-option>
-            <a-select-option value="icmp">{{ capitalise($t('label.icmp')) }}</a-select-option>
-            <a-select-option value="all">{{ $t('label.all') }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ capitalise($t('label.tcp'))  }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ capitalise($t('label.udp')) }}</a-select-option>
+            <a-select-option value="icmp" :label="$t('label.icmp')">{{ capitalise($t('label.icmp')) }}</a-select-option>
+            <a-select-option value="all" :label="$t('label.all')">{{ $t('label.all') }}</a-select-option>
           </a-select>
         </div>
         <div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
@@ -97,23 +97,25 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.id">
-      <template #protocol="{ record }">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #startport="{ record }">
-        {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
-      </template>
-      <template #endport="{ record }">
-        {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
-      </template>
-      <template #actions="{ record }">
-        <tooltip-button
-          :tooltip="$t('label.delete')"
-          :disabled="!('deleteEgressFirewallRule' in $store.getters.apis)"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="deleteRule(record)" />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'startport'">
+          {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
+        </template>
+        <template v-if="column.key === 'endport'">
+          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.delete')"
+            :disabled="!('deleteEgressFirewallRule' in $store.getters.apis)"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="deleteRule(record)" />
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -176,7 +178,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.delete.egress.firewall.rules'),
@@ -207,20 +209,20 @@
           dataIndex: 'destcidrlist'
         },
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
-          title: this.$t('label.icmptype.start.port'),
-          slots: { customRender: 'startport' }
+          key: 'startport',
+          title: this.$t('label.icmptype.start.port')
         },
         {
-          title: this.$t('label.icmpcode.end.port'),
-          slots: { customRender: 'endport' }
+          key: 'endport',
+          title: this.$t('label.icmpcode.end.port')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ]
     }
@@ -291,9 +293,9 @@
     deleteRules (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/network/EnableStaticNat.vue b/ui/src/views/network/EnableStaticNat.vue
index 8def80f..03bfc87 100644
--- a/ui/src/views/network/EnableStaticNat.vue
+++ b/ui/src/views/network/EnableStaticNat.vue
@@ -27,9 +27,9 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option v-for="network in networksList" :key="network.id" :value="network.id">
+          <a-select-option v-for="network in networksList" :key="network.id" :value="network.id" :label="network.name">
             {{ network.name }}
           </a-select-option>
         </a-select>
@@ -52,36 +52,39 @@
       :dataSource="vmsList"
       :pagination="false"
       :rowKey="record => record.id || record.account">
-      <template #name="{ record }">
-        <div>
-          {{ record.name }}
-        </div>
-        <a-select
-          v-if="nicsList.length && selectedVm && selectedVm === record.id"
-          class="nic-select"
-          :defaultValue="selectedNic.ipaddress"
-          showSearch
-          optionFilterProp="label"
-          :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-          }">
-          <a-select-option
-            @click="selectedNic = item"
-            v-for="item in nicsList"
-            :key="item.id">
-            {{ item.ipaddress }}
-          </a-select-option>
-        </a-select>
-      </template>
-      <template #state="{ text }">
-        <status :text="text ? text : ''" displayText />
-      </template>
-      <template #radio="{ text }">
-        <a-radio
-          class="list__radio"
-          :value="text"
-          :checked="selectedVm && selectedVm === text"
-          @change="fetchNics"></a-radio>
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'name'">
+          <div>
+            {{ record.name }}
+          </div>
+          <a-select
+            v-if="nicsList.length && selectedVm && selectedVm === record.id"
+            class="nic-select"
+            :defaultValue="selectedNic.ipaddress"
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }">
+            <a-select-option
+              @click="selectedNic = item"
+              v-for="item in nicsList"
+              :key="item.id"
+              :label="item.ipaddress">
+              {{ item.ipaddress }}
+            </a-select-option>
+          </a-select>
+        </template>
+        <template v-if="column.key === 'state'">
+          <status :text="text ? text : ''" displayText />
+        </template>
+        <template v-if="column.key === 'radio'">
+          <a-radio
+            class="list__radio"
+            :value="text"
+            :checked="selectedVm && selectedVm === text"
+            @change="fetchNics"></a-radio>
+        </template>
       </template>
     </a-table>
 
@@ -134,14 +137,14 @@
       selectedNic: null,
       columns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          slots: { customRender: 'name' },
           width: 200
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.displayname'),
@@ -156,9 +159,9 @@
           dataIndex: 'zonename'
         },
         {
+          key: 'radio',
           title: this.$t('label.select'),
           dataIndex: 'id',
-          slots: { customRender: 'radio' },
           width: 70
         }
       ],
diff --git a/ui/src/views/network/FirewallRules.vue b/ui/src/views/network/FirewallRules.vue
index 523a632..787f5c2 100644
--- a/ui/src/views/network/FirewallRules.vue
+++ b/ui/src/views/network/FirewallRules.vue
@@ -32,11 +32,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
-            <a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
-            <a-select-option value="icmp">{{ $t('label.icmp') }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
+            <a-select-option value="icmp" :label="$t('label.icmp')">{{ $t('label.icmp') }}</a-select-option>
           </a-select>
         </div>
         <div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
@@ -80,27 +80,29 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.id">
-      <template #protocol="{ record }">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #startport="{ record }">
-        {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
-      </template>
-      <template #endport="{ record }">
-        {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
-      </template>
-      <template #actions="{ record }">
-        <div class="actions">
-          <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record.id)" />
-          <tooltip-button
-            :tooltip="$t('label.delete')"
-            type="primary"
-            :danger="true"
-            icon="delete-outlined"
-            buttonClass="rule-action"
-            :disabled="!('deleteFirewallRule' in $store.getters.apis)"
-            @onClick="deleteRule(record)" />
-        </div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'startport'">
+          {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('label.all') }}
+        </template>
+        <template v-if="column.key === 'endport'">
+          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('label.all') }}
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record.id)" />
+            <tooltip-button
+              :tooltip="$t('label.delete')"
+              type="primary"
+              :danger="true"
+              icon="delete-outlined"
+              buttonClass="rule-action"
+              :disabled="!('deleteFirewallRule' in $store.getters.apis)"
+              @onClick="deleteRule(record)" />
+          </div>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -218,7 +220,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['State', 'Action'],
+      filterColumns: ['State', 'Actions'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.delete.firewall.rules'),
@@ -248,24 +250,24 @@
           dataIndex: 'cidrlist'
         },
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
-          title: `${this.$t('label.startport')}/${this.$t('label.icmptype')}`,
-          slots: { customRender: 'startport' }
+          key: 'startport',
+          title: `${this.$t('label.startport')}/${this.$t('label.icmptype')}`
         },
         {
-          title: `${this.$t('label.endport')}/${this.$t('label.icmpcode')}`,
-          slots: { customRender: 'endport' }
+          key: 'endport',
+          title: `${this.$t('label.endport')}/${this.$t('label.icmpcode')}`
         },
         {
           title: this.$t('label.state'),
           dataIndex: 'state'
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ]
     }
@@ -346,9 +348,9 @@
     deleteRules (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/network/GuestIpRanges.vue b/ui/src/views/network/GuestIpRanges.vue
index 14cd8a1..4c05162 100644
--- a/ui/src/views/network/GuestIpRanges.vue
+++ b/ui/src/views/network/GuestIpRanges.vue
@@ -36,19 +36,21 @@
         :rowKey="item => item.id"
         :pagination="false" >
 
-        <template #action="{ record }">
-          <a-popconfirm
-            :title="$t('message.confirm.remove.ip.range')"
-            @confirm="removeIpRange(record.id)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')" >
-            <tooltip-button
-              tooltipPlacement="bottom"
-              :tooltip="$t('label.action.delete.ip.range')"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined" />
-          </a-popconfirm>
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'actions'">
+            <a-popconfirm
+              :title="$t('message.confirm.remove.ip.range')"
+              @confirm="removeIpRange(record.id)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')" >
+              <tooltip-button
+                tooltipPlacement="bottom"
+                :tooltip="$t('label.action.delete.ip.range')"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined" />
+            </a-popconfirm>
+          </template>
         </template>
 
       </a-table>
@@ -140,8 +142,8 @@
           dataIndex: 'netmask'
         },
         {
-          title: '',
-          slots: { customRender: 'action' }
+          key: 'actions',
+          title: ''
         }
       ]
     }
diff --git a/ui/src/views/network/GuestVlanNetworksTab.vue b/ui/src/views/network/GuestVlanNetworksTab.vue
index 2258eed..657880b 100644
--- a/ui/src/views/network/GuestVlanNetworksTab.vue
+++ b/ui/src/views/network/GuestVlanNetworksTab.vue
@@ -25,13 +25,15 @@
     :pagination="false"
     :loading="fetchLoading"
   >
-    <template #name="{ text, record }">
-      <router-link v-if="record.issystem === false && !record.projectid" :to="{ path: '/guestnetwork/' + record.id }" >{{ text }}</router-link>
-      <router-link v-else-if="record.issystem === false && record.projectid" :to="{ path: '/guestnetwork/' + record.id, query: { projectid: record.projectid}}" >{{ text }}</router-link>
-      <span v-else>{{ text }}</span>
-    </template>
-    <template #state="{ record }">
-      <status :text="record.state" displayText></status>
+    <template #bodyCell="{ column, text, record }">
+      <template v-if="column.key === 'name'">
+        <router-link v-if="record.issystem === false && !record.projectid" :to="{ path: '/guestnetwork/' + record.id }" >{{ text }}</router-link>
+        <router-link v-else-if="record.issystem === false && record.projectid" :to="{ path: '/guestnetwork/' + record.id, query: { projectid: record.projectid}}" >{{ text }}</router-link>
+        <span v-else>{{ text }}</span>
+      </template>
+      <template v-if="column.key === 'state'">
+        <status :text="record.state" displayText></status>
+      </template>
     </template>
   </a-table>
 </template>
@@ -61,17 +63,17 @@
       networks: [],
       columns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
           title: this.$t('label.type'),
           dataIndex: 'type'
         },
         {
-          title: this.$t('label.state'),
-          slots: { customRender: 'state' }
+          key: 'state',
+          title: this.$t('label.state')
         },
         {
           title: this.$t('label.broadcasturi'),
diff --git a/ui/src/views/network/IngressEgressRuleConfigure.vue b/ui/src/views/network/IngressEgressRuleConfigure.vue
index c1e82a8..adf013c 100644
--- a/ui/src/views/network/IngressEgressRuleConfigure.vue
+++ b/ui/src/views/network/IngressEgressRuleConfigure.vue
@@ -38,13 +38,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp">{{ capitalise($t('label.tcp')) }}</a-select-option>
-            <a-select-option value="udp">{{ capitalise($t('label.udp')) }}</a-select-option>
-            <a-select-option value="icmp">{{ capitalise($t('label.icmp')) }}</a-select-option>
-            <a-select-option value="all">{{ capitalise($t('label.all')) }}</a-select-option>
-            <a-select-option value="protocolnumber">{{ capitalise($t('label.protocol.number')) }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ capitalise($t('label.tcp')) }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ capitalise($t('label.udp')) }}</a-select-option>
+            <a-select-option value="icmp" :label="$t('label.icmp')">{{ capitalise($t('label.icmp')) }}</a-select-option>
+            <a-select-option value="all" :label="$t('label.all')">{{ capitalise($t('label.all')) }}</a-select-option>
+            <a-select-option value="protocolnumber" :label="$t('label.protocol.number')">{{ capitalise($t('label.protocol.number')) }}</a-select-option>
           </a-select>
         </div>
         <div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
@@ -94,38 +94,40 @@
       :dataSource="rules"
       :pagination="{ pageSizeOptions: ['10', '20', '40', '80', '100', '200'], showSizeChanger: true}"
       :rowKey="record => record.ruleid">
-      <template #protocol="{ record }">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #account="{ record }">
-        <div v-if="record.account && record.securitygroupname">
-          {{ record.account }} - {{ record.securitygroupname }}
-        </div>
-      </template>
-      <template #startport="{text, record}">
-        <div v-if="!['tcp', 'udp', 'icmp'].includes(record.protocol)">{{ $t('label.all') }}</div>
-        <div v-else>{{ text }}</div>
-      </template>
-      <template #endport="{text, record}">
-        <div v-if="!['tcp', 'udp', 'icmp'].includes(record.protocol)">{{ $t('label.all') }}</div>
-        <div v-else>{{ text }}</div>
-      </template>
-      <template #actions="{ record }">
-        <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record)" />
-        <a-popconfirm
-          :title="$t('label.delete') + '?'"
-          @confirm="handleDeleteRule(record)"
-          :okText="$t('label.yes')"
-          :cancelText="$t('label.no')"
-        >
-          <tooltip-button
-            :disabled="!('revokeSecurityGroupIngress' in $store.getters.apis) && !('revokeSecurityGroupEgress' in $store.getters.apis)"
-            :tooltip="$t('label.delete')"
-            type="primary"
-            :danger="true"
-            icon="delete-outlined"
-            buttonClass="rule-action" />
-        </a-popconfirm>
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'account'">
+          <div v-if="record.account && record.securitygroupname">
+            {{ record.account }} - {{ record.securitygroupname }}
+          </div>
+        </template>
+        <template v-if="column.key === 'startport'">
+          <div v-if="!['tcp', 'udp', 'icmp'].includes(record.protocol)">{{ $t('label.all') }}</div>
+          <div v-else>{{ text }}</div>
+        </template>
+        <template v-if="column.key === 'endport'">
+          <div v-if="!['tcp', 'udp', 'icmp'].includes(record.protocol)">{{ $t('label.all') }}</div>
+          <div v-else>{{ text }}</div>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button :tooltip="$t('label.edit.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record)" />
+          <a-popconfirm
+            :title="$t('label.delete') + '?'"
+            @confirm="handleDeleteRule(record)"
+            :okText="$t('label.yes')"
+            :cancelText="$t('label.no')"
+          >
+            <tooltip-button
+              :disabled="!('revokeSecurityGroupIngress' in $store.getters.apis) && !('revokeSecurityGroupEgress' in $store.getters.apis)"
+              :tooltip="$t('label.delete')"
+              type="primary"
+              :danger="true"
+              icon="delete-outlined"
+              buttonClass="rule-action" />
+          </a-popconfirm>
+        </template>
       </template>
     </a-table>
 
@@ -223,18 +225,18 @@
       pagesize: 10,
       columns: [
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
+          key: 'startport',
           title: this.$t('label.startport'),
-          dataIndex: 'startport',
-          slots: { customRender: 'startport' }
+          dataIndex: 'startport'
         },
         {
+          key: 'endport',
           title: this.$t('label.endport'),
-          dataIndex: 'endport',
-          slots: { customRender: 'endport' }
+          dataIndex: 'endport'
         },
         {
           title: this.$t('label.icmptype'),
@@ -249,12 +251,12 @@
           dataIndex: 'cidr'
         },
         {
-          title: this.$t('label.account.and.security.group'),
-          slots: { customRender: 'account' }
+          key: 'account',
+          title: this.$t('label.account.and.security.group')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       isSubmitted: false
@@ -464,7 +466,6 @@
     },
     openTagsModal (rule) {
       this.selectedRule = rule
-      this.initForm()
       this.fetchTags(this.selectedRule)
       this.tagsModalVisible = true
     },
diff --git a/ui/src/views/network/InternalLBAssignVmForm.vue b/ui/src/views/network/InternalLBAssignVmForm.vue
index 2e48689..8eb030c 100644
--- a/ui/src/views/network/InternalLBAssignVmForm.vue
+++ b/ui/src/views/network/InternalLBAssignVmForm.vue
@@ -40,14 +40,19 @@
               v-focus="!addVmModalNicLoading && iLb.virtualmachineid[index] === vm.id && index === 0"
               v-else-if="!addVmModalNicLoading && iLb.virtualmachineid[index] === vm.id"
               mode="multiple"
+              style="min-width: 200px;"
               v-model:value="iLb.vmguestip[index]"
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="(nic, nicIndex) in nics[index]" :key="nic" :value="nic">
-                {{ nic }}{{ nicIndex === 0 ? ` (${this.$t('label.primary')})` : null }}
+              <a-select-option
+                v-for="(nic, nicIndex) in nics[index]"
+                :key="nic"
+                :value="nic"
+                :label="nic">
+                {{ nic }}{{ nicIndex === 0 ? ` (${this.$t('label.primary')})` : '' }}
               </a-select-option>
             </a-select>
           </span>
diff --git a/ui/src/views/network/InternalLBAssignedVmTab.vue b/ui/src/views/network/InternalLBAssignedVmTab.vue
index 67211f0..035af0e 100644
--- a/ui/src/views/network/InternalLBAssignedVmTab.vue
+++ b/ui/src/views/network/InternalLBAssignedVmTab.vue
@@ -25,23 +25,25 @@
       :rowKey="item => item.id"
       :pagination="false"
     >
-      <template #displayname="{ record }">
-        <router-link :to="{ path: '/vm/' + record.id }">{{ record.displayname || record.name }}</router-link>
-      </template>
-      <template #ipaddress="{ record }">
-        <span v-for="nic in record.nic" :key="nic.id">
-          <span v-if="nic.networkid === resource.networkid">
-            {{ nic.ipaddress }} <br/>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'displayname'">
+          <router-link :to="{ path: '/vm/' + record.id }">{{ record.displayname || record.name }}</router-link>
+        </template>
+        <template v-if="column.key === 'ipaddress'">
+          <span v-for="nic in record.nic" :key="nic.id">
+            <span v-if="nic.networkid === resource.networkid">
+              {{ nic.ipaddress }} <br/>
+            </span>
           </span>
-        </span>
-      </template>
-      <template #remove="{ record }">
-        <tooltip-button
-          :tooltip="$t('label.remove.vm.from.lb')"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          @onClick="removeVmFromLB(record)" />
+        </template>
+        <template v-if="column.key === 'remove'">
+          <tooltip-button
+            :tooltip="$t('label.remove.vm.from.lb')"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            @onClick="removeVmFromLB(record)" />
+        </template>
       </template>
       <a-divider />
     </a-table>
@@ -86,18 +88,18 @@
       totalInstances: 0,
       columns: [
         {
+          key: 'displayname',
           title: this.$t('label.name'),
-          dataIndex: 'displayname',
-          slots: { customRender: 'displayname' }
+          dataIndex: 'displayname'
         },
         {
+          key: 'ipaddress',
           title: this.$t('label.ipaddress'),
-          dataIndex: 'ipaddress',
-          slots: { customRender: 'ipaddress' }
+          dataIndex: 'ipaddress'
         },
         {
-          title: '',
-          slots: { customRender: 'remove' }
+          key: 'remove',
+          title: ''
         }
       ]
     }
diff --git a/ui/src/views/network/IpAddressesTab.vue b/ui/src/views/network/IpAddressesTab.vue
index bc61d65..8b22ec6 100644
--- a/ui/src/views/network/IpAddressesTab.vue
+++ b/ui/src/views/network/IpAddressesTab.vue
@@ -47,12 +47,12 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option key="all" value="">
             {{ $t('label.view.all') }}
           </a-select-option>
-          <a-select-option v-for="network in networksList" :key="network.id" :value="network.id">
+          <a-select-option v-for="network in networksList" :key="network.id" :value="network.id" :label="network.name">
             {{ network.name }}
           </a-select-option>
         </a-select>
@@ -65,35 +65,57 @@
         :rowKey="item => item.id"
         :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
         :pagination="false" >
-        <template #ipaddress="{ text, record }">
-          <router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
-          <div v-else>{{ text }}</div>
-          <a-tag v-if="record.issourcenat === true">source-nat</a-tag>
-        </template>
+        <template #bodyCell="{ column, text, record }">
+          <template v-if="column.key === 'ipaddress'">
+            <router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }}&nbsp;</router-link>
+            <div v-else>{{ text }}</div>
+            <template v-if="record.issourcenat === true">
+              <a-tag>{{ $t('label.sourcenat') }}</a-tag>
+            </template>
+            <template v-else-if="record.isstaticnat === true">
+              <a-tag>{{ $t('label.staticnat') }}</a-tag>
+            </template>
+            <template v-else-if="record.hasrules === false">
+              <tooltip-button
+                v-if="record.forvirtualnetwork === true"
+                :tooltip="$t('label.action.set.as.source.nat.ip')"
+                type="primary"
+                :danger="false"
+                icon="aim-outlined"
+                :disabled="!('updateNetwork' in $store.getters.apis)"
+                @onClick="showChangeSourceNat(record)"></tooltip-button>
+            </template>
+            <template v-else><!-- -if="record.hasrules === true" -->
+              <Tooltip placement="topLeft" :title="$t('message.sourcenatip.change.inhibited')" >
+                <a-tag>{{ $t('label.hasrules') }}</a-tag>
+              </Tooltip>
+            </template>
+          </template>
 
-        <template #state="{ record }">
-          <status :text="record.state" displayText />
-        </template>
+          <template v-if="column.key === 'state'">
+            <status :text="record.state" displayText />
+          </template>
 
-        <template #virtualmachineid="{ record }">
-          <desktop-outlined v-if="record.virtualmachineid" />
-          <router-link :to="{ path: '/vm/' + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
-        </template>
+          <template v-if="column.key === 'virtualmachineid'">
+            <desktop-outlined v-if="record.virtualmachineid" />
+            <router-link :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
+          </template>
 
-        <template #associatednetworkname="{ record }">
-          <router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
-          <div v-else>{{ record.networkname }}</div>
-        </template>
+          <template v-if="column.key === 'associatednetworkname'">
+            <router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
+            <div v-else>{{ record.networkname }}</div>
+          </template>
 
-        <template #action="{ record }">
-          <tooltip-button
-            v-if="record.issourcenat !== true && record.forvirtualnetwork === true"
-            :tooltip="$t('label.action.release.ip')"
-            type="primary"
-            :danger="true"
-            icon="delete-outlined"
-            :disabled="!('disassociateIpAddress' in $store.getters.apis)"
-            @onClick="releaseIpAddress(record)" />
+          <template v-if="column.key === 'actions'">
+            <tooltip-button
+              v-if="record.issourcenat !== true && record.forvirtualnetwork === true"
+              :tooltip="$t('label.action.release.ip')"
+              type="primary"
+              :danger="true"
+              icon="delete-outlined"
+              :disabled="!('disassociateIpAddress' in $store.getters.apis)"
+              @onClick="releaseIpAddress(record)" />
+          </template>
         </template>
       </a-table>
       <a-divider/>
@@ -133,11 +155,12 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option
                 v-for="ip in listPublicIpAddress"
-                :key="ip.ipaddress">{{ ip.ipaddress }} ({{ ip.state }})</a-select-option>
+                :key="ip.ipaddress"
+                :label="ip.ipaddress + '(' + ip.state + ')'">{{ ip.ipaddress }} ({{ ip.state }})</a-select-option>
             </a-select>
           </a-form-item>
           <div :span="24" class="action-button">
@@ -147,6 +170,24 @@
         </a-form>
       </a-spin>
     </a-modal>
+    <a-modal
+      v-if="changeSourceNat"
+      :title="$t('message.sourcenatip.change.warning')"
+      :visible="changeSourceNat"
+      :closable="true"
+      :footer="null"
+      @cancel="cancelChangeSourceNat"
+      centered
+      :disabled="!('updateNetwork' in $store.getters.apis)"
+      width="450px">
+      <template>
+        <a-alert :message="$t('message.sourcenatip.change.warning')" type="warning" />
+      </template>
+      <div :span="24" class="action-button">
+        <a-button @click="cancelChangeSourceNat">{{ $t('label.cancel') }}</a-button>
+        <a-button ref="submit" type="primary" @click="setSourceNatIp(record)">{{ $t('label.ok') }}</a-button>
+      </div>
+    </a-modal>
     <bulk-action-view
       v-if="showConfirmationAction || showGroupActionModal"
       :showConfirmationAction="showConfirmationAction"
@@ -164,6 +205,7 @@
       @close-modal="closeModal" />
   </div>
 </template>
+
 <script>
 import { api } from '@/api'
 import Status from '@/components/widgets/Status'
@@ -204,7 +246,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.release.public.ip.address'),
@@ -212,34 +254,35 @@
       },
       columns: [
         {
+          key: 'ipaddress',
           title: this.$t('label.ipaddress'),
-          dataIndex: 'ipaddress',
-          slots: { customRender: 'ipaddress' }
+          dataIndex: 'ipaddress'
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
+          key: 'virtualmachineid',
           title: this.$t('label.vm'),
-          dataIndex: 'virtualmachineid',
-          slots: { customRender: 'virtualmachineid' }
+          dataIndex: 'virtualmachineid'
         },
         {
+          key: 'associatednetworkname',
           title: this.$t('label.network'),
-          dataIndex: 'associatednetworkname',
-          slots: { customRender: 'associatednetworkname' }
+          dataIndex: 'associatednetworkname'
         },
         {
-          title: '',
-          slots: { customRender: 'action' }
+          key: 'actions',
+          title: ''
         }
       ],
       showAcquireIp: false,
       acquireLoading: false,
       acquireIp: null,
-      listPublicIpAddress: []
+      listPublicIpAddress: [],
+      changeSourceNat: false
     }
   },
   created () {
@@ -312,6 +355,50 @@
         return selection.indexOf(item.id) !== -1
       }))
     },
+    setSourceNatIp (ipaddress) {
+      if (this.settingsourcenat) return
+      if (this.$route.path.startsWith('/vpc')) {
+        this.updateVpc(ipaddress)
+      } else {
+        this.updateNetwork(ipaddress)
+      }
+    },
+    updateNetwork (ipaddress) {
+      const params = {}
+      params.sourcenatipaddress = this.sourceNatIp.ipaddress
+      params.id = this.resource.id
+      this.settingsourcenat = true
+      api('updateNetwork', params).then(response => {
+        this.fetchData()
+      }).catch(error => {
+        this.$notification.error({
+          message: `${this.$t('label.error')} ${error.response.status}`,
+          description: error.response.data.updatenetworkresponse.errortext || error.response.data.errorresponse.errortext,
+          duration: 0
+        })
+      }).finally(() => {
+        this.settingsourcenat = false
+        this.cancelChangeSourceNat()
+      })
+    },
+    updateVpc (ipaddress) {
+      const params = {}
+      params.sourcenatipaddress = this.sourceNatIp.ipaddress
+      params.id = this.resource.id
+      this.settingsourcenat = true
+      api('updateVPC', params).then(response => {
+        this.fetchData()
+      }).catch(error => {
+        this.$notification.error({
+          message: `${this.$t('label.error')} ${error.response.status}`,
+          description: error.response.data.updatevpcresponse.errortext || error.response.data.errorresponse.errortext,
+          duration: 0
+        })
+      }).finally(() => {
+        this.settingsourcenat = false
+        this.cancelChangeSourceNat()
+      })
+    },
     resetSelection () {
       this.setSelection([])
     },
@@ -385,9 +472,9 @@
     releaseIpAddresses (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
@@ -439,6 +526,14 @@
         })
       })
     },
+    getVmRouteUsingType (record) {
+      switch (record.virtualmachinetype) {
+        case 'DomainRouter' : return '/router/'
+        case 'ConsoleProxy' :
+        case 'SecondaryStorageVm': return '/systemvm/'
+        default: return '/vm/'
+      }
+    },
     async onShowAcquireIp () {
       this.showAcquireIp = true
       this.acquireLoading = true
@@ -471,6 +566,13 @@
     },
     closeModal () {
       this.showConfirmationAction = false
+    },
+    showChangeSourceNat (ipaddress) {
+      this.changeSourceNat = true
+      this.sourceNatIp = ipaddress
+    },
+    cancelChangeSourceNat () {
+      this.changeSourceNat = false
     }
   }
 }
diff --git a/ui/src/views/network/Ipv6FirewallRulesTab.vue b/ui/src/views/network/Ipv6FirewallRulesTab.vue
index cacaaf2..ffdcc9d 100644
--- a/ui/src/views/network/Ipv6FirewallRulesTab.vue
+++ b/ui/src/views/network/Ipv6FirewallRulesTab.vue
@@ -41,13 +41,13 @@
           <a-select
             v-model:value="newRule.traffictype"
             showSearch
-            optionFilterProp="children"
+            optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             @change="val => { handleTrafficTypeChange(val) }" >
-            <a-select-option value="ingress">{{ $t('label.ingress') }}</a-select-option>
-            <a-select-option value="egress">{{ $t('label.egress') }}</a-select-option>
+            <a-select-option value="ingress" :label="$t('label.ingress')">{{ $t('label.ingress') }}</a-select-option>
+            <a-select-option value="egress" :label="$t('label.egress')">{{ $t('label.egress') }}</a-select-option>
           </a-select>
         </div>
         <div class="form__item">
@@ -57,11 +57,11 @@
             style="width: 100%;"
             @change="resetRulePorts"
             showSearch
-            optionFilterProp="children"
+            optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="opt in protocols" :key="opt">
+            <a-select-option v-for="opt in protocols" :key="opt" :label="$t('label.' + opt)">
               {{ $t('label.' + opt) }}
             </a-select-option>
           </a-select>
@@ -107,27 +107,29 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.id">
-      <template #traffictype="{record}">
-        {{ record.traffictype }}
-      </template>
-      <template #protocol="{record}">
-        {{ capitalise(record.protocol) }}
-      </template>
-      <template #startport="{record}">
-        {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
-      </template>
-      <template #endport="{record}">
-        {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
-      </template>
-      <template #actions="{record}">
-        <tooltip-button
-          :tooltip="$t('label.delete')"
-          :disabled="!('deleteIpv6FirewallRule' in $store.getters.apis)"
-          type="primary"
-          :danger="true"
-          icon="delete-outlined"
-          buttonClass="rule-action"
-          @click="deleteRule(record)" />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'traffictype'">
+          {{ record.traffictype }}
+        </template>
+        <template v-if="column.key === 'protocol'">
+          {{ capitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'startport'">
+          {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
+        </template>
+        <template v-if="column.key === 'endport'">
+          {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
+        </template>
+        <template v-if="column.key === 'actions'">
+          <tooltip-button
+            :tooltip="$t('label.delete')"
+            :disabled="!('deleteIpv6FirewallRule' in $store.getters.apis)"
+            type="primary"
+            :danger="true"
+            icon="delete-outlined"
+            buttonClass="rule-action"
+            @click="deleteRule(record)" />
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -190,7 +192,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['Action'],
+      filterColumns: ['Actions'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.delete.ip.v6.firewall.rules'),
@@ -222,24 +224,24 @@
           dataIndex: 'destcidrlist'
         },
         {
-          title: this.$t('label.traffictype'),
-          slots: { customRender: 'traffictype' }
+          key: 'traffictype',
+          title: this.$t('label.traffictype')
         },
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
-          title: this.$t('label.icmptype.start.port'),
-          slots: { customRender: 'startport' }
+          key: 'startport',
+          title: this.$t('label.icmptype.start.port')
         },
         {
-          title: this.$t('label.icmpcode.end.port'),
-          slots: { customRender: 'endport' }
+          key: 'endport',
+          title: this.$t('label.icmpcode.end.port')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       protocols: [
@@ -312,9 +314,9 @@
     deleteRules (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/network/LoadBalancing.vue b/ui/src/views/network/LoadBalancing.vue
index 90ed4c0..a03bc38 100644
--- a/ui/src/views/network/LoadBalancing.vue
+++ b/ui/src/views/network/LoadBalancing.vue
@@ -47,11 +47,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="roundrobin">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option>
-            <a-select-option value="leastconn">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option>
-            <a-select-option value="source">{{ $t('label.lb.algorithm.source') }}</a-select-option>
+            <a-select-option value="roundrobin" :label="$t('label.lb.algorithm.roundrobin')">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option>
+            <a-select-option value="leastconn" :label="$t('label.lb.algorithm.leastconn')">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option>
+            <a-select-option value="source" :label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') }}</a-select-option>
           </a-select>
         </div>
         <div class="form__item">
@@ -62,11 +62,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp-proxy">{{ $t('label.tcp.proxy') }}</a-select-option>
-            <a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
-            <a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
+            <a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
           </a-select>
         </div>
         <div class="form__item">
@@ -76,9 +76,9 @@
             defaultValue="no"
             style="min-width: 100px"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option value="yes">{{ $t('label.yes') }}</a-select-option>
             <a-select-option value="no">{{ $t('label.no') }}</a-select-option>
@@ -118,42 +118,61 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.id">
-      <template #cidrlist="{ record }">
-        <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
-      </template>
-      <template #algorithm="{ record }">
-        {{ returnAlgorithmName(record.algorithm) }}
-      </template>
-      <template #protocol="{record}">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #stickiness="{record}">
-        <a-button @click="() => openStickinessModal(record.id)">
-          {{ returnStickinessLabel(record.id) }}
-        </a-button>
-      </template>
-      <template #autoscale="{record}">
-        <div>
-          <router-link :to="{ path: '/autoscalevmgroup/' + record.autoscalevmgroup.id }" v-if='record.autoscalevmgroup'>
-            <a-button>{{ $t('label.view') }}</a-button>
-          </router-link>
-          <router-link :to="{ path: '/action/createAutoScaleVmGroup', query: { networkid: record.networkid, lbruleid : record.id } }" v-else-if='!record.ruleInstances'>
-            <a-button>{{ $t('label.new') }}</a-button>
-          </router-link>
-        </div>
-      </template>
-      <template #healthmonitor="{ record }">
-        <a-button @click="() => openHealthMonitorModal(record.id)">
-          {{ returnHealthMonitorLabel(record.id) }}
-        </a-button>
-      </template>
-      <template #add="{record}">
-        <a-button type="primary" @click="() => { selectedRule = record; handleOpenAddVMModal() }" v-if='!record.autoscalevmgroup'>
-          <template #icon>
-            <plus-outlined />
-          </template>
-          {{ $t('label.add') }}
-        </a-button>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'cidrlist'">
+          <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(" ", "\n") }}</span>
+        </template>
+        <template v-if="column.key === 'algorithm'">
+          {{ returnAlgorithmName(record.algorithm) }}
+        </template>
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'stickiness'">
+          <a-button @click="() => openStickinessModal(record.id)">
+            {{ returnStickinessLabel(record.id) }}
+          </a-button>
+        </template>
+        <template v-if="column.key === 'autoscale'">
+          <div>
+            <router-link :to="{ path: '/autoscalevmgroup/' + record.autoscalevmgroup.id }" v-if='record.autoscalevmgroup'>
+              <a-button>{{ $t('label.view') }}</a-button>
+            </router-link>
+            <router-link :to="{ path: '/action/createAutoScaleVmGroup', query: { networkid: record.networkid, lbruleid : record.id } }" v-else-if='!record.ruleInstances'>
+              <a-button>{{ $t('label.new') }}</a-button>
+            </router-link>
+          </div>
+        </template>
+        <template v-if="column.key === 'healthmonitor'">
+          <a-button @click="() => openHealthMonitorModal(record.id)">
+            {{ returnHealthMonitorLabel(record.id) }}
+          </a-button>
+        </template>
+        <template v-if="column.key === 'add'">
+          <a-button v-if="!record.autoscalevmgroup" type="primary" @click="() => { selectedRule = record; handleOpenAddVMModal() }">
+            <template #icon><plus-outlined /></template>
+              {{ $t('label.add') }}
+          </a-button>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(record)" />
+            <tooltip-button :tooltip="$t('label.edit.tags')" :disabled="!('updateLoadBalancerRule' in $store.getters.apis)" icon="tag-outlined" @onClick="() => openTagsModal(record.id)" />
+            <a-popconfirm
+              :title="$t('label.delete') + '?'"
+              @confirm="handleDeleteRule(record)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')"
+            >
+              <tooltip-button
+                :tooltip="$t('label.delete')"
+                :disabled="!('deleteLoadBalancerRule' in $store.getters.apis)"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined" />
+            </a-popconfirm>
+          </div>
+        </template>
       </template>
       <template #expandedRowRender="{ record }">
         <div class="rule-instance-list">
@@ -178,25 +197,6 @@
           </div>
         </div>
       </template>
-      <template #actions="{record}">
-        <div class="actions">
-          <tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(record)" />
-          <tooltip-button :tooltip="$t('label.edit.tags')" :disabled="!('updateLoadBalancerRule' in $store.getters.apis)" icon="tag-outlined" @onClick="() => openTagsModal(record.id)" />
-          <a-popconfirm
-            :title="$t('label.delete') + '?'"
-            @confirm="handleDeleteRule(record)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')"
-          >
-            <tooltip-button
-              :tooltip="$t('label.delete')"
-              :disabled="!('deleteLoadBalancerRule' in $store.getters.apis) || record.autoscalevmgroup"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined" />
-          </a-popconfirm>
-        </div>
-      </template>
     </a-table>
     <a-pagination
       class="pagination"
@@ -267,7 +267,6 @@
     </a-modal>
 
     <a-modal
-      :title="$t('label.configure.sticky.policy')"
       :visible="stickinessModalVisible"
       :footer="null"
       :afterClose="closeModal"
@@ -276,6 +275,16 @@
       :okButtonProps="{ props: {htmlType: 'submit'}}"
       @cancel="stickinessModalVisible = false">
 
+      <template #title>
+        <span>{{ $t('label.configure.sticky.policy') }}</span>
+        <a
+          style="margin-left: 5px"
+          :href="$config.docBase + '/adminguide/networking/external_firewalls_and_load_balancers.html#sticky-session-policies-for-load-balancer-rules'"
+          target="_blank">
+          <question-circle-outlined />
+        </a>
+      </template>
+
       <span v-show="stickinessModalLoading" class="modal-loading">
         <loading-outlined />
       </span>
@@ -288,7 +297,10 @@
         v-ctrl-enter="handleSubmitStickinessForm"
         class="custom-ant-form"
        >
-        <a-form-item name="methodname" ref="methodname" :label="$t('label.stickiness.method')">
+        <a-form-item name="methodname" ref="methodname">
+          <template #label>
+            <tooltip-label :title="$t('label.stickiness.method')" :tooltip="createLoadBalancerStickinessPolicyParams.methodname.description" :tooltip-placement="'right'"/>
+          </template>
           <a-select
             v-focus="true"
             v-model:value="form.methodname"
@@ -296,69 +308,77 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="LbCookie">{{ $t('label.lb.cookie') }}</a-select-option>
-            <a-select-option value="AppCookie">{{ $t('label.app.cookie') }}</a-select-option>
-            <a-select-option value="SourceBased">{{ $t('label.source.based') }}</a-select-option>
-            <a-select-option value="none">{{ $t('label.none') }}</a-select-option>
+            <a-select-option value="LbCookie" :label="$t('label.lb.cookie')">{{ $t('label.lb.cookie') }}</a-select-option>
+            <a-select-option value="AppCookie" :label="$t('label.app.cookie')">{{ $t('label.app.cookie') }}</a-select-option>
+            <a-select-option value="SourceBased" :label="$t('label.source.based')">{{ $t('label.source.based') }}</a-select-option>
+            <a-select-option value="none" :label="$t('label.none')">{{ $t('label.none') }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item
           name="name"
           ref="name"
-          :label="$t('label.sticky.name')"
           v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod ===
             'AppCookie' || stickinessPolicyMethod === 'SourceBased'">
           <a-input v-model:value="form.name" />
+          <template #label>
+            <tooltip-label :title="$t('label.sticky.name')" :tooltip="createLoadBalancerStickinessPolicyParams.name.description" :tooltip-placement="'right'"/>
+          </template>
         </a-form-item>
-        <a-form-item
-          name="cookieName"
-          ref="cookieName"
-          :label="$t('label.sticky.cookie-name')"
-          v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod ===
-            'AppCookie'">
-          <a-input v-model:value="form.cookieName" />
-        </a-form-item>
-        <a-form-item
-          name="mode"
-          ref="mode"
-          :label="$t('label.sticky.mode')"
-          v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod ===
-            'AppCookie'">
-          <a-input v-model:value="form.mode" />
-        </a-form-item>
-        <a-form-item name="nocache" ref="nocache" :label="$t('label.sticky.nocache')" v-show="stickinessPolicyMethod === 'LbCookie'">
-          <a-checkbox v-model:checked="form.nocache"></a-checkbox>
-        </a-form-item>
-        <a-form-item name="indirect" ref="indirect" :label="$t('label.sticky.indirect')" v-show="stickinessPolicyMethod === 'LbCookie'">
-          <a-checkbox v-model:checked="form.indirect"></a-checkbox>
-        </a-form-item>
-        <a-form-item name="postonly" ref="postonly" :label="$t('label.sticky.postonly')" v-show="stickinessPolicyMethod === 'LbCookie'">
-          <a-checkbox v-model:checked="form.postonly"></a-checkbox>
-        </a-form-item>
-        <a-form-item name="domain" ref="domain" :label="$t('label.domain')" v-show="stickinessPolicyMethod === 'LbCookie'">
-          <a-input v-model:value="form.domain" />
-        </a-form-item>
-        <a-form-item name="length" ref="length" :label="$t('label.sticky.length')" v-show="stickinessPolicyMethod === 'AppCookie'">
-          <a-input v-model:value="form.length" type="number" />
-        </a-form-item>
-        <a-form-item name="holdtime" ref="holdtime" :label="$t('label.sticky.holdtime')" v-show="stickinessPolicyMethod === 'AppCookie'">
-          <a-input v-model:value="form.holdtime" type="number" />
-        </a-form-item>
-        <a-form-item name="requestLearn" ref="requestLearn" :label="$t('label.sticky.request-learn')" v-show="stickinessPolicyMethod === 'AppCookie'">
-          <a-checkbox v-model:checked="form.requestLearn"></a-checkbox>
-        </a-form-item>
-        <a-form-item name="prefix" ref="prefix" :label="$t('label.sticky.prefix')" v-show="stickinessPolicyMethod === 'AppCookie'">
-          <a-checkbox v-model:checked="form.prefix"></a-checkbox>
-        </a-form-item>
-        <a-form-item name="tablesize" ref="tablesize" :label="$t('label.sticky.tablesize')" v-show="stickinessPolicyMethod === 'SourceBased'">
-          <a-input v-model:value="form.tablesize" />
-        </a-form-item>
-        <a-form-item name="expire" ref="expire" :label="$t('label.sticky.expire')" v-show="stickinessPolicyMethod === 'SourceBased'">
-          <a-input v-model:value="form.expire" />
-        </a-form-item>
-
+        <div v-if="stickinessPolicyMethod !== 'none'">
+          <br/>
+          {{ $t('message.loadbalancer.stickypolicy.configuration') }}
+          <br/>
+          <a-card>
+            <a-form-item
+              name="cookieName"
+              ref="cookieName"
+              :label="$t('label.sticky.cookie-name')"
+              v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod ===
+                'AppCookie'">
+              <a-input v-model:value="form.cookieName" />
+            </a-form-item>
+            <a-form-item
+              name="mode"
+              ref="mode"
+              :label="$t('label.sticky.mode')"
+              v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod ===
+                'AppCookie'">
+              <a-input v-model:value="form.mode" />
+            </a-form-item>
+            <a-form-item name="nocache" ref="nocache" :label="$t('label.sticky.nocache')" v-show="stickinessPolicyMethod === 'LbCookie'">
+              <a-checkbox v-model:checked="form.nocache"></a-checkbox>
+            </a-form-item>
+            <a-form-item name="indirect" ref="indirect" :label="$t('label.sticky.indirect')" v-show="stickinessPolicyMethod === 'LbCookie'">
+              <a-checkbox v-model:checked="form.indirect"></a-checkbox>
+            </a-form-item>
+            <a-form-item name="postonly" ref="postonly" :label="$t('label.sticky.postonly')" v-show="stickinessPolicyMethod === 'LbCookie'">
+              <a-checkbox v-model:checked="form.postonly"></a-checkbox>
+            </a-form-item>
+            <a-form-item name="domain" ref="domain" :label="$t('label.domain')" v-show="stickinessPolicyMethod === 'LbCookie'">
+              <a-input v-model:value="form.domain" />
+            </a-form-item>
+            <a-form-item name="length" ref="length" :label="$t('label.sticky.length')" v-show="stickinessPolicyMethod === 'AppCookie'">
+              <a-input v-model:value="form.length" type="number" />
+            </a-form-item>
+            <a-form-item name="holdtime" ref="holdtime" :label="$t('label.sticky.holdtime')" v-show="stickinessPolicyMethod === 'AppCookie'">
+              <a-input v-model:value="form.holdtime" type="number" />
+            </a-form-item>
+            <a-form-item name="requestLearn" ref="requestLearn" :label="$t('label.sticky.request-learn')" v-show="stickinessPolicyMethod === 'AppCookie'">
+              <a-checkbox v-model:checked="form.requestLearn"></a-checkbox>
+            </a-form-item>
+            <a-form-item name="prefix" ref="prefix" :label="$t('label.sticky.prefix')" v-show="stickinessPolicyMethod === 'AppCookie'">
+              <a-checkbox v-model:checked="form.prefix"></a-checkbox>
+            </a-form-item>
+            <a-form-item name="tablesize" ref="tablesize" :label="$t('label.sticky.tablesize')" v-show="stickinessPolicyMethod === 'SourceBased'">
+              <a-input v-model:value="form.tablesize" />
+            </a-form-item>
+            <a-form-item name="expire" ref="expire" :label="$t('label.sticky.expire')" v-show="stickinessPolicyMethod === 'SourceBased'">
+              <a-input v-model:value="form.expire" />
+            </a-form-item>
+          </a-card>
+        </div>
         <div :span="24" class="action-button">
           <a-button @click="stickinessModalVisible = false">{{ $t('label.cancel') }}</a-button>
           <a-button type="primary" ref="submit" @click="handleSubmitStickinessForm">{{ $t('label.ok') }}</a-button>
@@ -390,11 +410,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="roundrobin">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option>
-            <a-select-option value="leastconn">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option>
-            <a-select-option value="source">{{ $t('label.lb.algorithm.source') }}</a-select-option>
+            <a-select-option value="roundrobin" :label="$t('label.lb.algorithm.roundrobin')">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option>
+            <a-select-option value="leastconn" :label="$t('label.lb.algorithm.leastconn')">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option>
+            <a-select-option value="source" :label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') }}</a-select-option>
           </a-select>
         </div>
         <div class="edit-rule__item">
@@ -404,11 +424,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp-proxy">{{ $t('label.tcp.proxy') }}</a-select-option>
-            <a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
-            <a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
+            <a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
+            <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
           </a-select>
         </div>
         <div :span="24" class="action-button">
@@ -441,12 +461,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="tier in tiers.data"
               :loading="tiers.loading"
-              :key="tier.id">
+              :key="tier.id"
+              :label="tier.displaytext">
               {{ tier.displaytext }}
             </a-select-option>
           </a-select>
@@ -467,33 +488,39 @@
           :pagination="false"
           :rowKey="record => record.id"
           :scroll="{ y: 300 }">
-          <template #name="{text, record, index}">
-            <span>
-              {{ text }}
-            </span>
-            <loading-outlined v-if="addVmModalNicLoading" />
-            <a-select
-              style="display: block"
-              v-else-if="!addVmModalNicLoading && newRule.virtualmachineid[index] === record.id"
-              mode="multiple"
-              v-model:value="newRule.vmguestip[index]"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }" >
-              <a-select-option v-for="(nic, nicIndex) in nics[index]" :key="nic" :value="nic">
-                {{ nic }}{{ nicIndex === 0 ? ` (${$t('label.primary')})` : null }}
-              </a-select-option>
-            </a-select>
-          </template>
+          <template #bodyCell="{ column, text, record, index }">
+            <template v-if="column.key === 'name'">
+              <span>
+                {{ text }}
+              </span>
+              <loading-outlined v-if="addVmModalNicLoading" />
+              <a-select
+                style="display: block"
+                v-else-if="!addVmModalNicLoading && newRule.virtualmachineid[index] === record.id"
+                mode="multiple"
+                v-model:value="newRule.vmguestip[index]"
+                showSearch
+                optionFilterProp="label"
+                :filterOption="(input, option) => {
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }" >
+                <a-select-option
+                  v-for="(nic, nicIndex) in nics[index]"
+                  :key="nic"
+                  :value="nic"
+                  :label="nic + nicIndex === 0 ? ` (${$t('label.primary')})` : null">
+                  {{ nic }}{{ nicIndex === 0 ? ` (${$t('label.primary')})` : null }}
+                </a-select-option>
+              </a-select>
+            </template>
 
-          <template #state="{text}">
-            <status :text="text ? text : ''" displayText></status>
-          </template>
+            <template v-if="column.key === 'state'">
+              <status :text="text ? text : ''" displayText></status>
+            </template>
 
-          <template #action="{text, record, index}" style="text-align: center" :text="text">
-            <a-checkbox v-model:value="record.id" @change="e => fetchNics(e, index)" :disabled="newRule.autoscale" />
+            <template v-if="column.key === 'actions'" style="text-align: center" :text="text">
+              <a-checkbox v-model:value="record.id" @change="e => fetchNics(e, index)" :disabled="newRule.autoscale"/>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -540,9 +567,9 @@
             v-model:value="monitorForm.type"
             @change="(value) => { healthMonitorParams.type = value }"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
             <a-select-option value="PING">PING</a-select-option>
             <a-select-option value="TCP">TCP</a-select-option>
@@ -567,9 +594,9 @@
             v-focus="true"
             v-model:value="monitorForm.httpmethodtype"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }">
             <a-select-option value="GET">GET</a-select-option>
             <a-select-option value="HEAD">HEAD</a-select-option>
@@ -648,7 +675,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['State', 'Action', 'Add VMs', 'Stickiness'],
+      filterColumns: ['State', 'Actions', 'Add VMs', 'Stickiness'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.delete.load.balancer.rules'),
@@ -705,36 +732,36 @@
           dataIndex: 'privateport'
         },
         {
-          title: this.$t('label.cidrlist'),
-          slots: { customRender: 'cidrlist' }
+          key: 'algorithm',
+          title: this.$t('label.algorithm')
         },
         {
-          title: this.$t('label.algorithm'),
-          slots: { customRender: 'algorithm' }
+          key: 'cidrlist',
+          title: this.$t('label.cidrlist')
         },
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
           title: this.$t('label.state'),
           dataIndex: 'state'
         },
         {
-          title: this.$t('label.action.configure.stickiness'),
-          slots: { customRender: 'stickiness' }
+          key: 'stickiness',
+          title: this.$t('label.action.configure.stickiness')
         },
         {
-          title: this.$t('label.autoscale'),
-          slots: { customRender: 'autoscale' }
+          key: 'add',
+          title: this.$t('label.add.vms')
         },
         {
-          title: this.$t('label.add.vms'),
-          slots: { customRender: 'add' }
+          key: 'autoscale',
+          title: this.$t('label.autoscale')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       tiers: {
@@ -743,15 +770,15 @@
       },
       vmColumns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
           dataIndex: 'name',
-          slots: { customRender: 'name' },
           width: 220
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.displayname'),
@@ -766,9 +793,9 @@
           dataIndex: 'zonename'
         },
         {
+          key: 'actions',
           title: this.$t('label.select'),
-          dataIndex: 'action',
-          slots: { customRender: 'action' },
+          dataIndex: 'actions',
           width: 80
         }
       ],
@@ -797,6 +824,7 @@
   },
   beforeCreate () {
     this.createLoadBalancerRuleParams = this.$getApiParams('createLoadBalancerRule')
+    this.createLoadBalancerStickinessPolicyParams = this.$getApiParams('createLBStickinessPolicy')
   },
   created () {
     this.initForm()
@@ -857,7 +885,7 @@
         if (this.tiers.data?.[0]?.broadcasturi === 'tf://tf') {
           this.columns.splice(8, 0, {
             title: this.$t('label.action.health.monitor'),
-            slots: { customRender: 'healthmonitor' }
+            key: 'healthmonitor'
           })
         }
       }).catch(error => {
@@ -1311,9 +1339,9 @@
     deleteRules (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/network/NetworkPermissions.vue b/ui/src/views/network/NetworkPermissions.vue
index 8961b7e..a5434e5 100644
--- a/ui/src/views/network/NetworkPermissions.vue
+++ b/ui/src/views/network/NetworkPermissions.vue
@@ -44,19 +44,21 @@
         :rowKey="item => item.id"
         :pagination="false" >
 
-        <template #action="{ record }">
-          <a-popconfirm
-            :title="$t('message.confirm.remove.network.permission')"
-            @confirm="removeNetworkPermission(record.accountid, record.projectid)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')" >
-            <tooltip-button
-              tooltipPlacement="bottom"
-              :tooltip="$t('label.action.delete.network.permission')"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined" />
-          </a-popconfirm>
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'actions'">
+            <a-popconfirm
+              :title="$t('message.confirm.remove.network.permission')"
+              @confirm="removeNetworkPermission(record.accountid, record.projectid)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')" >
+              <tooltip-button
+                tooltipPlacement="bottom"
+                :tooltip="$t('label.action.delete.network.permission')"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined" />
+            </a-popconfirm>
+          </template>
         </template>
 
       </a-table>
@@ -85,15 +87,12 @@
       :footer="null"
       @cancel="showResetPermissionModal = false"
       centered
-      width="auto"
-      v-ctrl-enter="resetNetworkPermission">
+      width="auto">
       {{ $t('message.confirm.reset.network.permissions') }}
-      <a-form @submit="resetNetworkPermission">
-        <div :span="24" class="action-button">
-          <a-button @click="showResetPermissionModal = false">{{ $t('label.cancel') }}</a-button>
-          <a-button type="primary" ref="submit" @click="resetNetworkPermission">{{ $t('label.ok') }}</a-button>
-        </div>
-      </a-form>
+      <div :span="24" class="action-button">
+        <a-button @click="showResetPermissionModal = false">{{ $t('label.cancel') }}</a-button>
+        <a-button type="primary" ref="submit" @click="resetNetworkPermission">{{ $t('label.ok') }}</a-button>
+      </div>
     </a-modal>
   </div>
 </template>
@@ -140,8 +139,8 @@
           dataIndex: 'project'
         },
         {
-          title: '',
-          slots: { customRender: 'action' }
+          key: 'actions',
+          title: ''
         }
       ]
     }
diff --git a/ui/src/views/network/NicsTable.vue b/ui/src/views/network/NicsTable.vue
index d254990..f791e12 100644
--- a/ui/src/views/network/NicsTable.vue
+++ b/ui/src/views/network/NicsTable.vue
@@ -60,15 +60,17 @@
         </template>
       </a-descriptions>
     </template>
-    <template #networkname="{ text, record }">
-      <resource-icon v-if="!networkIconLoading && networkicon[record.id]" :image="networkicon[record.id]" size="1x" style="margin-right: 5px"/>
-      <apartment-outlined v-else style="margin-right: 5px" />
-      <router-link :to="{ path: '/guestnetwork/' + record.networkid }">
-        {{ text }}
-      </router-link>
-      <a-tag v-if="record.isdefault">
-        {{ $t('label.default') }}
-      </a-tag>
+    <template #bodyCell="{ column, text, record }">
+      <template v-if="column.key === 'networkname'">
+        <resource-icon v-if="!networkIconLoading && networkicon[record.id]" :image="networkicon[record.id]" size="1x" style="margin-right: 5px"/>
+        <apartment-outlined v-else style="margin-right: 5px" />
+        <router-link :to="{ path: '/guestnetwork/' + record.networkid }">
+          {{ text }}
+        </router-link>
+        <a-tag v-if="record.isdefault">
+          {{ $t('label.default') }}
+        </a-tag>
+      </template>
     </template>
   </a-table>
 </template>
@@ -97,14 +99,14 @@
     return {
       nicColumns: [
         {
+          key: 'deviceid',
           title: this.$t('label.deviceid'),
-          dataIndex: 'deviceid',
-          slots: { customRender: 'deviceid' }
+          dataIndex: 'deviceid'
         },
         {
+          key: 'networkname',
           title: this.$t('label.networkname'),
-          dataIndex: 'networkname',
-          slots: { customRender: 'networkname' }
+          dataIndex: 'networkname'
         },
         {
           title: this.$t('label.macaddress'),
diff --git a/ui/src/views/network/PortForwarding.vue b/ui/src/views/network/PortForwarding.vue
index edbf49a..22afcc3 100644
--- a/ui/src/views/network/PortForwarding.vue
+++ b/ui/src/views/network/PortForwarding.vue
@@ -66,10 +66,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="tcp">{{ $t('label.tcp') }}</a-select-option>
-            <a-select-option value="udp">{{ $t('label.udp') }}</a-select-option>
+            <a-select-option value="tcp" label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option>
+            <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option>
           </a-select>
         </div>
         <div class="form__item" style="margin-left: auto;">
@@ -98,33 +98,35 @@
       :pagination="false"
       :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
       :rowKey="record => record.id">
-      <template #privateport="{record}">
-        {{ record.privateport }} - {{ record.privateendport }}
-      </template>
-      <template #publicport="{record}">
-        {{ record.publicport }} - {{ record.publicendport }}
-      </template>
-      <template #protocol="{record}">
-        {{ getCapitalise(record.protocol) }}
-      </template>
-      <template #vm="{record}">
-        <div><desktop-outlined/>
-          <router-link
-            :to="{ path: '/vm/' + record.virtualmachineid }">
-            {{ record.virtualmachinename }}</router-link> ({{ record.vmguestip }})</div>
-      </template>
-      <template #actions="{record}">
-        <div class="actions">
-          <tooltip-button :tooltip="$t('label.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record.id)" />
-          <tooltip-button
-            :tooltip="$t('label.remove.rule')"
-            type="primary"
-            :danger="true"
-            icon="delete-outlined"
-            buttonClass="rule-action"
-            :disabled="!('deletePortForwardingRule' in $store.getters.apis)"
-            @onClick="deleteRule(record)" />
-        </div>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'privateport'">
+          {{ record.privateport }} - {{ record.privateendport }}
+        </template>
+        <template v-if="column.key === 'publicport'">
+          {{ record.publicport }} - {{ record.publicendport }}
+        </template>
+        <template v-if="column.key === 'protocol'">
+          {{ getCapitalise(record.protocol) }}
+        </template>
+        <template v-if="column.key === 'vm'">
+          <div><desktop-outlined/>
+            <router-link
+              :to="{ path: '/vm/' + record.virtualmachineid }">
+              {{ record.virtualmachinename }}</router-link> ({{ record.vmguestip }})</div>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="actions">
+            <tooltip-button :tooltip="$t('label.tags')" icon="tag-outlined" buttonClass="rule-action" @onClick="() => openTagsModal(record.id)" />
+            <tooltip-button
+              :tooltip="$t('label.remove.rule')"
+              type="primary"
+              :danger="true"
+              icon="delete-outlined"
+              buttonClass="rule-action"
+              :disabled="!('deletePortForwardingRule' in $store.getters.apis)"
+              @onClick="deleteRule(record)" />
+          </div>
+        </template>
       </template>
     </a-table>
     <a-pagination
@@ -215,12 +217,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="tier in tiers.data"
               :loading="tiers.loading"
-              :key="tier.id">
+              :key="tier.id"
+              :label="tier.displaytext || ''">
               {{ tier.displaytext }}
             </a-select-option>
           </a-select>
@@ -241,40 +244,46 @@
           :pagination="false"
           :rowKey="record => record.id"
           :scroll="{ y: 300 }">
-          <template #name="{text, record}">
-            <span>
-              {{ text }}
-            </span>
-            <loading-outlined v-if="addVmModalNicLoading"></loading-outlined>
-            <a-select
-              style="display: block"
-              v-else-if="!addVmModalNicLoading && newRule.virtualmachineid === record.id"
-              v-model:value="newRule.vmguestip"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }" >
-              <a-select-option v-for="(nic, nicIndex) in nics" :key="nic" :value="nic">
-                {{ nic }}{{ nicIndex === 0 ? ` (${$t('label.primary')})` : null }}
-              </a-select-option>
-            </a-select>
-          </template>
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'name'">
+              <span>
+                {{ text }}
+              </span>
+              <loading-outlined v-if="addVmModalNicLoading"></loading-outlined>
+              <a-select
+                style="display: block"
+                v-else-if="!addVmModalNicLoading && newRule.virtualmachineid === record.id"
+                v-model:value="newRule.vmguestip"
+                showSearch
+                optionFilterProp="label"
+                :filterOption="(input, option) => {
+                  return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }" >
+                <a-select-option
+                  v-for="(nic, nicIndex) in nics"
+                  :key="nic"
+                  :value="nic"
+                  :label="nic">
+                  {{ nic }}{{ nicIndex === 0 ? ` (${$t('label.primary')})` : null }}
+                </a-select-option>
+              </a-select>
+            </template>
 
-          <template #state="{text}">
-            <status :text="text ? text : ''" displayText></status>
-          </template>
+            <template v-if="column.key === 'state'">
+              <status :text="text ? text : ''" displayText></status>
+            </template>
 
-          <template #action="{record}">
-            <div style="text-align: center">
-              <a-radio-group
-                class="radio-group"
-                :key="record.id"
-                v-model:value="checked"
-                @change="($event) => checked = $event.target.value">
-                <a-radio :value="record.id" @change="e => fetchNics(e)" />
-              </a-radio-group>
-            </div>
+            <template v-if="column.key === 'actions'">
+              <div style="text-align: center">
+                <a-radio-group
+                  class="radio-group"
+                  :key="record.id"
+                  v-model:value="checked"
+                  @change="($event) => checked = $event.target.value">
+                  <a-radio :value="record.id" @change="e => fetchNics(e)" />
+                </a-radio-group>
+              </div>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -346,7 +355,7 @@
       showGroupActionModal: false,
       selectedItems: [],
       selectedColumns: [],
-      filterColumns: ['State', 'Action'],
+      filterColumns: ['State', 'Actions'],
       showConfirmationAction: false,
       message: {
         title: this.$t('label.action.bulk.delete.portforward.rules'),
@@ -379,28 +388,28 @@
       pageSize: 10,
       columns: [
         {
-          title: this.$t('label.privateport'),
-          slots: { customRender: 'privateport' }
+          key: 'privateport',
+          title: this.$t('label.privateport')
         },
         {
-          title: this.$t('label.publicport'),
-          slots: { customRender: 'publicport' }
+          key: 'publicport',
+          title: this.$t('label.publicport')
         },
         {
-          title: this.$t('label.protocol'),
-          slots: { customRender: 'protocol' }
+          key: 'protocol',
+          title: this.$t('label.protocol')
         },
         {
           title: this.$t('label.state'),
           dataIndex: 'state'
         },
         {
-          title: this.$t('label.vm'),
-          slots: { customRender: 'vm' }
+          key: 'vm',
+          title: this.$t('label.vm')
         },
         {
-          title: this.$t('label.action'),
-          slots: { customRender: 'actions' }
+          key: 'actions',
+          title: this.$t('label.actions')
         }
       ],
       tiers: {
@@ -409,15 +418,15 @@
       },
       vmColumns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
           dataIndex: 'name',
-          slots: { customRender: 'name' },
           width: 210
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.displayname'),
@@ -437,9 +446,9 @@
           dataIndex: 'zonename'
         },
         {
+          key: 'actions',
           title: this.$t('label.select'),
-          dataIndex: 'action',
-          slots: { customRender: 'action' },
+          dataIndex: 'actions',
           width: 80
         }
       ],
@@ -550,9 +559,9 @@
     deleteRules (e) {
       this.showConfirmationAction = false
       this.selectedColumns.splice(0, 0, {
+        key: 'status',
         dataIndex: 'status',
         title: this.$t('label.operation.status'),
-        slots: { customRender: 'status' },
         filters: [
           { text: 'In Progress', value: 'InProgress' },
           { text: 'Success', value: 'success' },
diff --git a/ui/src/views/network/ReservePublicIP.vue b/ui/src/views/network/ReservePublicIP.vue
index dc5486e..3fffc5e 100644
--- a/ui/src/views/network/ReservePublicIP.vue
+++ b/ui/src/views/network/ReservePublicIP.vue
@@ -35,9 +35,9 @@
           v-model:value="selectedAccountType"
           v-focus="true"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }">
           <a-select-option :value="$t('label.account')">{{ $t('label.account') }}</a-select-option>
           <a-select-option :value="$t('label.project')">{{ $t('label.project') }}</a-select-option>
@@ -71,7 +71,7 @@
             @change="changeAccount"
             v-model:value="selectedAccount"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
               return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
diff --git a/ui/src/views/network/RoutersTab.vue b/ui/src/views/network/RoutersTab.vue
index 73f7bf6..a2e9323 100644
--- a/ui/src/views/network/RoutersTab.vue
+++ b/ui/src/views/network/RoutersTab.vue
@@ -25,20 +25,22 @@
     :pagination="false"
     :loading="fetchLoading"
   >
-    <template #name="{ text, record }">
-      <router-link :to="{ path: '/router/' + record.id }" >{{ text }}</router-link>
-    </template>
-    <template #status="{ record }">
-      <status class="status" :text="record.state" displayText />
-    </template>
-    <template #requiresupgrade="{ record }">
-      {{ record.requiresupgrade ? $t('label.yes') : $t('label.no') }}
-    </template>
-    <template #isredundantrouter="{ record }">
-      {{ record.isredundantrouter ? record.redundantstate : record.isredundantrouter }}
-    </template>
-    <template #hostname="{ record }">
-      <router-link :to="{ path: '/host/' + record.hostid }" >{{ record.hostname || record.hostid }}</router-link>
+    <template #bodyCell="{ column, text, record }">
+      <template v-if="column.key === 'name'">
+        <router-link :to="{ path: '/router/' + record.id }" >{{ text }}</router-link>
+      </template>
+      <template v-if="column.key === 'status'">
+        <status class="status" :text="record.state" displayText />
+      </template>
+      <template v-if="column.key === 'requiresupgrade'">
+        {{ record.requiresupgrade ? $t('label.yes') : $t('label.no') }}
+      </template>
+      <template v-if="column.key === 'isredundantrouter'">
+        {{ record.isredundantrouter ? record.redundantstate : record.isredundantrouter }}
+      </template>
+      <template v-if="column.key === 'hostname'">
+        <router-link :to="{ path: '/host/' + record.hostid }" >{{ record.hostname || record.hostid }}</router-link>
+      </template>
     </template>
   </a-table>
 </template>
@@ -68,14 +70,14 @@
       routers: [],
       columns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
+          key: 'status',
           title: this.$t('label.status'),
-          dataIndex: 'state',
-          slots: { customRender: 'status' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.ip'),
@@ -86,19 +88,19 @@
           dataIndex: 'version'
         },
         {
+          key: 'requiresupgrade',
           title: this.$t('label.requiresupgrade'),
-          dataIndex: 'requiresupgrade',
-          slots: { customRender: 'requiresupgrade' }
+          dataIndex: 'requiresupgrade'
         },
         {
+          key: 'isredundantrouter',
           title: this.$t('label.isredundantrouter'),
-          dataIndex: 'isredundantrouter',
-          slots: { customRender: 'isredundantrouter' }
+          dataIndex: 'isredundantrouter'
         },
         {
+          key: 'hostname',
           title: this.$t('label.hostname'),
-          dataIndex: 'hostname',
-          slots: { customRender: 'hostname' }
+          dataIndex: 'hostname'
         }
       ]
     }
diff --git a/ui/src/views/network/UpdateNetwork.vue b/ui/src/views/network/UpdateNetwork.vue
index 02b826c..42c9107 100644
--- a/ui/src/views/network/UpdateNetwork.vue
+++ b/ui/src/views/network/UpdateNetwork.vue
@@ -80,6 +80,24 @@
             </a-col>
           </a-row>
         </div>
+        <a-form-item name="sourcenatipaddress" ref="sourcenatipaddress">
+          <template #label>
+            <tooltip-label :title="$t('label.sourcenatipaddress')" :tooltip="apiParams.sourcenatipaddress.description"/>
+          </template>
+          <span v-if="sourcenatchange">
+            <a-alert type="warning">
+              <template #message>
+                <span v-html="$t('message.sourcenatip.change.warning')" />
+              </template>
+            </a-alert>
+            <br/>
+          </span>
+          <a-input
+            v-model:value="form.sourcenatipaddress"
+            :placeholder="apiParams.sourcenatipaddress.description"
+            v-focus="true"
+            @change="sourcenatchange = form.sourcenatipaddress.length > 0"/>
+        </a-form-item>
         <a-form-item name="networkofferingid" ref="networkofferingid" v-if="isUpdatingIsolatedNetwork">
           <template #label>
             <tooltip-label :title="$t('label.networkofferingid')" :tooltip="apiParams.networkofferingid.description"/>
@@ -98,12 +116,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="networkOfferingLoading"
             :placeholder="apiParams.networkofferingid.description"
             @change="val => { networkOffering = networkOfferings[val] }">
-            <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in networkOfferings" :key="optIndex" :label="opt.displaytext || opt.name">
               {{ opt.displaytext || opt.name }}
             </a-select-option>
           </a-select>
@@ -238,7 +256,8 @@
       minMTU: 68,
       errorPrivateMtu: '',
       errorPublicMtu: '',
-      setMTU: false
+      setMTU: false,
+      sourcenatchange: false
     }
   },
   beforeCreate () {
diff --git a/ui/src/views/network/VpcTab.vue b/ui/src/views/network/VpcTab.vue
index 0183764..57abe8e 100644
--- a/ui/src/views/network/VpcTab.vue
+++ b/ui/src/views/network/VpcTab.vue
@@ -48,10 +48,12 @@
           :rowKey="item => item.id"
           :pagination="false"
         >
-          <template #name="{ text, record }">
-            <router-link :to="{ path: '/acllist/' + record.id }">
-              {{ text }}
-            </router-link>
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'name'">
+              <router-link :to="{ path: '/acllist/' + record.id }">
+                {{ text }}
+              </router-link>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -75,7 +77,7 @@
           :footer="null"
           :maskClosable="false"
           :closable="true"
-          @cancel="modals.networkAcl = fetchAclList">
+          @cancel="modals.networkAcl = false">
           <a-form
             layout="vertical"
             :ref="formRef"
@@ -117,11 +119,13 @@
           :rowKey="item => item.id"
           :pagination="false"
         >
-          <template #ipaddress="{ text, record }">
-            <router-link :to="{ path: '/privategw/' + record.id }">{{ text }}</router-link>
-          </template>
-          <template #state="{ record }">
-            <status :text="record.state" displayText></status>
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'ipaddress'">
+              <router-link :to="{ path: '/privategw/' + record.id }">{{ text }}</router-link>
+            </template>
+            <template v-if="column.key === 'state'">
+              <status :text="record.state" displayText></status>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -163,9 +167,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
-                  <a-select-option v-for="item in physicalnetworks" :key="item.id" :value="item.id">
+                  <a-select-option v-for="item in physicalnetworks" :key="item.id" :value="item.id" :label="item.name">
                     {{ item.name }}
                   </a-select-option>
                 </a-select>
@@ -286,13 +290,15 @@
           :dataSource="vpnConnections"
           :pagination="false"
           :rowKey="record => record.id">
-          <template #publicip="{text, record}">
-            <router-link :to="{ path: '/s2svpnconn/' + record.id }">
-              {{ text }}
-            </router-link>
-          </template>
-          <template #state="{text}">
-            <status :text="text ? text : ''" displayText />
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'publicip'">
+              <router-link :to="{ path: '/s2svpnconn/' + record.id }">
+                {{ text }}
+              </router-link>
+            </template>
+            <template v-if="column.key === 'state'">
+              <status :text="text ? text : ''" displayText />
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -332,9 +338,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
-                  <a-select-option v-for="item in vpncustomergateways" :key="item.id" :value="item.id">
+                  <a-select-option v-for="item in vpncustomergateways" :key="item.id" :value="item.id" :label="item.name">
                     {{ item.name }}
                   </a-select-option>
                 </a-select>
@@ -378,6 +384,7 @@
 import VpcTiersTab from './VpcTiersTab'
 import EventsTab from '@/components/view/EventsTab'
 import AnnotationsTab from '@/components/view/AnnotationsTab'
+import ResourceIcon from '@/components/view/ResourceIcon'
 
 export default {
   name: 'VpcTab',
@@ -388,7 +395,8 @@
     RoutersTab,
     VpcTiersTab,
     EventsTab,
-    AnnotationsTab
+    AnnotationsTab,
+    ResourceIcon
   },
   mixins: [mixinDevice],
   props: {
@@ -426,14 +434,14 @@
       vpncustomergateways: [],
       privateGatewaysColumns: [
         {
+          key: 'ipaddress',
           title: this.$t('label.ip'),
-          dataIndex: 'ipaddress',
-          slots: { customRender: 'ipaddress' }
+          dataIndex: 'ipaddress'
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.gateway'),
@@ -450,14 +458,14 @@
       ],
       vpnConnectionsColumns: [
         {
+          key: 'publicip',
           title: this.$t('label.ip'),
-          dataIndex: 'publicip',
-          slots: { customRender: 'publicip' }
+          dataIndex: 'publicip'
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
           title: this.$t('label.gateway'),
@@ -470,9 +478,9 @@
       ],
       networkAclsColumns: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
           title: this.$t('label.description'),
diff --git a/ui/src/views/network/VpcTiersTab.vue b/ui/src/views/network/VpcTiersTab.vue
index bd37001..214ea1a 100644
--- a/ui/src/views/network/VpcTiersTab.vue
+++ b/ui/src/views/network/VpcTiersTab.vue
@@ -80,17 +80,19 @@
                 :rowKey="item => item.id"
                 :pagination="false"
                 :loading="fetchLoading">
-                <template #name="{ record }">
-                  <router-link :to="{ path: '/vm/' + record.id}">{{ record.name }}
-                  </router-link>
-                </template>
-                <template #state="{ record }">
-                  <status :text="record.state" displayText></status>
-                </template>
-                <template #ip="{ record }">
-                  <div v-for="nic in record.nic" :key="nic.id">
-                    {{ nic.networkid === network.id ? nic.ipaddress : '' }}
-                  </div>
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.key === 'name'">
+                    <router-link :to="{ path: '/vm/' + record.id}">{{ record.name }}
+                    </router-link>
+                  </template>
+                  <template v-if="column.key === 'state'">
+                    <status :text="record.state" displayText></status>
+                  </template>
+                  <template v-if="column.key === 'ip'">
+                    <div v-for="nic in record.nic" :key="nic.id">
+                      {{ nic.networkid === network.id ? nic.ipaddress : '' }}
+                    </div>
+                  </template>
                 </template>
               </a-table>
               <a-divider/>
@@ -110,7 +112,7 @@
                 </template>
               </a-pagination>
             </a-collapse-panel>
-            <a-collapse-panel :header="$t('label.internal.lb')" key="ilb" :style="customStyle" :disabled="!showIlb(network)" >
+            <a-collapse-panel :header="$t('label.internal.lb')" key="ilb" :style="customStyle" :collapsible="!showIlb(network) ? 'disabled' : null" >
               <a-button
                 type="dashed"
                 style="margin-bottom: 15px; width: 100%"
@@ -127,9 +129,11 @@
                 :rowKey="item => item.id"
                 :pagination="false"
                 :loading="fetchLoading">
-                <template #name="{ record }">
-                  <router-link :to="{ path: '/ilb/'+ record.id}">{{ record.name }}
-                  </router-link>
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.key === 'name'">
+                    <router-link :to="{ path: '/ilb/'+ record.id}">{{ record.name }}
+                    </router-link>
+                  </template>
                 </template>
               </a-table>
               <a-divider/>
@@ -188,9 +192,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="item in networkOfferings" :key="item.id" :value="item.id">
+              <a-select-option v-for="item in networkOfferings" :key="item.id" :value="item.id" :label="item.displaytext || item.name || item.description">
                 {{ item.displaytext || item.name || item.description }}
               </a-select-option>
             </a-select>
@@ -252,9 +256,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="item in networkAclList" :key="item.id" :value="item.id">
+              <a-select-option v-for="item in networkAclList" :key="item.id" :value="item.id" :label="`${item.name}(${item.description})`">
                 <strong>{{ item.name }}</strong> ({{ item.description }})
               </a-select-option>
             </a-select>
@@ -316,9 +320,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="(key, idx) in Object.keys(algorithms)" :key="idx" :value="algorithms[key]">
+              <a-select-option v-for="(key, idx) in Object.keys(algorithms)" :key="idx" :value="algorithms[key]" :label="key">
                 {{ key }}
               </a-select-option>
             </a-select>
@@ -384,9 +388,9 @@
       },
       internalLbCols: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
           title: this.$t('label.sourceipaddress'),
@@ -401,58 +405,20 @@
           dataIndex: 'account'
         }
       ],
-      LBPublicIPCols: [
-        {
-          title: this.$t('label.ip'),
-          dataIndex: 'ipaddress',
-          slots: { customRender: 'ipaddress' }
-        },
-        {
-          title: this.$t('label.state'),
-          dataIndex: 'state'
-        },
-        {
-          title: this.$t('label.networkid'),
-          dataIndex: 'associatedNetworkName'
-        },
-        {
-          title: this.$t('label.vm'),
-          dataIndex: 'virtualmachinename'
-        }
-      ],
-      StaticNatCols: [
-        {
-          title: this.$t('label.ips'),
-          dataIndex: 'ipaddress',
-          slots: { customRender: 'ipaddress' }
-        },
-        {
-          title: this.$t('label.zoneid'),
-          dataIndex: 'zonename'
-        },
-        {
-          title: this.$t('label.networkid'),
-          dataIndex: 'associatedNetworkName'
-        },
-        {
-          title: this.$t('label.state'),
-          dataIndex: 'state'
-        }
-      ],
       vmCols: [
         {
+          key: 'name',
           title: this.$t('label.name'),
-          dataIndex: 'name',
-          slots: { customRender: 'name' }
+          dataIndex: 'name'
         },
         {
+          key: 'state',
           title: this.$t('label.state'),
-          dataIndex: 'state',
-          slots: { customRender: 'state' }
+          dataIndex: 'state'
         },
         {
-          title: this.$t('label.ip'),
-          slots: { customRender: 'ip' }
+          key: 'ip',
+          title: this.$t('label.ip')
         }
       ],
       customStyle: 'margin-bottom: 0; border: none',
diff --git a/ui/src/views/network/tungsten/FirewallPolicyTab.vue b/ui/src/views/network/tungsten/FirewallPolicyTab.vue
index f25997b..9be36fd 100644
--- a/ui/src/views/network/tungsten/FirewallPolicyTab.vue
+++ b/ui/src/views/network/tungsten/FirewallPolicyTab.vue
@@ -34,26 +34,28 @@
         :dataSource="dataSource"
         :rowKey="item => item.uuid"
         :pagination="false">
-        <template #name="{ text, record }">
+        <template #bodyCell="{ column, text, record }">
+          <template v-if="column.key === 'name'">
           <router-link :to="{ path: '/tungstenfirewallpolicy/' + record.uuid, query: { zoneid: zoneId } }">{{ text }}</router-link>
-        </template>
-        <template #firewallrule="{ record }">
-          <span v-if="record.firewallrule.length > 0">{{ record.firewallrule[0].name }}</span>
-        </template>
-        <template #action="{ record }">
-          <a-popconfirm
-            :title="$t('label.confirm.delete.tungsten.firewall.policy')"
-            @confirm="removeFirewallRule(record.uuid)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')">
-            <tooltip-button
-              tooltipPlacement="bottom"
-              :tooltip="$t('label.delete.tungsten.firewall.policy')"
-              danger
-              type="primary"
-              icon="delete-outlined"
-              :loading="deleteLoading" />
-          </a-popconfirm>
+          </template>
+          <template v-if="column.key === 'firewallrule'">
+            <span v-if="record.firewallrule.length > 0">{{ record.firewallrule[0].name }}</span>
+          </template>
+          <template v-if="column.key === 'actions'">
+            <a-popconfirm
+              :title="$t('label.confirm.delete.tungsten.firewall.policy')"
+              @confirm="removeFirewallRule(record.uuid)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')">
+              <tooltip-button
+                tooltipPlacement="bottom"
+                :tooltip="$t('label.delete.tungsten.firewall.policy')"
+                danger
+                type="primary"
+                icon="delete-outlined"
+                :loading="deleteLoading" />
+            </a-popconfirm>
+          </template>
         </template>
       </a-table>
       <div style="display: block; text-align: right; margin-top: 10px;">
@@ -146,14 +148,14 @@
       columns: [{
         title: this.$t('label.name'),
         dataIndex: 'name',
-        slots: { customRender: 'name' }
+        key: 'name'
       }, {
         title: this.$t('label.firewallrule'),
         dataIndex: 'firewallrule',
-        slots: { customRender: 'firewallrule' }
+        key: 'firewallrule'
       }, {
-        title: this.$t('label.action'),
-        slots: { customRender: 'action' },
+        title: this.$t('label.actions'),
+        key: 'actions',
         width: 80
       }],
       dataSource: [],
diff --git a/ui/src/views/network/tungsten/FirewallRuleTab.vue b/ui/src/views/network/tungsten/FirewallRuleTab.vue
index 7bfaa71..c19cdda 100644
--- a/ui/src/views/network/tungsten/FirewallRuleTab.vue
+++ b/ui/src/views/network/tungsten/FirewallRuleTab.vue
@@ -34,20 +34,22 @@
         :dataSource="dataSource"
         :rowKey="item => item.uuid"
         :pagination="false">
-        <template #actions="{ record }">
-          <a-popconfirm
-            :title="$t('message.confirm.remove.firewall.rule')"
-            @confirm="removeFirewallRule(record.uuid)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')">
-            <tooltip-button
-              tooltipPlacement="bottom"
-              :tooltip="$t('label.remove.firewall.rule')"
-              danger
-              type="primary"
-              icon="delete-outlined"
-              :loading="deleteLoading" />
-          </a-popconfirm>
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'actions'">
+            <a-popconfirm
+              :title="$t('message.confirm.remove.firewall.rule')"
+              @confirm="removeFirewallRule(record.uuid)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')">
+              <tooltip-button
+                tooltipPlacement="bottom"
+                :tooltip="$t('label.remove.firewall.rule')"
+                danger
+                type="primary"
+                icon="delete-outlined"
+                :loading="deleteLoading" />
+            </a-popconfirm>
+          </template>
         </template>
       </a-table>
       <div style="display: block; text-align: right; margin-top: 10px;">
@@ -96,9 +98,9 @@
             <a-select
               v-model:value="form.action"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.action.description">
               <a-select-option value="pass">PASS</a-select-option>
@@ -114,11 +116,14 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="serviceGroup.loading"
               :placeholder="apiParams.servicegroupuuid.description">
-              <a-select-option v-for="group in serviceGroup.opts" :key="group.uuid">
+              <a-select-option
+                v-for="group in serviceGroup.opts"
+                :key="group.uuid"
+                :label="group.name || group.description || group.displaytext">
                 {{ group.name || group.description || group.displaytext }}
               </a-select-option>
             </a-select>
@@ -142,11 +147,14 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="srcTag.loading"
               :placeholder="apiParams.srctaguuid.description">
-              <a-select-option v-for="tag in srcTag.opts" :key="tag.uuid">
+              <a-select-option
+                v-for="tag in srcTag.opts"
+                :key="tag.uuid"
+                :label="tag.name || tag.description || tag.displaytext">
                 {{ tag.name || tag.description || tag.displaytext }}
               </a-select-option>
             </a-select>
@@ -160,11 +168,14 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="srcAddress.loading"
               :placeholder="apiParams.srcaddressgroupuuid.description">
-              <a-select-option v-for="address in srcAddress.opts" :key="address.uuid">
+              <a-select-option
+                v-for="address in srcAddress.opts"
+                :key="address.uuid"
+                :label="address.name || address.description || address.displaytext">
                 {{ address.name || address.description || address.displaytext }}
               </a-select-option>
             </a-select>
@@ -178,11 +189,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="networks.loading"
               :placeholder="apiParams.srcnetworkuuid.description">
-              <a-select-option v-for="network in networks.opts" :key="network.uuid">
+              <a-select-option v-for="network in networks.opts" :key="network.uuid" :label="network.name || network.description || network.displaytext">
                 {{ network.name || network.description || network.displaytext }}
               </a-select-option>
             </a-select>
@@ -194,9 +205,9 @@
             <a-select
               v-model:value="form.direction"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :placeholder="apiParams.direction.description">
               <a-select-option value="oneway">ONE WAY</a-select-option>
@@ -222,11 +233,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="srcTag.loading"
               :placeholder="apiParams.desttaguuid.description">
-              <a-select-option v-for="tag in srcTag.opts" :key="tag.uuid">
+              <a-select-option v-for="tag in srcTag.opts" :key="tag.uuid" :label="tag.name || tag.description || tag.displaytext">
                 {{ tag.name || tag.description || tag.displaytext }}
               </a-select-option>
             </a-select>
@@ -240,11 +251,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="srcAddress.loading"
               :placeholder="apiParams.destaddressgroupuuid.description">
-              <a-select-option v-for="address in srcAddress.opts" :key="address.uuid">
+              <a-select-option v-for="address in srcAddress.opts" :key="address.uuid" :label="address.name || address.description || address.displaytext">
                 {{ address.name || address.description || address.displaytext }}
               </a-select-option>
             </a-select>
@@ -258,11 +269,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="networks.loading"
               :placeholder="apiParams.destnetworkuuid.description">
-              <a-select-option v-for="network in networks.opts" :key="network.uuid">
+              <a-select-option v-for="network in networks.opts" :key="network.uuid" :label="network.name || network.description || network.displaytext">
                 {{ network.name || network.description || network.displaytext }}
               </a-select-option>
             </a-select>
@@ -276,11 +287,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               :loading="tagType.loading"
               :placeholder="apiParams.tagtypeuuid.description">
-              <a-select-option v-for="tag in tagType.opts" :key="tag.uuid">
+              <a-select-option v-for="tag in tagType.opts" :key="tag.uuid" :label="tag.name || tag.description || tag.displaytext">
                 {{ tag.name || tag.description || tag.displaytext }}
               </a-select-option>
             </a-select>
@@ -357,7 +368,7 @@
         dataIndex: 'tagtype'
       }, {
         title: this.$t('label.actions'),
-        slots: { customRender: 'actions' },
+        key: 'actions',
         width: 80
       }],
       dataSource: [],
diff --git a/ui/src/views/network/tungsten/FirewallTagTab.vue b/ui/src/views/network/tungsten/FirewallTagTab.vue
index 8373130..b1e08e9 100644
--- a/ui/src/views/network/tungsten/FirewallTagTab.vue
+++ b/ui/src/views/network/tungsten/FirewallTagTab.vue
@@ -34,20 +34,22 @@
         :dataSource="dataSource"
         :rowKey="item => item.uuid"
         :pagination="false">
-        <template #action="{ record }">
-          <a-popconfirm
-            :title="$t('message.delete.tungsten.tag')"
-            @confirm="removeTag(record.uuid)"
-            :okText="$t('label.yes')"
-            :cancelText="$t('label.no')">
-            <tooltip-button
-              tooltipPlacement="bottom"
-              :tooltip="$t('label.remove.tag')"
-              danger
-              type="primary"
-              icon="delete-outlined"
-              :loading="deleteLoading" />
-          </a-popconfirm>
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'actions'">
+            <a-popconfirm
+              :title="$t('message.delete.tungsten.tag')"
+              @confirm="removeTag(record.uuid)"
+              :okText="$t('label.yes')"
+              :cancelText="$t('label.no')">
+              <tooltip-button
+                tooltipPlacement="bottom"
+                :tooltip="$t('label.remove.tag')"
+                danger
+                type="primary"
+                icon="delete-outlined"
+                :loading="deleteLoading" />
+            </a-popconfirm>
+          </template>
         </template>
       </a-table>
       <a-divider/>
@@ -89,11 +91,11 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }"
               v-model:value="form.taguuid"
               :placeholder="apiParams.taguuid.description">
-              <a-select-option v-for="opt in tagSrc.opts" :key="opt.uuid">{{ opt.name }}</a-select-option>
+              <a-select-option v-for="opt in tagSrc.opts" :key="opt.uuid" :label="opt.name">{{ opt.name }}</a-select-option>
             </a-select>
           </a-form-item>
 
@@ -141,8 +143,8 @@
         title: this.$t('label.name'),
         dataIndex: 'name'
       }, {
-        title: this.$t('label.action'),
-        slots: { customRender: 'action' },
+        title: this.$t('label.actions'),
+        key: 'actions',
         width: 80
       }],
       page: 1,
diff --git a/ui/src/views/network/tungsten/LogicalRouterTab.vue b/ui/src/views/network/tungsten/LogicalRouterTab.vue
index 60b3cfc..5334d5b 100644
--- a/ui/src/views/network/tungsten/LogicalRouterTab.vue
+++ b/ui/src/views/network/tungsten/LogicalRouterTab.vue
@@ -110,9 +110,9 @@
         title: this.$t('label.name'),
         dataIndex: 'name'
       }, {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        slots: { customRender: 'action' },
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
+        key: 'actions',
         width: 80
       }],
       dataSource: [],
diff --git a/ui/src/views/network/tungsten/NetworkPolicyTab.vue b/ui/src/views/network/tungsten/NetworkPolicyTab.vue
index 91e27e7..b1970d0 100644
--- a/ui/src/views/network/tungsten/NetworkPolicyTab.vue
+++ b/ui/src/views/network/tungsten/NetworkPolicyTab.vue
@@ -32,22 +32,24 @@
       :dataSource="dataSource"
       :rowKey="(item, index) => index"
       :pagination="false">
-      <template #action="{ record }">
-        <a-popconfirm
-          v-if="'removeTungstenFabricPolicy' in $store.getters.apis"
-          placement="topRight"
-          :title="$t('message.confirm.remove.network.policy')"
-          :ok-text="$t('label.yes')"
-          :cancel-text="$t('label.no')"
-          :loading="deleteLoading"
-          @confirm="removeNetworkPolicy(record)"
-        >
-          <tooltip-button
-            :tooltip="$t('label.action.remove.network.policy')"
-            danger
-            type="primary"
-            icon="delete-outlined" />
-        </a-popconfirm>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'actions'">
+          <a-popconfirm
+            v-if="'removeTungstenFabricPolicy' in $store.getters.apis"
+            placement="topRight"
+            :title="$t('message.confirm.remove.network.policy')"
+            :ok-text="$t('label.yes')"
+            :cancel-text="$t('label.no')"
+            :loading="deleteLoading"
+            @confirm="removeNetworkPolicy(record)"
+          >
+            <tooltip-button
+              :tooltip="$t('label.action.remove.network.policy')"
+              danger
+              type="primary"
+              icon="delete-outlined" />
+          </a-popconfirm>
+        </template>
       </template>
     </a-table>
     <div style="display: block; text-align: right; margin-top: 10px;">
@@ -143,9 +145,9 @@
         title: this.$t('label.name'),
         dataIndex: 'name'
       }, {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        slots: { customRender: 'action' },
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
+        key: 'actions',
         width: 80
       }],
       dataSource: [],
diff --git a/ui/src/views/network/tungsten/TungstenFabric.vue b/ui/src/views/network/tungsten/TungstenFabric.vue
index 066e32b..25ebf85 100644
--- a/ui/src/views/network/tungsten/TungstenFabric.vue
+++ b/ui/src/views/network/tungsten/TungstenFabric.vue
@@ -87,16 +87,16 @@
             {
               dataIndex: 'name',
               title: this.$t('label.name'),
-              slots: { customRender: 'name' }
+              key: 'name'
             },
             {
               dataIndex: 'network',
               title: this.$t('label.network'),
-              slots: { customRender: 'network' }
+              key: 'network'
             },
             {
-              title: this.$t('label.action'),
-              slots: { customRender: 'action' },
+              title: this.$t('label.actions'),
+              key: 'actions',
               width: 150
             }
           ]
@@ -135,21 +135,21 @@
             {
               dataIndex: 'name',
               title: this.$t('label.name'),
-              slots: { customRender: 'name' }
+              key: 'name'
             },
             {
               dataIndex: 'firewallpolicy',
               title: this.$t('label.firewallpolicy'),
-              slots: { customRender: 'firewallpolicy' }
+              key: 'firewallpolicy'
             },
             {
               dataIndex: 'tag',
               title: this.$t('label.tag'),
-              slots: { customRender: 'tag' }
+              key: 'tag'
             },
             {
-              title: this.$t('label.action'),
-              slots: { customRender: 'action' },
+              title: this.$t('label.actions'),
+              key: 'actions',
               width: 150
             }
           ]
@@ -206,14 +206,14 @@
           columns: [{
             dataIndex: 'name',
             title: this.$t('label.name'),
-            slots: { customRender: 'name' }
+            key: 'name'
           }, {
             dataIndex: 'network',
             title: this.$t('label.network'),
-            slots: { customRender: 'network' }
+            key: 'network'
           }, {
-            title: this.$t('label.action'),
-            slots: { customRender: 'action' },
+            title: this.$t('label.actions'),
+            key: 'actions',
             width: 150
           }]
         },
@@ -298,11 +298,11 @@
             {
               dataIndex: 'name',
               title: this.$t('label.name'),
-              slots: { customRender: 'name' }
+              key: 'name'
             },
             {
-              title: this.$t('label.action'),
-              slots: { customRender: 'action' },
+              title: this.$t('label.actions'),
+              key: 'actions',
               width: 150
             }
           ]
@@ -338,10 +338,10 @@
           columns: [{
             dataIndex: 'name',
             title: this.$t('label.name'),
-            slots: { customRender: 'name' }
+            key: 'name'
           }, {
-            title: this.$t('label.action'),
-            slots: { customRender: 'action' },
+            title: this.$t('label.actions'),
+            key: 'actions',
             width: 150
           }]
         },
@@ -411,26 +411,26 @@
             {
               dataIndex: 'name',
               title: this.$t('label.name'),
-              slots: { customRender: 'name' }
+              key: 'name'
             },
             {
               dataIndex: 'protocol',
               title: this.$t('label.protocol'),
-              slots: { customRender: 'protocol' }
+              key: 'protocol'
             },
             {
               dataIndex: 'startport',
               title: this.$t('label.startport'),
-              slots: { customRender: 'startport' }
+              key: 'startport'
             },
             {
               dataIndex: 'endport',
               title: this.$t('label.endport'),
-              slots: { customRender: 'endport' }
+              key: 'endport'
             },
             {
-              title: this.$t('label.action'),
-              slots: { customRender: 'action' },
+              title: this.$t('label.actions'),
+              key: 'actions',
               width: 150
             }
           ]
@@ -476,21 +476,21 @@
             {
               dataIndex: 'name',
               title: this.$t('label.name'),
-              slots: { customRender: 'name' }
+              key: 'name'
             },
             {
               dataIndex: 'ipprefix',
               title: this.$t('label.ipprefix'),
-              slots: { customRender: 'ipprefix' }
+              key: 'ipprefix'
             },
             {
               dataIndex: 'ipprefixlen',
               title: this.$t('label.ipprefixlen'),
-              slots: { customRender: 'ipprefixlen' }
+              key: 'ipprefixlen'
             },
             {
-              title: this.$t('label.action'),
-              slots: { customRender: 'action' },
+              title: this.$t('label.actions'),
+              key: 'actions',
               width: 150
             }
           ]
diff --git a/ui/src/views/network/tungsten/TungstenFabricPolicyRule.vue b/ui/src/views/network/tungsten/TungstenFabricPolicyRule.vue
index 0b0b3d5..c842224 100644
--- a/ui/src/views/network/tungsten/TungstenFabricPolicyRule.vue
+++ b/ui/src/views/network/tungsten/TungstenFabricPolicyRule.vue
@@ -32,28 +32,30 @@
       :dataSource="dataSource"
       :rowKey="(item, index) => index"
       :pagination="false">
-      <template #sourceport="{ record }">
-        <span>{{ record.srcstartport + ':' + record.srcendport }}</span>
-      </template>
-      <template #destport="{ record }">
-        <span>{{ record.deststartport + ':' + record.destendport }}</span>
-      </template>
-      <template #ruleAction="{ record }">
-        <a-popconfirm
-          v-if="'removeTungstenFabricPolicyRule' in $store.getters.apis"
-          placement="topRight"
-          :title="$t('message.delete.tungsten.policy.rule')"
-          :ok-text="$t('label.yes')"
-          :cancel-text="$t('label.no')"
-          :loading="deleteLoading"
-          @confirm="deleteRule(record)"
-        >
-          <tooltip-button
-            :tooltip="$t('label.delete.rule')"
-            danger
-            type="primary"
-            icon="delete-outlined" />
-        </a-popconfirm>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'sourceport'">
+          <span>{{ record.srcstartport + ':' + record.srcendport }}</span>
+        </template>
+        <template v-if="column.key === 'destport'">
+          <span>{{ record.deststartport + ':' + record.destendport }}</span>
+        </template>
+        <template v-if="column.key === 'ruleAction'">
+          <a-popconfirm
+            v-if="'removeTungstenFabricPolicyRule' in $store.getters.apis"
+            placement="topRight"
+            :title="$t('message.delete.tungsten.policy.rule')"
+            :ok-text="$t('label.yes')"
+            :cancel-text="$t('label.no')"
+            :loading="deleteLoading"
+            @confirm="deleteRule(record)"
+          >
+            <tooltip-button
+              :tooltip="$t('label.delete.rule')"
+              danger
+              type="primary"
+              icon="delete-outlined" />
+          </a-popconfirm>
+        </template>
       </template>
     </a-table>
 
@@ -247,43 +249,43 @@
       pageSize: this.$store.getters.defaultListViewPageSize,
       columns: [
         {
-          title: this.$t('label.action'),
-          dataIndex: 'action',
-          slots: { customRender: 'action' }
+          title: this.$t('label.actions'),
+          dataIndex: 'actions',
+          key: 'actions'
         },
         {
           title: this.$t('label.direction'),
           dataIndex: 'direction',
-          slots: { customRender: 'direction' }
+          key: 'direction'
         },
         {
           title: this.$t('label.protocol'),
           dataIndex: 'protocol',
-          slots: { customRender: 'protocol' }
+          key: 'protocol'
         },
         {
           title: this.$t('label.srcnetwork'),
           dataIndex: 'srcnetwork',
-          slots: { customRender: 'srcnetwork' }
+          key: 'srcnetwork'
         },
         {
           title: this.$t('label.sourceport'),
           dataIndex: 'sourceport',
-          slots: { customRender: 'sourceport' }
+          key: 'sourceport'
         },
         {
           title: this.$t('label.destnetwork'),
           dataIndex: 'destnetwork',
-          slots: { customRender: 'destnetwork' }
+          key: 'destnetwork'
         },
         {
           title: this.$t('label.destport'),
           dataIndex: 'destport',
-          slots: { customRender: 'destport' }
+          key: 'destport'
         },
         {
           dataIndex: 'ruleAction',
-          slots: { customRender: 'ruleAction' },
+          key: 'ruleAction',
           width: 50
         }
       ]
diff --git a/ui/src/views/network/tungsten/TungstenFabricPolicyTag.vue b/ui/src/views/network/tungsten/TungstenFabricPolicyTag.vue
index d4e14cf..502279a 100644
--- a/ui/src/views/network/tungsten/TungstenFabricPolicyTag.vue
+++ b/ui/src/views/network/tungsten/TungstenFabricPolicyTag.vue
@@ -32,25 +32,27 @@
       :dataSource="dataSource"
       :rowKey="(item, index) => index"
       :pagination="false">
-      <template #policy="{ record }">
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'policy'">
         <span v-if="record.policy.length > 0">{{ record.policy[0].name }}</span>
-      </template>
-      <template #action="{ record }">
-        <a-popconfirm
-          v-if="'removeTungstenFabricTag' in $store.getters.apis"
-          placement="topRight"
-          :title="$t('message.delete.tungsten.tag')"
-          :ok-text="$t('label.yes')"
-          :cancel-text="$t('label.no')"
-          :loading="deleteLoading"
-          @confirm="deleteRule(record)"
-        >
-          <tooltip-button
-            :tooltip="$t('label.delete.tag')"
-            danger
-            type="primary"
-            icon="delete-outlined" />
-        </a-popconfirm>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <a-popconfirm
+            v-if="'removeTungstenFabricTag' in $store.getters.apis"
+            placement="topRight"
+            :title="$t('message.delete.tungsten.tag')"
+            :ok-text="$t('label.yes')"
+            :cancel-text="$t('label.no')"
+            :loading="deleteLoading"
+            @confirm="deleteRule(record)"
+          >
+            <tooltip-button
+              :tooltip="$t('label.delete.tag')"
+              danger
+              type="primary"
+              icon="delete-outlined" />
+          </a-popconfirm>
+        </template>
       </template>
     </a-table>
 
@@ -147,17 +149,17 @@
         {
           title: this.$t('label.name'),
           dataIndex: 'name',
-          slots: { customRender: 'name' }
+          key: 'name'
         },
         {
           title: this.$t('label.policy'),
           dataIndex: 'policy',
-          slots: { customRender: 'policy' }
+          key: 'policy'
         },
         {
-          title: this.$t('label.action'),
-          dataIndex: 'action',
-          slots: { customRender: 'action' },
+          title: this.$t('label.actions'),
+          dataIndex: 'actions',
+          key: 'actions',
           width: 70
         }
       ]
diff --git a/ui/src/views/network/tungsten/TungstenNetworkTable.vue b/ui/src/views/network/tungsten/TungstenNetworkTable.vue
index 7bb0e90..bed85f8 100644
--- a/ui/src/views/network/tungsten/TungstenNetworkTable.vue
+++ b/ui/src/views/network/tungsten/TungstenNetworkTable.vue
@@ -25,52 +25,49 @@
       :pagination="false"
       :rowKey="(record, idx) => record.id || record.name || idx + '-' + Math.random()"
       :scroll="{ y: 350 }">
-      <template #name="{ text, record }">
-        <!-- <QuickView
-          :actions="actions"
-          :enabled="true"
-          :resource="record"
-          @exec-action="(action) => execAction(action, record)"/> -->
+      <template #bodyCell="{ column, text, record }">
+        <template v-if="column.key === 'name'">
         <router-link v-if="apiName === 'listTungstenFabricPolicy'" :to="{ path: '/tungstenpolicy/' + record.uuid, query: { zoneid: resource.zoneid } }" >{{ text }}</router-link>
         <router-link v-else-if="apiName === 'listTungstenFabricApplicationPolicySet'" :to="{ path: '/tungstenpolicyset/' + record.uuid, query: { zoneid: resource.zoneid } }" >{{ text }}</router-link>
         <span v-else>{{ text }}</span>
-      </template>
-      <template #tungstenvms="{ record }">
-        <ul v-if="record.tungstenvms.length > 0"><li v-for="item in record.tungstenvms" :key="item.uuid">{{ item.name }}</li></ul>
-      </template>
-      <template #network="{ record }">
-        <ul v-if="record.network.length > 0"><li v-for="item in record.network" :key="item.uuid"><span v-if="item.name">{{ item.name }}</span></li></ul>
-      </template>
-      <template #firewallpolicy="{ record }">
-        <span v-if="record.firewallpolicy.length > 0">{{ record.firewallpolicy.map(item => item.name).join(',') }}</span>
-      </template>
-      <template #firewallrule="{ record }">
-        <span v-if="record.firewallrule.length > 0">{{ record.firewallrule[0].name }}</span>
-      </template>
-      <template #tungstenroutingpolicyterm="{ record }">
-        <span v-if="record.tungstenroutingpolicyterm.length > 0">{{ record.tungstenroutingpolicyterm[0].name }}</span>
-      </template>
-      <template #vm="{ record }">
-        <ul v-if="record.vm.length > 0"><li v-for="item in record.vm" :key="item.uuid">{{ item.name }}</li></ul>
-      </template>
-      <template #nic="{ record }">
-        <ul v-if="record.nic.length > 0"><li v-for="item in record.nic" :key="item.uuid">{{ item.name }}</li></ul>
-      </template>
-      <template #tag="{ record }">
-        <div class="tags" v-for="tag in record.tag" :key="tag.uuid">
-          <a-tag :key="tag.uuid">{{ tag.name }}</a-tag>
-        </div>
-      </template>
-      <template #action="{ record }">
-        <span v-for="(action, index) in actions" :key="index" style="margin-right: 5px">
-          <tooltip-button
-            v-if="action.dataView && ('show' in action ? action.show(record, $store.getters) : true)"
-            :tooltip="$t(action.label)"
-            :danger="['delete-outlined', 'DeleteOutlined'].includes(action.icon)"
-            :type="(['DeleteOutlined', 'delete-outlined'].includes(action.icon) ? 'primary' : 'default')"
-            :icon="action.icon"
-            @click="() => execAction(action, record)" />
-        </span>
+        </template>
+        <template v-if="column.key === 'tungstenvms'">
+          <ul v-if="record.tungstenvms.length > 0"><li v-for="item in record.tungstenvms" :key="item.uuid">{{ item.name }}</li></ul>
+        </template>
+        <template v-if="column.key === 'network'">
+          <ul v-if="record.network.length > 0"><li v-for="item in record.network" :key="item.uuid"><span v-if="item.name">{{ item.name }}</span></li></ul>
+        </template>
+        <template v-if="column.key === 'firewallpolicy'">
+          <span v-if="record.firewallpolicy.length > 0">{{ record.firewallpolicy.map(item => item.name).join(',') }}</span>
+        </template>
+        <template v-if="column.key === 'firewallrule'">
+          <span v-if="record.firewallrule.length > 0">{{ record.firewallrule[0].name }}</span>
+        </template>
+        <template v-if="column.key === 'tungstenroutingpolicyterm'">
+          <span v-if="record.tungstenroutingpolicyterm.length > 0">{{ record.tungstenroutingpolicyterm[0].name }}</span>
+        </template>
+        <template v-if="column.key === 'vm'">
+          <ul v-if="record.vm.length > 0"><li v-for="item in record.vm" :key="item.uuid">{{ item.name }}</li></ul>
+        </template>
+        <template v-if="column.key === 'nic'">
+          <ul v-if="record.nic.length > 0"><li v-for="item in record.nic" :key="item.uuid">{{ item.name }}</li></ul>
+        </template>
+        <template v-if="column.key === 'tag'">
+          <div class="tags" v-for="tag in record.tag" :key="tag.uuid">
+            <a-tag :key="tag.uuid">{{ tag.name }}</a-tag>
+          </div>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <span v-for="(action, index) in actions" :key="index" style="margin-right: 5px">
+            <tooltip-button
+              v-if="action.dataView && ('show' in action ? action.show(record, $store.getters) : true)"
+              :tooltip="$t(action.label)"
+              :danger="['delete-outlined', 'DeleteOutlined'].includes(action.icon)"
+              :type="(['DeleteOutlined', 'delete-outlined'].includes(action.icon) ? 'primary' : 'default')"
+              :icon="action.icon"
+              @click="() => execAction(action, record)" />
+          </span>
+        </template>
       </template>
     </a-table>
     <a-pagination
diff --git a/ui/src/views/offering/AddComputeOffering.vue b/ui/src/views/offering/AddComputeOffering.vue
index ae8e07e..5cd68c5 100644
--- a/ui/src/views/offering/AddComputeOffering.vue
+++ b/ui/src/views/offering/AddComputeOffering.vue
@@ -51,12 +51,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="apiParams.systemvmtype.description">
-            <a-select-option key="domainrouter">{{ $t('label.domain.router') }}</a-select-option>
-            <a-select-option key="consoleproxy">{{ $t('label.console.proxy') }}</a-select-option>
-            <a-select-option key="secondarystoragevm">{{ $t('label.secondary.storage.vm') }}</a-select-option>
+            <a-select-option key="domainrouter" :label="$t('label.domain.router')">{{ $t('label.domain.router') }}</a-select-option>
+            <a-select-option key="consoleproxy" :label="$t('label.console.proxy')">{{ $t('label.console.proxy') }}</a-select-option>
+            <a-select-option key="secondarystoragevm" :label="$t('label.secondary.storage.vm')">{{ $t('label.secondary.storage.vm') }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item name="offeringtype" ref="offeringtype" :label="$t('label.offeringtype')" v-show="!isSystem">
@@ -218,12 +218,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="deploymentPlannerLoading"
             :placeholder="apiParams.deploymentplanner.description"
             @change="val => { handleDeploymentPlannerChange(val) }">
-            <a-select-option v-for="(opt) in deploymentPlanners" :key="opt.name">
+            <a-select-option v-for="(opt) in deploymentPlanners" :key="opt.name" :label="opt.name || opt.description || ''">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -259,10 +259,10 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="$t('label.vgputype')">
-            <a-select-option v-for="(opt, optIndex) in vGpuTypes" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in vGpuTypes" :key="optIndex" :label="opt">
               {{ opt }}
             </a-select-option>
           </a-select>
@@ -331,9 +331,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="policy in storagePolicies" :key="policy.id">
+            <a-select-option v-for="policy in storagePolicies" :key="policy.id" :label="policy.name || policy.id || ''">
               {{ policy.name || policy.id }}
             </a-select-option>
           </a-select>
@@ -516,9 +516,9 @@
                     mode="tags"
                     v-model:value="form.storagetags"
                     showSearch
-                    optionFilterProp="label"
+                    optionFilterProp="value"
                     :filterOption="(input, option) => {
-                      return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                      return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }"
                     :loading="storageTagLoading"
                     :placeholder="apiParams.tags.description"
@@ -707,7 +707,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.required.input') }],
-        displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
         cpunumber: [
           { required: true, message: this.$t('message.error.required.input') },
           this.naturalNumberRule
diff --git a/ui/src/views/offering/AddDiskOffering.vue b/ui/src/views/offering/AddDiskOffering.vue
index 880eae5..576721d 100644
--- a/ui/src/views/offering/AddDiskOffering.vue
+++ b/ui/src/views/offering/AddDiskOffering.vue
@@ -205,9 +205,9 @@
             mode="tags"
             v-model:value="form.tags"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children?.[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="storageTagLoading"
             :placeholder="apiParams.tags.description"
@@ -278,9 +278,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="policy in storagePolicies" :key="policy.id">
+            <a-select-option v-for="policy in storagePolicies" :key="policy.id" :label="policy.name || policy.id || ''">
               {{ policy.name || policy.id }}
             </a-select-option>
           </a-select>
@@ -357,7 +357,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.required.input') }],
-        displaytext: [{ required: true, message: this.$t('message.error.required.input') }],
         disksize: [
           { required: true, message: this.$t('message.error.required.input') },
           { type: 'number', validator: this.validateNumber }
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index abdb9da..17359e4 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -252,11 +252,11 @@
             optionFilterProp="label"
             v-model:value="form.serviceofferingid"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="serviceOfferingLoading"
             :placeholder="apiParams.serviceofferingid.description">
-            <a-select-option v-for="(opt) in serviceOfferings" :key="opt.id">
+            <a-select-option v-for="(opt) in serviceOfferings" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -320,11 +320,11 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="registeredServicePackageLoading"
             :placeholder="$t('label.service.lb.netscaler.servicepackages')">
-            <a-select-option v-for="(opt, optIndex) in registeredServicePackages" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in registeredServicePackages" :key="optIndex" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -577,7 +577,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.name') }],
-        displaytext: [{ required: true, message: this.$t('message.error.description') }],
         networkrate: [{ type: 'number', validator: this.validateNumber }],
         serviceofferingid: [{ required: true, message: this.$t('message.error.select') }],
         domainid: [{ type: 'array', required: true, message: this.$t('message.error.select') }],
diff --git a/ui/src/views/offering/AddVpcOffering.vue b/ui/src/views/offering/AddVpcOffering.vue
index 9b9d576..738a03a 100644
--- a/ui/src/views/offering/AddVpcOffering.vue
+++ b/ui/src/views/offering/AddVpcOffering.vue
@@ -108,11 +108,11 @@
             optionFilterProp="label"
             v-model:value="form.serviceofferingid"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="serviceOfferingLoading"
             :placeholder="apiParams.serviceofferingid.description">
-            <a-select-option v-for="(opt) in serviceOfferings" :key="opt.id">
+            <a-select-option v-for="(opt) in serviceOfferings" :key="opt.id" :label="opt.name || opt.description">
               {{ opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -245,7 +245,6 @@
       })
       this.rules = reactive({
         name: [{ required: true, message: this.$t('message.error.name') }],
-        displaytext: [{ required: true, message: this.$t('message.error.description') }],
         domainid: [{ type: 'array', required: true, message: this.$t('message.error.select') }],
         zoneid: [{
           type: 'array',
diff --git a/ui/src/views/offering/ImportBackupOffering.vue b/ui/src/views/offering/ImportBackupOffering.vue
index a8becc9..9b8e9d6 100644
--- a/ui/src/views/offering/ImportBackupOffering.vue
+++ b/ui/src/views/offering/ImportBackupOffering.vue
@@ -72,9 +72,9 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option v-for="opt in externals.opts" :key="opt.id">
+          <a-select-option v-for="opt in externals.opts" :key="opt.id" :label="opt.name">
             {{ opt.name }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/plugins/quota/QuotaBalance.vue b/ui/src/views/plugins/quota/QuotaBalance.vue
index ce8f28d..ab81615 100644
--- a/ui/src/views/plugins/quota/QuotaBalance.vue
+++ b/ui/src/views/plugins/quota/QuotaBalance.vue
@@ -26,11 +26,13 @@
       :pagination="false"
       :scroll="{ y: '55vh' }"
     >
-      <template #quota="{ text }">
-        <span v-if="text!==null">{{ `${currency} ${text}` }}</span>
-      </template>
-      <template #credit="{ text }">
-        <span v-if="text!==null">{{ `${currency} ${text}` }}</span>
+      <template #bodyCell="{ column, text }">
+        <template v-if="column.key === 'quota'">
+          <span v-if="text!==null">{{ `${currency} ${text}` }}</span>
+        </template>
+        <template v-if="column.key === 'credit'">
+          <span v-if="text!==null">{{ `${currency} ${text}` }}</span>
+        </template>
       </template>
     </a-table>
   </div>
@@ -65,22 +67,22 @@
     columns () {
       return [
         {
+          key: 'date',
           title: this.$t('label.date'),
           dataIndex: 'date',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'date' }
+          width: 'calc(100% / 3)'
         },
         {
+          key: 'quota',
           title: this.$t('label.quota.value'),
           dataIndex: 'quota',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'quota' }
+          width: 'calc(100% / 3)'
         },
         {
+          key: 'credit',
           title: this.$t('label.credit'),
           dataIndex: 'credit',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'credit' }
+          width: 'calc(100% / 3)'
         }
       ]
     }
diff --git a/ui/src/views/plugins/quota/QuotaUsage.vue b/ui/src/views/plugins/quota/QuotaUsage.vue
index f8e339f..04a5f4f 100644
--- a/ui/src/views/plugins/quota/QuotaUsage.vue
+++ b/ui/src/views/plugins/quota/QuotaUsage.vue
@@ -26,8 +26,10 @@
       :pagination="false"
       :scroll="{ y: '55vh' }"
     >
-      <template #quota="{ text }">
-        <span v-if="text!==undefined">{{ `${currency} ${text}` }}</span>
+      <template #bodyCell="{ column, text }">
+        <template v-if="column.key === 'quota'">
+          <span v-if="text!==undefined">{{ `${currency} ${text}` }}</span>
+        </template>
       </template>
     </a-table>
   </div>
@@ -63,22 +65,22 @@
     columns () {
       return [
         {
+          key: 'name',
           title: this.$t('label.quota.type.name'),
           dataIndex: 'name',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'name' }
+          width: 'calc(100% / 3)'
         },
         {
+          key: 'unit',
           title: this.$t('label.quota.type.unit'),
           dataIndex: 'unit',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'unit' }
+          width: 'calc(100% / 3)'
         },
         {
+          key: 'quota',
           title: this.$t('label.quota.usage'),
           dataIndex: 'quota',
-          width: 'calc(100% / 3)',
-          slots: { customRender: 'quota' }
+          width: 'calc(100% / 3)'
         }
       ]
     }
diff --git a/ui/src/views/project/AccountsTab.vue b/ui/src/views/project/AccountsTab.vue
index fd3408b..8c7282b 100644
--- a/ui/src/views/project/AccountsTab.vue
+++ b/ui/src/views/project/AccountsTab.vue
@@ -26,42 +26,44 @@
           :dataSource="dataSource"
           :pagination="false"
           :rowKey="rowItem => rowItem.userid ? rowItem.userid : (rowItem.accountid || rowItem.account)">
-          <template #user="{ record }">
-            <span v-if="record.userid">{{ record.username }}</span>
-          </template>
-          <template #projectrole="{ record }">
-            <span v-if="record.projectroleid">{{ getProjectRole(record) }}</span>
-          </template>
-          <template #action="{ record }">
-            <div>
-              <span v-if="imProjectAdmin && dataSource.length > 1" class="account-button-action">
-                <tooltip-button
-                  tooltipPlacement="top"
-                  :tooltip="record.userid ? $t('label.make.user.project.owner') : $t('label.make.project.owner')"
-                  v-if="record.role !== owner"
-                  type="default"
-                  icon="arrow-up-outlined"
-                  size="small"
-                  @onClick="promoteAccount(record)" />
-                <tooltip-button
-                  tooltipPlacement="top"
-                  :tooltip="record.userid ? $t('label.demote.project.owner.user') : $t('label.demote.project.owner')"
-                  v-if="updateProjectApi.params.filter(x => x.name === 'swapowner').length > 0 && record.role === owner"
-                  type="default"
-                  icon="arrow-down-outlined"
-                  size="small"
-                  @onClick="demoteAccount(record)" />
-                <tooltip-button
-                  tooltipPlacement="top"
-                  :tooltip="record.userid ? $t('label.remove.project.user') : $t('label.remove.project.account')"
-                  type="primary"
-                  :danger="true"
-                  icon="delete-outlined"
-                  size="small"
-                  :disabled="!('deleteAccountFromProject' in $store.getters.apis)"
-                  @onClick="onShowConfirmDelete(record)" />
-              </span>
-            </div>
+          <template #bodyCell="{ column, record }">
+            <template v-if="column.key === 'user'">
+              <span v-if="record.userid">{{ record.username }}</span>
+            </template>
+            <template v-if="column.key === 'projectrole'">
+              <span v-if="record.projectroleid">{{ getProjectRole(record) }}</span>
+            </template>
+            <template v-if="column.key === 'actions'">
+              <div>
+                <span v-if="imProjectAdmin && dataSource.length > 1" class="account-button-action">
+                  <tooltip-button
+                    tooltipPlacement="top"
+                    :tooltip="record.userid ? $t('label.make.user.project.owner') : $t('label.make.project.owner')"
+                    v-if="record.role !== owner"
+                    type="default"
+                    icon="arrow-up-outlined"
+                    size="small"
+                    @onClick="promoteAccount(record)" />
+                  <tooltip-button
+                    tooltipPlacement="top"
+                    :tooltip="record.userid ? $t('label.demote.project.owner.user') : $t('label.demote.project.owner')"
+                    v-if="updateProjectApi.params.filter(x => x.name === 'swapowner').length > 0 && record.role === owner"
+                    type="default"
+                    icon="arrow-down-outlined"
+                    size="small"
+                    @onClick="demoteAccount(record)" />
+                  <tooltip-button
+                    tooltipPlacement="top"
+                    :tooltip="record.userid ? $t('label.remove.project.user') : $t('label.remove.project.account')"
+                    type="primary"
+                    :danger="true"
+                    icon="delete-outlined"
+                    size="small"
+                    :disabled="!('deleteAccountFromProject' in $store.getters.apis)"
+                    @onClick="onShowConfirmDelete(record)" />
+                </span>
+              </div>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -122,31 +124,31 @@
   created () {
     this.columns = [
       {
+        key: 'account',
         title: this.$t('label.account'),
-        dataIndex: 'account',
-        slots: { customRender: 'account' }
+        dataIndex: 'account'
       },
       {
+        key: 'role',
         title: this.$t('label.roletype'),
-        dataIndex: 'role',
-        slots: { customRender: 'role' }
+        dataIndex: 'role'
       },
       {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        slots: { customRender: 'action' }
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions'
       }
     ]
     if (this.isProjectRolesSupported()) {
       this.columns.splice(1, 0, {
+        key: 'user',
         title: this.$t('label.user'),
-        dataIndex: 'userid',
-        slots: { customRender: 'user' }
+        dataIndex: 'userid'
       })
       this.columns.splice(this.columns.length - 1, 0, {
+        key: 'projectrole',
         title: this.$t('label.project.role'),
-        dataIndex: 'projectroleid',
-        slots: { customRender: 'projectrole' }
+        dataIndex: 'projectroleid'
       })
     }
     this.page = 1
diff --git a/ui/src/views/project/AddAccountOrUserToProject.vue b/ui/src/views/project/AddAccountOrUserToProject.vue
index 31af9b0..e65c6a7 100644
--- a/ui/src/views/project/AddAccountOrUserToProject.vue
+++ b/ui/src/views/project/AddAccountOrUserToProject.vue
@@ -55,9 +55,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="role in projectRoles" :key="role.id">
+              <a-select-option v-for="role in projectRoles" :key="role.id" :label="role.name">
                 {{ role.name }}
               </a-select-option>
             </a-select>
@@ -70,9 +70,9 @@
               v-model:value="form.roletype"
               :placeholder="apiParams.addAccountToProject.roletype.description"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option value="Admin">Admin</a-select-option>
               <a-select-option value="Regular">Regular</a-select-option>
@@ -128,9 +128,9 @@
               showSearch
               optionFilterProp="label"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
-              <a-select-option v-for="role in projectRoles" :key="role.id">
+              <a-select-option v-for="role in projectRoles" :key="role.id" :label="role.name">
                 {{ role.name }}
               </a-select-option>
             </a-select>
@@ -143,9 +143,9 @@
               v-model:value="form.roletype"
               :placeholder="apiParams.addUserToProject.roletype.description"
               showSearch
-              optionFilterProp="label"
+              optionFilterProp="value"
               :filterOption="(input, option) => {
-                return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
               }" >
               <a-select-option value="Admin">Admin</a-select-option>
               <a-select-option value="Regular">Regular</a-select-option>
diff --git a/ui/src/views/project/InvitationsTemplate.vue b/ui/src/views/project/InvitationsTemplate.vue
index af13412..88159fc 100644
--- a/ui/src/views/project/InvitationsTemplate.vue
+++ b/ui/src/views/project/InvitationsTemplate.vue
@@ -36,26 +36,28 @@
           :pagination="false"
           :rowKey="record => record.id || record.account"
           @change="onChangeTable">
-          <template #state="{ text }">
-            <status :text="text ? text : ''" displayText />
-          </template>
-          <template #action="{ record }">
-            <div v-if="record.state===stateAllow" class="account-button-action">
-              <tooltip-button
-                tooltipPlacement="top"
-                :tooltip="$t('label.accept.project.invitation')"
-                icon="check-outlined"
-                size="small"
-                @onClick="onShowConfirmAcceptInvitation(record)"/>
-              <tooltip-button
-                tooltipPlacement="top"
-                :tooltip="$t('label.decline.invitation')"
-                type="primary"
-                :danger="true"
-                icon="close-outlined"
-                size="small"
-                @onClick="onShowConfirmRevokeInvitation(record)"/>
-            </div>
+          <template #bodyCell="{ column, text, record }">
+            <template v-if="column.key === 'state'">
+              <status :text="text ? text : ''" displayText />
+            </template>
+            <template v-if="column.key === 'actions'">
+              <div v-if="record.state===stateAllow" class="account-button-action">
+                <tooltip-button
+                  tooltipPlacement="top"
+                  :tooltip="$t('label.accept.project.invitation')"
+                  icon="check-outlined"
+                  size="small"
+                  @onClick="onShowConfirmAcceptInvitation(record)"/>
+                <tooltip-button
+                  tooltipPlacement="top"
+                  :tooltip="$t('label.decline.invitation')"
+                  type="primary"
+                  :danger="true"
+                  icon="close-outlined"
+                  size="small"
+                  @onClick="onShowConfirmRevokeInvitation(record)"/>
+              </div>
+            </template>
           </template>
         </a-table>
         <a-pagination
@@ -108,25 +110,25 @@
   created () {
     this.columns = [
       {
+        key: 'project',
         title: this.$t('label.project'),
-        dataIndex: 'project',
-        slots: { customRender: 'project' }
+        dataIndex: 'project'
       },
       {
+        key: 'account',
         title: this.$t('label.account'),
-        dataIndex: 'account',
-        slots: { customRender: 'account' }
+        dataIndex: 'account'
       },
       {
+        key: 'domain',
         title: this.$t('label.domain'),
-        dataIndex: 'domain',
-        slots: { customRender: 'domain' }
+        dataIndex: 'domain'
       },
       {
+        key: 'state',
         title: this.$t('label.state'),
         dataIndex: 'state',
         width: 130,
-        slots: { customRender: 'state' },
         filters: [
           {
             text: this.$t('state.pending'),
@@ -148,10 +150,10 @@
         filterMultiple: false
       },
       {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        width: 80,
-        slots: { customRender: 'action' }
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
+        width: 80
       }
     ]
 
@@ -161,9 +163,9 @@
     this.apiParams = this.$getApiParams('listProjectInvitations')
     if (this.apiParams.userid) {
       this.columns.splice(2, 0, {
+        key: 'user',
         title: this.$t('label.user'),
-        dataIndex: 'userid',
-        slots: { customRender: 'user' }
+        dataIndex: 'userid'
       })
     }
     this.fetchData()
diff --git a/ui/src/views/project/iam/ProjectRolePermissionTab.vue b/ui/src/views/project/iam/ProjectRolePermissionTab.vue
index df82d21..7b24098a 100644
--- a/ui/src/views/project/iam/ProjectRolePermissionTab.vue
+++ b/ui/src/views/project/iam/ProjectRolePermissionTab.vue
@@ -61,7 +61,6 @@
         handle=".drag-handle"
         animation="200"
         ghostClass="drag-ghost"
-        tag="transition-group"
         :component-data="{type: 'transition'}"
         item-key="id">
         <template #item="{element}">
diff --git a/ui/src/views/project/iam/ProjectRoleTab.vue b/ui/src/views/project/iam/ProjectRoleTab.vue
index d8d8b44..a0768db 100644
--- a/ui/src/views/project/iam/ProjectRoleTab.vue
+++ b/ui/src/views/project/iam/ProjectRoleTab.vue
@@ -27,31 +27,33 @@
           :loading="loading"
           :columns="columns"
           :dataSource="dataSource"
-          :rowKey="(record,idx) => record.projectid + '-' + idx"
+          :rowKey="(record, index) => record.projectid + '-' + index"
           :pagination="false">
           <template #expandedRowRender="{ record }">
             <ProjectRolePermissionTab class="table" :resource="resource" :role="record"/>
           </template>
-          <template #name="{ record }"> {{ record.name }} </template>
-          <template #description="{ record }">
-            {{ record.description }}
-          </template>
-          <template #action="{ record }">
-            <tooltip-button
-              tooltipPlacement="top"
-              :tooltip="$t('label.update.project.role')"
-              icon="edit-outlined"
-              size="small"
-              style="margin:10px"
-              @onClick="openUpdateModal(record)" />
-            <tooltip-button
-              tooltipPlacement="top"
-              :tooltip="$t('label.remove.project.role')"
-              type="primary"
-              :danger="true"
-              icon="delete-outlined"
-              size="small"
-              @onClick="deleteProjectRole(record)" />
+          <template #bodyCell="{ column, record }">
+            <template v-if="column.key === 'name'"> {{ record.name }} </template>
+            <template v-if="column.key === 'description'">
+              {{ record.description }}
+            </template>
+            <template v-if="column.key === 'actions'">
+              <tooltip-button
+                tooltipPlacement="top"
+                :tooltip="$t('label.update.project.role')"
+                icon="edit-outlined"
+                size="small"
+                style="margin:10px"
+                @onClick="openUpdateModal(record)" />
+              <tooltip-button
+                tooltipPlacement="top"
+                :tooltip="$t('label.remove.project.role')"
+                type="primary"
+                :danger="true"
+                icon="delete-outlined"
+                size="small"
+                @onClick="deleteProjectRole(record)" />
+            </template>
           </template>
         </a-table>
         <a-modal
@@ -149,20 +151,21 @@
   created () {
     this.columns = [
       {
+        key: 'name',
         title: this.$t('label.name'),
         dataIndex: 'name',
-        width: '35%',
-        slots: { customRender: 'name' }
+        width: '35%'
       },
       {
+        key: 'description',
         title: this.$t('label.description'),
         dataIndex: 'description'
       },
       {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        width: 100,
-        slots: { customRender: 'action' }
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
+        width: 100
       }
     ]
     this.initForm()
diff --git a/ui/src/views/setting/ConfigurationHierarchy.vue b/ui/src/views/setting/ConfigurationHierarchy.vue
index c3cfddb..45060ef 100644
--- a/ui/src/views/setting/ConfigurationHierarchy.vue
+++ b/ui/src/views/setting/ConfigurationHierarchy.vue
@@ -26,18 +26,18 @@
     :rowClassName="getRowClassName"
     style="overflow-y: auto; margin-left: 10px" >
 
-    <template #name="{ record }">
-      <span :style="hierarchyExists ? 'padding-left: 0px;' : 'padding-left: 25px;'">
-        <b><span v-if="record.parent">└─ &nbsp;</span>{{record.displaytext }} </b> {{ ' (' + record.name + ')' }}
-      </span>
-      <br/>
-      <span :style="record.parent ? 'padding-left: 50px; display:block' : 'padding-left: 25px; display:block'">{{ record.description }}</span>
+    <template #bodyCell="{ column, record }">
+      <template v-if="column.key === 'name'">
+    <span :style="hierarchyExists ? 'padding-left: 0px;' : 'padding-left: 25px;'">
+      <b><span v-if="record.parent">└─ &nbsp;</span>{{record.displaytext }} </b> {{ ' (' + record.name + ')' }}
+    </span>
+    <br/>
+    <span :style="record.parent ? 'padding-left: 50px; display:block' : 'padding-left: 25px; display:block'">{{ record.description }}</span>
+      </template>
+      <template v-if="column.key === 'value'">
+        <ConfigurationValue :configrecord="record" />
+      </template>
     </template>
-
-    <template #value="{ record }">
-      <ConfigurationValue :configrecord="record" />
-    </template>
-
   </a-table>
 </template>
 
diff --git a/ui/src/views/setting/ConfigurationTab.vue b/ui/src/views/setting/ConfigurationTab.vue
index 0948a13..11c0b04 100644
--- a/ui/src/views/setting/ConfigurationTab.vue
+++ b/ui/src/views/setting/ConfigurationTab.vue
@@ -138,12 +138,12 @@
         {
           title: 'name',
           dataIndex: 'name',
-          slots: { customRender: 'name' }
+          key: 'name'
         },
         {
           title: 'value',
           dataIndex: 'value',
-          slots: { customRender: 'value' },
+          key: 'value',
           width: '29%'
         }
       ]
diff --git a/ui/src/views/setting/ConfigurationTable.vue b/ui/src/views/setting/ConfigurationTable.vue
index d818251..6854852 100644
--- a/ui/src/views/setting/ConfigurationTable.vue
+++ b/ui/src/views/setting/ConfigurationTable.vue
@@ -27,11 +27,13 @@
       :rowKey="record => record.name"
       :rowClassName="getRowClassName" >
 
-      <template #name="{ record }">
-        <b> {{record.displaytext }} </b> {{ ' (' + record.name + ')' }} <br/> {{ record.description }}
-      </template>
-      <template #value="{ record }">
-        <ConfigurationValue :configrecord="record" />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'name'">
+          <b> {{record.displaytext }} </b> {{ ' (' + record.name + ')' }} <br/> {{ record.description }}
+        </template>
+        <template v-if="column.key === 'value'">
+          <ConfigurationValue :configrecord="record" />
+        </template>
       </template>
     </a-table>
     <a-pagination
diff --git a/ui/src/views/storage/AttachVolume.vue b/ui/src/views/storage/AttachVolume.vue
index 96e81ba3..de0a2f7 100644
--- a/ui/src/views/storage/AttachVolume.vue
+++ b/ui/src/views/storage/AttachVolume.vue
@@ -41,9 +41,9 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
-          <a-select-option v-for="vm in virtualmachines" :key="vm.id">
+          <a-select-option v-for="vm in virtualmachines" :key="vm.id" :label="vm.name || vm.displayname">
             {{ vm.name || vm.displayname }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/storage/ChangeOfferingForVolume.vue b/ui/src/views/storage/ChangeOfferingForVolume.vue
index 203a2da..5ab8039 100644
--- a/ui/src/views/storage/ChangeOfferingForVolume.vue
+++ b/ui/src/views/storage/ChangeOfferingForVolume.vue
@@ -41,13 +41,14 @@
         showSearch
         optionFilterProp="label"
         :filterOption="(input, option) => {
-          return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
         }"
         @change="id => onChangeDiskOffering(id)">
         <a-select-option
           v-for="(offering, index) in diskOfferings"
           :value="offering.id"
-          :key="index">
+          :key="index"
+          :label="offering.displaytext || offering.name">
           {{ offering.displaytext || offering.name }}
         </a-select-option>
       </a-select>
diff --git a/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue b/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
index 5702bc4..409e931 100644
--- a/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
+++ b/ui/src/views/storage/CreateSnapshotFromVMSnapshot.vue
@@ -40,12 +40,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="(volume, index) in volumes"
             :value="volume.id"
-            :key="index">
+            :key="index"
+            :label="volume.displaytext || volume.name">
             {{ volume.displaytext || volume.name }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/storage/CreateVolume.vue b/ui/src/views/storage/CreateVolume.vue
index 7f11033..d1c5cc5 100644
--- a/ui/src/views/storage/CreateVolume.vue
+++ b/ui/src/views/storage/CreateVolume.vue
@@ -74,12 +74,13 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="(offering, index) in offerings"
             :value="offering.id"
-            :key="index">
+            :key="index"
+            :label="offering.displaytext || offering.name">
             {{ offering.displaytext || offering.name }}
           </a-select-option>
         </a-select>
diff --git a/ui/src/views/storage/FormSchedule.vue b/ui/src/views/storage/FormSchedule.vue
index 235be2e..c689de2 100644
--- a/ui/src/views/storage/FormSchedule.vue
+++ b/ui/src/views/storage/FormSchedule.vue
@@ -74,8 +74,10 @@
                 name="timeSelect"
                 ref="timeSelect">
                 <a-time-picker
-                  format="HH:mm"
-                  v-model:value="form.timeSelect" />
+                  use12Hours
+                  format="h:mm A"
+                  v-model:value="form.timeSelect"
+                  style="width: 100%;" />
               </a-form-item>
             </a-col>
             <a-col :md="24" :lg="12" v-if="form.intervaltype==='weekly'">
@@ -85,9 +87,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
-                  <a-select-option v-for="(opt, optIndex) in dayOfWeek" :key="optIndex">
+                  <a-select-option v-for="(opt, optIndex) in dayOfWeek" :key="optIndex" :label="opt.name || opt.description">
                     {{ opt.name || opt.description }}
                   </a-select-option>
                 </a-select>
@@ -98,9 +100,9 @@
                 <a-select
                   v-model:value="form['day-of-month']"
                   showSearch
-                  optionFilterProp="label"
+                  optionFilterProp="value"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
                   <a-select-option v-for="opt in dayOfMonth" :key="opt.name">
                     {{ opt.name }}
@@ -128,9 +130,9 @@
                   showSearch
                   optionFilterProp="label"
                   :filterOption="(input, option) => {
-                    return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                   }" >
-                  <a-select-option v-for="opt in timeZoneMap" :key="opt.id">
+                  <a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
                     {{ opt.name || opt.description }}
                   </a-select-option>
                 </a-select>
diff --git a/ui/src/views/storage/MigrateVolume.vue b/ui/src/views/storage/MigrateVolume.vue
index 62bed70..761fe02 100644
--- a/ui/src/views/storage/MigrateVolume.vue
+++ b/ui/src/views/storage/MigrateVolume.vue
@@ -50,9 +50,9 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="(diskOffering, index) in diskOfferings" :value="diskOffering.id" :key="index">
+            <a-select-option v-for="(diskOffering, index) in diskOfferings" :value="diskOffering.id" :key="index" :label="diskOffering.displaytext">
               {{ diskOffering.displaytext }}
             </a-select-option>
           </a-select>
diff --git a/ui/src/views/storage/RestoreAttachBackupVolume.vue b/ui/src/views/storage/RestoreAttachBackupVolume.vue
index 41a8f76..72c38ac 100644
--- a/ui/src/views/storage/RestoreAttachBackupVolume.vue
+++ b/ui/src/views/storage/RestoreAttachBackupVolume.vue
@@ -34,11 +34,12 @@
           showSearch
           optionFilterProp="label"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="(opt) in volumeOptions.opts"
-            :key="opt.id">
+            :key="opt.id"
+            :label="opt.name">
             {{ opt.name }}
           </a-select-option>
         </a-select>
@@ -49,9 +50,9 @@
           v-model:value="form.virtualmachineid"
           :loading="virtualMachineOptions.loading"
           showSearch
-          optionFilterProp="label"
+          optionFilterProp="value"
           :filterOption="(input, option) => {
-            return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
           }" >
           <a-select-option
             v-for="(opt) in virtualMachineOptions.opts"
diff --git a/ui/src/views/storage/ScheduledSnapshots.vue b/ui/src/views/storage/ScheduledSnapshots.vue
index 0c0f6d1..8c95d0b 100644
--- a/ui/src/views/storage/ScheduledSnapshots.vue
+++ b/ui/src/views/storage/ScheduledSnapshots.vue
@@ -24,54 +24,56 @@
       :rowKey="record => record.id"
       :pagination="false"
       :loading="loading">
-      <template #icon="{ record }">
-        <label class="interval-icon">
-          <span v-if="record.intervaltype===0">
-            <clock-circle-outlined />
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'icon'">
+          <label class="interval-icon">
+            <span v-if="record.intervaltype===0">
+              <clock-circle-outlined />
+            </span>
+            <span class="custom-icon icon-daily" v-else-if="record.intervaltype===1">
+              <calendar-outlined />
+            </span>
+            <span class="custom-icon icon-weekly" v-else-if="record.intervaltype===2">
+              <calendar-outlined />
+            </span>
+            <span class="custom-icon icon-monthly" v-else-if="record.intervaltype===3">
+              <calendar-outlined />
+            </span>
+          </label>
+        </template>
+        <template v-if="column.key === 'time'">
+          <label class="interval-content">
+            <span v-if="record.intervaltype===0">{{ record.schedule + $t('label.min.past.hour') }}</span>
+            <span v-else>{{ record.schedule.split(':')[1] + ':' + record.schedule.split(':')[0] }}</span>
+          </label>
+        </template>
+        <template v-if="column.key === 'interval'">
+          <span v-if="record.intervaltype===2">
+            {{ `${$t('label.every')} ${$t(listDayOfWeek[record.schedule.split(':')[2] - 1])}` }}
           </span>
-          <span class="custom-icon icon-daily" v-else-if="record.intervaltype===1">
-            <calendar-outlined />
+          <span v-else-if="record.intervaltype===3">
+            {{ `${$t('label.day')} ${record.schedule.split(':')[2]} ${$t('label.of.month')}` }}
           </span>
-          <span class="custom-icon icon-weekly" v-else-if="record.intervaltype===2">
-            <calendar-outlined />
-          </span>
-          <span class="custom-icon icon-monthly" v-else-if="record.intervaltype===3">
-            <calendar-outlined />
-          </span>
-        </label>
-      </template>
-      <template #time="{ record }">
-        <label class="interval-content">
-          <span v-if="record.intervaltype===0">{{ record.schedule + $t('label.min.past.hour') }}</span>
-          <span v-else>{{ record.schedule.split(':')[1] + ':' + record.schedule.split(':')[0] }}</span>
-        </label>
-      </template>
-      <template #interval="{ record }">
-        <span v-if="record.intervaltype===2">
-          {{ `${$t('label.every')} ${$t(listDayOfWeek[record.schedule.split(':')[2] - 1])}` }}
-        </span>
-        <span v-else-if="record.intervaltype===3">
-          {{ `${$t('label.day')} ${record.schedule.split(':')[2]} ${$t('label.of.month')}` }}
-        </span>
-      </template>
-      <template #timezone="{ record }">
-        <label>{{ getTimeZone(record.timezone) }}</label>
-      </template>
-      <template #tags="{ record }">
-        <a-tag v-for="(tag, index) in record.tags" :key="index">{{ tag.key + '=' + tag.value }}</a-tag>
-      </template>
-      <template #action="{ record }">
-        <div class="account-button-action">
-          <tooltip-button
-            tooltipPlacement="top"
-            :tooltip="$t('label.delete')"
-            type="primary"
-            :danger="true"
-            icon="close-outlined"
-            size="small"
-            :loading="actionLoading"
-            @onClick="handleClickDelete(record)" />
-        </div>
+        </template>
+        <template v-if="column.key === 'timezone'">
+          <label>{{ getTimeZone(record.timezone) }}</label>
+        </template>
+        <template v-if="column.key === 'tags'">
+          <a-tag v-for="(tag, index) in record.tags" :key="index">{{ tag.key + '=' + tag.value }}</a-tag>
+        </template>
+        <template v-if="column.key === 'actions'">
+          <div class="account-button-action">
+            <tooltip-button
+              tooltipPlacement="top"
+              :tooltip="$t('label.delete')"
+              type="primary"
+              :danger="true"
+              icon="close-outlined"
+              size="small"
+              :loading="actionLoading"
+              @onClick="handleClickDelete(record)" />
+          </div>
+        </template>
       </template>
     </a-table>
   </div>
@@ -112,41 +114,41 @@
   created () {
     this.columns = [
       {
+        key: 'icon',
         title: '',
         dataIndex: 'icon',
-        width: 30,
-        slots: { customRender: 'icon' }
+        width: 30
       },
       {
+        key: 'time',
         title: this.$t('label.time'),
-        dataIndex: 'schedule',
-        slots: { customRender: 'time' }
+        dataIndex: 'schedule'
       },
       {
+        key: 'interval',
         title: '',
-        dataIndex: 'interval',
-        slots: { customRender: 'interval' }
+        dataIndex: 'interval'
       },
       {
+        key: 'timezone',
         title: this.$t('label.timezone'),
-        dataIndex: 'timezone',
-        slots: { customRender: 'timezone' }
+        dataIndex: 'timezone'
       },
       {
+        key: 'keep',
         title: this.$t('label.keep'),
-        dataIndex: 'maxsnaps',
-        slots: { customRender: 'keep' }
+        dataIndex: 'maxsnaps'
       },
       {
+        key: 'tags',
         title: this.$t('label.tags'),
-        dataIndex: 'tags',
-        slots: { customRender: 'tags' }
+        dataIndex: 'tags'
       },
       {
-        title: this.$t('label.action'),
-        dataIndex: 'action',
-        width: 50,
-        slots: { customRender: 'action' }
+        key: 'actions',
+        title: this.$t('label.actions'),
+        dataIndex: 'actions',
+        width: 50
       }
     ]
   },
diff --git a/ui/src/views/storage/UploadLocalVolume.vue b/ui/src/views/storage/UploadLocalVolume.vue
index 3548e02..75775c9 100644
--- a/ui/src/views/storage/UploadLocalVolume.vue
+++ b/ui/src/views/storage/UploadLocalVolume.vue
@@ -33,7 +33,7 @@
           <a-upload-dragger
             :multiple="false"
             :fileList="fileList"
-            :remove="handleRemove"
+            @remove="handleRemove"
             :beforeUpload="beforeUpload"
             v-model:value="form.file">
             <p class="ant-upload-drag-icon">
@@ -84,10 +84,14 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option v-for="opt in offerings" :key="opt.id">
-              {{ opt.name || opt.displaytext }}
+            <a-select-option
+              v-for="(offering, index) in offerings"
+              :value="offering.id"
+              :key="index"
+              :label="offering.displaytext || offering.name">
+              {{ offering.displaytext || offering.name }}
             </a-select-option>
           </a-select>
         </a-form-item>
@@ -98,9 +102,9 @@
           <a-select
             v-model:value="form.format"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option v-for="format in formats" :key="format">
               {{ format }}
@@ -125,12 +129,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="domainLoading"
             :placeholder="$t('label.domainid')"
             @change="val => { handleDomainChange(domainList[val].id) }">
-            <a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex" :label="opt.path || opt.name || opt.description">
               {{ opt.path || opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -142,9 +146,9 @@
           <a-select
             v-model:value="form.account"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="$t('label.account')"
             @change="val => { handleAccountChange(val) }">
diff --git a/ui/src/views/storage/UploadVolume.vue b/ui/src/views/storage/UploadVolume.vue
index 5bfdcfa..937c3ad 100644
--- a/ui/src/views/storage/UploadVolume.vue
+++ b/ui/src/views/storage/UploadVolume.vue
@@ -70,9 +70,9 @@
           <a-select
             v-model:value="form.format"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option v-for="format in formats" :key="format">
               {{ format }}
@@ -90,12 +90,13 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
             <a-select-option
               v-for="(offering, index) in offerings"
               :value="offering.id"
-              :key="index">
+              :key="index"
+              :label="offering.displaytext || offering.name">
               {{ offering.displaytext || offering.name }}
             </a-select-option>
           </a-select>
@@ -118,12 +119,12 @@
             showSearch
             optionFilterProp="label"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :loading="domainLoading"
             :placeholder="$t('label.domainid')"
             @change="val => { handleDomainChange(domainList[val].id) }">
-            <a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex">
+            <a-select-option v-for="(opt, optIndex) in domainList" :key="optIndex" :label="opt.path || opt.name || opt.description">
               {{ opt.path || opt.name || opt.description }}
             </a-select-option>
           </a-select>
@@ -135,9 +136,9 @@
           <a-select
             v-model:value="form.account"
             showSearch
-            optionFilterProp="label"
+            optionFilterProp="value"
             :filterOption="(input, option) => {
-              return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+              return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }"
             :placeholder="$t('label.account')"
             @change="val => { handleAccountChange(val) }">
diff --git a/ui/src/views/tools/ImportUnmanagedInstance.vue b/ui/src/views/tools/ImportUnmanagedInstance.vue
index 1e5629b..7e89d48 100644
--- a/ui/src/views/tools/ImportUnmanagedInstance.vue
+++ b/ui/src/views/tools/ImportUnmanagedInstance.vue
@@ -180,7 +180,7 @@
                 :cpuSpeed="getCPUSpeed()"
                 @update-iops-value="updateFieldValue"
                 @update-compute-cpunumber="updateFieldValue"
-                @update-compute-cpuspeed="updateFieldValue"
+                @update-compute-cpuspeed="updateCpuSpeed"
                 @update-compute-memory="updateFieldValue" />
               <div v-if="resource.disk && resource.disk.length > 1">
                 <a-form-item name="selection" ref="selection">
@@ -195,10 +195,10 @@
                     showSearch
                     optionFilterProp="label"
                     :filterOption="(input, option) => {
-                      return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }"
                     @change="val => { selectedRootDiskIndex = val }">
-                    <a-select-option v-for="(opt, optIndex) in resource.disk" :key="optIndex">
+                    <a-select-option v-for="(opt, optIndex) in resource.disk" :key="optIndex" :label="opt.label || opt.id">
                       {{ opt.label || opt.id }}
                     </a-select-option>
                   </a-select>
@@ -595,6 +595,15 @@
         this.selectMatchingComputeOffering()
       })
     },
+    updateCpuSpeed (name, value) {
+      if (this.computeOffering.iscustomized) {
+        if (this.computeOffering.serviceofferingdetails) {
+          this.updateFieldValue(this.cpuSpeedKey, this.computeOffering.cpuspeed)
+        } else {
+          this.updateFieldValue(this.cpuSpeedKey, value)
+        }
+      }
+    },
     updateFieldValue (name, value) {
       this.form[name] = value
     },
diff --git a/ui/src/views/tools/ManageInstances.vue b/ui/src/views/tools/ManageInstances.vue
index 1e6cf3a..460ff91 100644
--- a/ui/src/views/tools/ManageInstances.vue
+++ b/ui/src/views/tools/ManageInstances.vue
@@ -139,8 +139,10 @@
                   size="middle"
                   :rowClassName="getRowClassName"
                 >
-                  <template #state="{text}">
-                    <status :text="text ? text : ''" displayText />
+                  <template #bodyCell="{ column, text }">
+                    <template v-if="column.key === 'state'">
+                      <status :text="text ? text : ''" displayText />
+                    </template>
                   </template>
                 </a-table>
                 <div class="instances-card-footer">
@@ -205,11 +207,13 @@
                   size="middle"
                   :rowClassName="getRowClassName"
                 >
-                  <template #name="{text, record}" href="javascript:;">
-                    <router-link :to="{ path: '/vm/' + record.id }">{{ text }}</router-link>
-                  </template>
-                  <template #state="{text}">
-                    <status :text="text ? text : ''" displayText />
+                  <template #bodyCell="{ column, text, record }">
+                    <template v-if="column.key === 'name'">
+                      <router-link :to="{ path: '/vm/' + record.id }">{{ text }}</router-link>
+                    </template>
+                    <template v-if="column.key === 'state'">
+                      <status :text="text ? text : ''" displayText />
+                    </template>
                   </template>
                 </a-table>
                 <div class="instances-card-footer">
@@ -295,9 +299,9 @@
         width: 100
       },
       {
+        key: 'state',
         title: this.$t('label.state'),
-        dataIndex: 'powerstate',
-        slots: { customRender: 'state' }
+        dataIndex: 'powerstate'
       },
       {
         title: this.$t('label.hostname'),
@@ -310,19 +314,19 @@
     ]
     const managedInstancesColumns = [
       {
+        key: 'name',
         title: this.$t('label.name'),
         dataIndex: 'name',
-        width: 100,
-        slots: { customRender: 'name' }
+        width: 100
       },
       {
         title: this.$t('label.instancename'),
         dataIndex: 'instancename'
       },
       {
+        key: 'state',
         title: this.$t('label.state'),
-        dataIndex: 'state',
-        slots: { customRender: 'state' }
+        dataIndex: 'state'
       },
       {
         title: this.$t('label.hostname'),
@@ -505,7 +509,7 @@
     },
     filterOption (input, option) {
       return (
-        option.children[0].children.toUpperCase().indexOf(input.toUpperCase()) >= 0
+        option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0
       )
     },
     fetchOptions (param, name, exclude) {
diff --git a/ui/tests/mockData/AutogenView.mock.json b/ui/tests/mockData/AutogenView.mock.json
index 8d932e0..56807cc 100644
--- a/ui/tests/mockData/AutogenView.mock.json
+++ b/ui/tests/mockData/AutogenView.mock.json
@@ -458,7 +458,8 @@
       "meta": {
         "title": "label.title",
         "icon": "play-circle-outlined",
-        "permission": ["testApiNameCase1"]
+        "permission": ["testApiNameCase1"],
+        "columns": ["column1", "column2", "column3"]
       },
       "component": {},
       "children": [{
@@ -487,7 +488,8 @@
       "meta": {
         "title": "label.title",
         "icon": "play-circle-outlined",
-        "permission": ["testApiNameCase1"]
+        "permission": ["testApiNameCase1"],
+        "columns": ["column1"]
       },
       "component": {}
     },
diff --git a/ui/tests/unit/components/view/ActionButton.spec.js b/ui/tests/unit/components/view/ActionButton.spec.js
index 29ade02..7e41f0b 100644
--- a/ui/tests/unit/components/view/ActionButton.spec.js
+++ b/ui/tests/unit/components/view/ActionButton.spec.js
@@ -51,6 +51,7 @@
 describe('Components > View > ActionButton.vue', () => {
   beforeEach(() => {
     jest.clearAllMocks()
+    jest.spyOn(console, 'warn').mockImplementation(() => {})
   })
 
   describe('Template', () => {
diff --git a/ui/tests/unit/views/AutogenView.spec.js b/ui/tests/unit/views/AutogenView.spec.js
index ec3130c..daac26d 100644
--- a/ui/tests/unit/views/AutogenView.spec.js
+++ b/ui/tests/unit/views/AutogenView.spec.js
@@ -16,6 +16,7 @@
 // under the License.
 
 import { flushPromises } from '@vue/test-utils'
+import { ref } from 'vue'
 
 import mockAxios from '../../mock/mockAxios'
 import AutogenView from '@/views/AutogenView'
@@ -180,6 +181,7 @@
 describe('Views > AutogenView.vue', () => {
   beforeEach(async () => {
     jest.clearAllMocks()
+    jest.spyOn(console, 'warn').mockImplementation(() => {})
 
     delete window.ResizeObserver
     delete window.ls
@@ -253,6 +255,9 @@
           case 'RefValidateFields':
             wrapper.vm.formRef.value.validateFields = originalFunc[key]
             break
+          case 'formRef':
+            wrapper.vm.formRef = originalFunc[key]
+            break
           case 'switchProject':
             wrapper.vm.switchProject = originalFunc[key]
             break
@@ -549,10 +554,13 @@
         await flushPromises()
 
         expect(wrapper.vm.columns.length).toEqual(2)
+        expect(wrapper.vm.columns[0].key).toEqual('name')
         expect(wrapper.vm.columns[0].title).toEqual('name-en')
         expect(wrapper.vm.columns[0].dataIndex).toEqual('name')
-        expect(wrapper.vm.columns[0].slots).toEqual({ customRender: 'name' })
         expect(typeof wrapper.vm.columns[0].sorter).toBe('function')
+        expect(wrapper.vm.columns[1].key).toEqual('filtercolumn')
+        expect(wrapper.vm.columns[1].dataIndex).toEqual('filtercolumn')
+        expect(wrapper.vm.columns[1].customFilterDropdown).toBeTruthy()
         done(0)
       })
 
@@ -623,7 +631,7 @@
       //   })
       //   await router.push({ name: 'testRouter12', params: { id: 'test-id' } })
       //   await flushPromises()
-      //
+
       //   expect(mockAxios).toHaveBeenCalled()
       //   expect(mockAxios).toHaveBeenLastCalledWith({
       //     url: '/',
@@ -731,14 +739,12 @@
         expect(wrapper.vm.items).toEqual([{
           id: 'test-id',
           name: 'test-name-value',
-          key: 0,
-          column1: 'test-name-value'
+          key: 0
         }])
         expect(wrapper.vm.resource).toEqual({
           id: 'test-id',
           name: 'test-name-value',
-          key: 0,
-          column1: 'test-name-value'
+          key: 0
         })
         done()
       })
@@ -2288,12 +2294,21 @@
       it('formRef makes validation calls when handleSubmit() is called in list view', async (done) => {
         originalFunc.callGroupApi = wrapper.vm.callGroupApi
         originalFunc.fetchData = wrapper.vm.fetchData
+        originalFunc.formRef = wrapper.vm.formRef
         wrapper.vm.fetchData = jest.fn()
         wrapper.vm.callGroupApi = jest.fn((params, resourceName) => {
           return new Promise(resolve => {
             resolve()
           })
         })
+        if (!wrapper.vm.formRef) {
+          wrapper.vm.formRef = ref()
+        }
+        wrapper.vm.formRef.value.validate = jest.fn((params, resourceName) => {
+          return new Promise(resolve => {
+            resolve()
+          })
+        })
 
         const fetchData = jest.spyOn(wrapper.vm, 'fetchData')
         const callGroupApi = jest.spyOn(wrapper.vm, 'callGroupApi')
@@ -2312,6 +2327,11 @@
           items: [{
             id: 'test-id-value-1',
             name: 'test-name-value-1'
+          }],
+          columns: [{
+            key: 'column1',
+            dataIndex: 'column1',
+            title: 'column1'
           }]
         })
         await wrapper.vm.handleSubmit(event)
diff --git a/ui/tests/unit/views/compute/MigrateWizard.spec.js b/ui/tests/unit/views/compute/MigrateWizard.spec.js
index ab44b74..f352b2d 100644
--- a/ui/tests/unit/views/compute/MigrateWizard.spec.js
+++ b/ui/tests/unit/views/compute/MigrateWizard.spec.js
@@ -93,12 +93,12 @@
 describe('Views > compute > MigrateWizard.vue', () => {
   beforeEach(() => {
     jest.clearAllMocks()
+    jest.spyOn(console, 'warn').mockImplementation(() => {})
 
     if (!wrapper) {
       mockAxios.mockResolvedValue({ findhostsformigrationresponse: { count: 0, host: [] } })
       wrapper = factory({
-        props: { resource: {} },
-        data: { columns: [] }
+        props: { resource: {} }
       })
     }
 
@@ -107,7 +107,6 @@
 
   afterEach(() => {
     if (wrapper) {
-      wrapper.vm.columns = []
       wrapper.vm.searchQuery = ''
       wrapper.vm.page = 1
       wrapper.vm.pageSize = 10
@@ -132,7 +131,6 @@
       it('API should be called with resource is empty and searchQuery is empty', async (done) => {
         await mockAxios.mockResolvedValue({ findhostsformigrationresponse: { count: 0, host: [] } })
         await wrapper.setProps({ resource: {} })
-        await wrapper.setData({ columns: [] })
         await wrapper.vm.fetchData()
         await flushPromises()
 
@@ -156,7 +154,6 @@
       it('API should be called with resource.id is empty and searchQuery is empty', async (done) => {
         await mockAxios.mockResolvedValue({ findhostsformigrationresponse: { count: 0, host: [] } })
         await wrapper.setProps({ resource: { id: null } })
-        await wrapper.setData({ columns: [] })
         await wrapper.vm.fetchData()
         await flushPromises()
 
@@ -180,7 +177,6 @@
       it('API should be called with resource.id is not empty and searchQuery is empty', async (done) => {
         await mockAxios.mockResolvedValue({ findhostsformigrationresponse: { count: 0, host: [] } })
         await wrapper.setProps({ resource: { id: 'test-id-value' } })
-        await wrapper.setData({ columns: [] })
         await wrapper.vm.fetchData()
         await flushPromises()
 
@@ -204,7 +200,7 @@
       it('API should be called with resource.id is not empty and searchQuery is not empty', async (done) => {
         await mockAxios.mockResolvedValue({ findhostsformigrationresponse: { count: 0, host: [] } })
         await wrapper.setProps({ resource: { id: 'test-id-value' } })
-        await wrapper.setData({ searchQuery: 'test-query-value', columns: [] })
+        await wrapper.setData({ searchQuery: 'test-query-value' })
         await wrapper.vm.fetchData()
         await flushPromises()
 
@@ -231,8 +227,7 @@
         await wrapper.setData({
           searchQuery: 'test-query-value',
           page: 2,
-          pageSize: 20,
-          columns: []
+          pageSize: 20
         })
         await wrapper.vm.fetchData()
         await flushPromises()
diff --git a/ui/vue.config.js b/ui/vue.config.js
index 09f0423..c74218b 100644
--- a/ui/vue.config.js
+++ b/ui/vue.config.js
@@ -127,6 +127,7 @@
         modifyVars: {
           // https://ant.design/docs/spec/colors
           // https://vue.ant.design/docs/vue/customize-theme/
+          'root-entry-name': 'default'
         },
         javascriptEnabled: true
       }
diff --git a/usage/pom.xml b/usage/pom.xml
index 389d34a..d6ea06d 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/utils/pom.xml b/utils/pom.xml
index 963aaca..4360e18 100755
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <dependencies>
@@ -118,6 +118,11 @@
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.cronutils</groupId>
+            <artifactId>cron-utils</artifactId>
+            <version>${cs.cron-utils.version}</version>
+        </dependency>
         <!-- Test dependency in mysql for db tests -->
         <dependency>
             <groupId>mysql</groupId>
diff --git a/utils/src/main/java/com/cloud/utils/DateUtil.java b/utils/src/main/java/com/cloud/utils/DateUtil.java
index 4d0157f..41afcb1 100644
--- a/utils/src/main/java/com/cloud/utils/DateUtil.java
+++ b/utils/src/main/java/com/cloud/utils/DateUtil.java
@@ -22,6 +22,9 @@
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.TimeZone;
@@ -31,6 +34,13 @@
 import java.time.OffsetDateTime;
 
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cronutils.descriptor.CronDescriptor;
+import com.cronutils.model.CronType;
+import com.cronutils.model.definition.CronDefinition;
+import com.cronutils.model.definition.CronDefinitionBuilder;
+import com.cronutils.parser.CronParser;
+import org.springframework.scheduling.support.CronExpression;
+
 
 public class DateUtil {
     public static final int HOURS_IN_A_MONTH = 30 * 24;
@@ -52,7 +62,7 @@
     };
 
     public static Date currentGMTTime() {
-        // Date object always stores miliseconds offset based on GMT internally
+        // Date object always stores milliseconds offset based on GMT internally
         return new Date();
     }
 
@@ -295,4 +305,35 @@
         return (dateCalendar1.getTimeInMillis() - dateCalendar2.getTimeInMillis() )/1000;
 
     }
+
+    public static CronExpression parseSchedule(String schedule) {
+        if (schedule != null) {
+            // CronExpression's granularity is in seconds. Prepending "0 " to change the granularity to minutes.
+            return CronExpression.parse(String.format("0 %s", schedule));
+        } else {
+            return null;
+        }
+    }
+
+    public static String getHumanReadableSchedule(CronExpression schedule) {
+        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.SPRING);
+        CronParser parser = new CronParser(cronDefinition);
+        CronDescriptor descriptor = CronDescriptor.instance();
+        return descriptor.describe(parser.parse(schedule.toString()));
+    }
+
+    public static ZonedDateTime getZoneDateTime(Date date, ZoneId tzId) {
+        if (date == null) {
+            return null;
+        }
+        ZonedDateTime zonedDate = ZonedDateTime.ofInstant(date.toInstant(), tzId);
+        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), TimeZone.getDefault().toZoneId());
+        zonedDate = zonedDate.withYear(localDateTime.getYear())
+                .withMonth(localDateTime.getMonthValue())
+                .withDayOfMonth(localDateTime.getDayOfMonth())
+                .withHour(localDateTime.getHour())
+                .withMinute(localDateTime.getMinute())
+                .withSecond(localDateTime.getSecond());
+        return zonedDate;
+    }
 }
diff --git a/utils/src/main/java/com/cloud/utils/LogUtils.java b/utils/src/main/java/com/cloud/utils/LogUtils.java
index edfb53f..a458be7 100644
--- a/utils/src/main/java/com/cloud/utils/LogUtils.java
+++ b/utils/src/main/java/com/cloud/utils/LogUtils.java
@@ -28,6 +28,7 @@
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.FileAppender;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.log4j.Logger;
 import org.apache.log4j.xml.DOMConfigurator;
 
@@ -78,21 +79,29 @@
         return fileNames;
     }
 
+    /**
+     * Tries to convert message parameters to JSON format and use them in the message.
+     * @param formatMessage message to format.
+     * @param objects objects to convert to JSON. A null object will be defaulted to the String "null";
+     * if it is not possible to convert an object to JSON, the object's 'toString' will be used instead.
+     * @return the formatted message.
+     */
     public static String logGsonWithoutException(String formatMessage, Object ... objects) {
         List<String> gsons = new ArrayList<>();
         for (Object object : objects) {
             try {
                 gsons.add(GSON.toJson(object));
             } catch (Exception e) {
-                LOGGER.debug(String.format("Failed to log object [%s] using GSON.", object != null ? object.getClass().getSimpleName() : "null"));
-                gsons.add("error to decode");
+                Object errObj = ObjectUtils.defaultIfNull(object, "null");
+                LOGGER.trace(String.format("Failed to log object [%s] using GSON.", errObj.getClass().getSimpleName()));
+                gsons.add("error decoding " + errObj);
             }
         }
         try {
             return String.format(formatMessage, gsons.toArray());
         } catch (Exception e) {
             String errorMsg = String.format("Failed to log objects using GSON due to: [%s].", e.getMessage());
-            LOGGER.error(errorMsg, e);
+            LOGGER.trace(errorMsg, e);
             return errorMsg;
         }
     }
diff --git a/utils/src/main/java/com/cloud/utils/concurrency/SynchronizationEvent.java b/utils/src/main/java/com/cloud/utils/concurrency/SynchronizationEvent.java
index 5207ad4..5871c0c 100644
--- a/utils/src/main/java/com/cloud/utils/concurrency/SynchronizationEvent.java
+++ b/utils/src/main/java/com/cloud/utils/concurrency/SynchronizationEvent.java
@@ -65,13 +65,13 @@
         }
     }
 
-    public boolean waitEvent(long timeOutMiliseconds) throws InterruptedException {
+    public boolean waitEvent(long timeOutMilliseconds) throws InterruptedException {
         synchronized (this) {
             if (signalled)
                 return true;
 
             try {
-                wait(timeOutMiliseconds);
+                wait(timeOutMilliseconds);
                 return signalled;
             } catch (InterruptedException e) {
                 // TODO, we don't honor time out semantics when the waiting thread is interrupted
diff --git a/utils/src/test/java/com/cloud/utils/FileUtilTest.java b/utils/src/test/java/com/cloud/utils/FileUtilTest.java
index 70ad39c..87e1a18 100644
--- a/utils/src/test/java/com/cloud/utils/FileUtilTest.java
+++ b/utils/src/test/java/com/cloud/utils/FileUtilTest.java
@@ -20,49 +20,52 @@
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
 
 import static org.mockito.ArgumentMatchers.nullable;
 
-@PrepareForTest(value = {SshHelper.class})
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.parsers.*", "javax.xml.*", "org.w3c.dom.*", "org.xml.*"})
+@RunWith(MockitoJUnitRunner.class)
 public class FileUtilTest {
 
     @Test
     public void successfulScpTest() throws Exception {
-        PowerMockito.mockStatic(SshHelper.class);
+        MockedStatic<SshHelper> sshHelperMocked = Mockito.mockStatic(SshHelper.class);
         String basePath = "/tmp";
         String[] files = new String[] { "file1.txt" };
         int sshPort = 3922;
         String controlIp = "10.0.0.10";
         String destPath = "/home/cloud/";
         File pemFile = new File("/root/.ssh/id_rsa");
-        PowerMockito.doNothing().when(SshHelper.class, "scpTo", Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), nullable(String.class), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString());
+        sshHelperMocked.when(() ->
+                SshHelper.scpTo(
+                        Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), nullable(String.class), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString()
+                )).then(invocation -> null);
         FileUtil.scpPatchFiles(controlIp, destPath, sshPort, pemFile, files, basePath);
+        sshHelperMocked.close();
     }
 
     @Test
     public void FailingScpFilesTest() throws Exception {
-        PowerMockito.mockStatic(SshHelper.class);
         String basePath = "/tmp";
         String[] files = new String[] { "file1.txt" };
         int sshPort = 3922;
         String controlIp = "10.0.0.10";
         String destPath = "/home/cloud/";
         File pemFile = new File("/root/.ssh/id_rsa");
-        PowerMockito.doThrow(new Exception()).when(SshHelper.class, "scpTo", Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString());
+        MockedStatic<SshHelper> sshHelperMocked = Mockito.mockStatic(SshHelper.class);
+        sshHelperMocked.when(() ->
+                SshHelper.scpTo(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString()
+                )).thenThrow(new Exception());
         try {
             FileUtil.scpPatchFiles(controlIp, destPath, sshPort, pemFile, files, basePath);
         } catch (Exception e) {
             Assert.assertEquals("Failed to scp files to system VM", e.getMessage());
         }
+        sshHelperMocked.close();
 
     }
 
diff --git a/utils/src/test/java/com/cloud/utils/ScriptTest.java b/utils/src/test/java/com/cloud/utils/ScriptTest.java
index e6ed64a..12963dc 100644
--- a/utils/src/test/java/com/cloud/utils/ScriptTest.java
+++ b/utils/src/test/java/com/cloud/utils/ScriptTest.java
@@ -130,6 +130,6 @@
     public void testFindScript() {
         Assume.assumeTrue(SystemUtils.IS_OS_LINUX);
         String script = Script.findScript("/bin", "pwd");
-        Assert.assertNotNull("/bin/pwd shoud be there on linux", script);
+        Assert.assertNotNull("/bin/pwd should be there on linux", script);
     }
 }
diff --git a/utils/src/test/java/com/cloud/utils/TestProfiler.java b/utils/src/test/java/com/cloud/utils/TestProfiler.java
index 1520963..1d2d5f7 100644
--- a/utils/src/test/java/com/cloud/utils/TestProfiler.java
+++ b/utils/src/test/java/com/cloud/utils/TestProfiler.java
@@ -23,16 +23,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
 
 import com.cloud.utils.testcase.Log4jEnabledTestCase;
+import org.mockito.junit.MockitoJUnitRunner;
 
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({ "javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*",
-        "org.xml.*", "org.w3c.dom.*", "com.sun.org.apache.xalan.*", "javax.activation.*" })
-@PrepareForTest(Profiler.class)
+@RunWith(MockitoJUnitRunner.class)
 public class TestProfiler extends Log4jEnabledTestCase {
 
     private static final long SLEEP_TIME_NANO = 1000000000L;
diff --git a/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java b/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java
index 68479f5..3f1e707 100644
--- a/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java
+++ b/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java
@@ -31,18 +31,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-import org.powermock.modules.junit4.PowerMockRunnerDelegate;
 
 import com.google.common.collect.ImmutableSet;
 
-@RunWith(PowerMockRunner.class)
-@PowerMockRunnerDelegate(Parameterized.class)
-@PowerMockIgnore({"javax.xml.*", "org.apache.xerces.*", "org.xml.*", "org.w3c.*"})
+@RunWith(Parameterized.class)
 public class UriUtilsParametrizedTest {
     @FunctionalInterface
     public interface ThrowingBlock<E extends Exception> {
@@ -149,14 +143,11 @@
     }
 
     @Test
-    @PrepareForTest({UriUtils.class})
-
     public void validateUrl() throws Exception {
 
+        MockedStatic<InetAddress> inetAddressMocked = Mockito.mockStatic(InetAddress.class);
         InetAddress inetAddressMock = Mockito.mock(InetAddress.class);
-
-        PowerMockito.mockStatic(InetAddress.class);
-        PowerMockito.when(InetAddress.getByName(Mockito.anyString())).thenReturn(inetAddressMock);
+        inetAddressMocked.when(() -> InetAddress.getByName(Mockito.anyString())).thenReturn(inetAddressMock);
 
         if (expectSuccess) {
             UriUtils.validateUrl(format, url);
@@ -164,14 +155,14 @@
             assertThrows(() -> UriUtils.validateUrl(format, url), IllegalArgumentException.class);
         }
 
-        PowerMockito.verifyStatic(InetAddress.class);
-        InetAddress.getByName(Mockito.anyString());
+        inetAddressMocked.verify(() -> InetAddress.getByName(Mockito.anyString()));
 
         Mockito.verify(inetAddressMock).isAnyLocalAddress();
         Mockito.verify(inetAddressMock).isLinkLocalAddress();
         Mockito.verify(inetAddressMock).isLoopbackAddress();
         Mockito.verify(inetAddressMock).isMulticastAddress();
 
+        inetAddressMocked.close();
     }
 
     @Test
diff --git a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
index 53e2e3a..4a6c5df 100644
--- a/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/net/NetUtilsTest.java
@@ -52,15 +52,12 @@
 import com.googlecode.ipv6.IPv6Network;
 
 import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.runners.MockitoJUnitRunner;
 
 
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"jdk.xml.internal.*", "javax.xml.parsers.*", "org.xml.sax.*", "org.w3c.dom.*"})
+@RunWith(MockitoJUnitRunner.class)
 public class NetUtilsTest {
     private static final String WIDE_SHARED_NET_CIDR_IP = "10.20.0.0";
     private static final List<String> WIDE_SHARED_NET_USED_IPS = List.of("10.20.0.22", "10.20.1.22", "10.20.2.22");
@@ -815,34 +812,34 @@
     }
 
     @Test
-    @PrepareForTest(NetUtils.class)
     public void getNetworkInterfaceTestReturnNullWhenGetByNameReturnsNull() throws SocketException {
-        PowerMockito.mockStatic(NetworkInterface.class);
-        PowerMockito.when(NetworkInterface.getByName(Mockito.anyString())).thenReturn(null);
+        MockedStatic<NetworkInterface> networkInterfaceMocked = Mockito.mockStatic(NetworkInterface.class);
+        Mockito.when(NetworkInterface.getByName(Mockito.anyString())).thenReturn(null);
         NetworkInterface result = NetUtils.getNetworkInterface("  test   ");
 
         Assert.assertNull(result);
+        networkInterfaceMocked.close();
     }
 
     @Test
-    @PrepareForTest(NetUtils.class)
     public void getNetworkInterfaceTestReturnNullWhenGetByNameThrowsException() throws SocketException {
-        PowerMockito.mockStatic(NetworkInterface.class);
-        PowerMockito.when(NetworkInterface.getByName(Mockito.anyString())).thenThrow(SocketException.class);
+        MockedStatic<NetworkInterface> networkInterfaceMocked = Mockito.mockStatic(NetworkInterface.class);
+        Mockito.when(NetworkInterface.getByName(Mockito.anyString())).thenThrow(SocketException.class);
         NetworkInterface result = NetUtils.getNetworkInterface("  test   ");
 
         Assert.assertNull(result);
+        networkInterfaceMocked.close();
     }
 
     @Test
-    @PrepareForTest(NetUtils.class)
     public void getNetworkInterfaceTestReturnInterfaceReturnedByGetByName() throws SocketException {
-        NetworkInterface expected = PowerMockito.mock(NetworkInterface.class);
-        PowerMockito.mockStatic(NetworkInterface.class);
-        PowerMockito.when(NetworkInterface.getByName(Mockito.anyString())).thenReturn(expected);
+        MockedStatic<NetworkInterface> networkInterfaceMocked = Mockito.mockStatic(NetworkInterface.class);
+        NetworkInterface expected = Mockito.mock(NetworkInterface.class);
+        Mockito.when(NetworkInterface.getByName(Mockito.anyString())).thenReturn(expected);
 
         NetworkInterface result = NetUtils.getNetworkInterface("  test   ");
 
         Assert.assertEquals(expected, result);
+        networkInterfaceMocked.close();
     }
 }
diff --git a/utils/src/test/java/com/cloud/utils/ssh/SshHelperTest.java b/utils/src/test/java/com/cloud/utils/ssh/SshHelperTest.java
index 37bd089..04d8443 100644
--- a/utils/src/test/java/com/cloud/utils/ssh/SshHelperTest.java
+++ b/utils/src/test/java/com/cloud/utils/ssh/SshHelperTest.java
@@ -25,38 +25,31 @@
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.runners.MockitoJUnitRunner;
 
 import com.trilead.ssh2.ChannelCondition;
 import com.trilead.ssh2.Connection;
 import com.trilead.ssh2.Session;
 
-@PrepareForTest({ Thread.class, SshHelper.class })
-@PowerMockIgnore({ "javax.management.*", "com.sun.org.apache.xerces.*", "javax.xml.*",
-        "org.xml.*", "org.w3c.dom.*", "com.sun.org.apache.xalan.*", "javax.activation.*" })
-@RunWith(PowerMockRunner.class)
+@RunWith(MockitoJUnitRunner.class)
 public class SshHelperTest {
 
     @Test
     public void canEndTheSshConnectionTest() throws Exception {
-        PowerMockito.spy(SshHelper.class);
+        MockedStatic<SshHelper> sshHelperMocked = Mockito.mockStatic(SshHelper.class, Mockito.CALLS_REAL_METHODS);
         Session mockedSession = Mockito.mock(Session.class);
 
-        PowerMockito.doReturn(true).when(SshHelper.class, "isChannelConditionEof", Mockito.anyInt());
-        Mockito.when(mockedSession.waitForCondition(ChannelCondition.EXIT_STATUS, 1l)).thenReturn(0);
-        PowerMockito.doNothing().when(SshHelper.class, "throwSshExceptionIfConditionsTimeout", Mockito.anyInt());
+        Mockito.when(SshHelper.isChannelConditionEof(Mockito.anyInt())).thenReturn(true);
+        Mockito.when(mockedSession.waitForCondition(ChannelCondition.EXIT_STATUS, 1L)).thenReturn(0);
 
         SshHelper.canEndTheSshConnection(1, mockedSession, 0);
-
-        PowerMockito.verifyStatic(SshHelper.class);
-        SshHelper.isChannelConditionEof(Mockito.anyInt());
-        SshHelper.throwSshExceptionIfConditionsTimeout(Mockito.anyInt());
+        sshHelperMocked.verify(() -> SshHelper.isChannelConditionEof(Mockito.anyInt()), Mockito.times(1));
+        sshHelperMocked.verify(() -> SshHelper.throwSshExceptionIfConditionsTimeout(Mockito.anyInt()));
 
         Mockito.verify(mockedSession).waitForCondition(ChannelCondition.EXIT_STATUS, 1l);
+        sshHelperMocked.close();
     }
 
     @Test(expected = SshException.class)
@@ -143,7 +136,6 @@
     @Test
     public void openConnectionSessionTest() throws IOException, InterruptedException {
         Connection conn = Mockito.mock(Connection.class);
-        PowerMockito.mockStatic(Thread.class);
         SshHelper.openConnectionSession(conn);
 
         Mockito.verify(conn).openSession();
diff --git a/utils/src/test/java/com/cloud/utils/validation/ChecksumUtilTest.java b/utils/src/test/java/com/cloud/utils/validation/ChecksumUtilTest.java
index 08cc389..2b99275 100644
--- a/utils/src/test/java/com/cloud/utils/validation/ChecksumUtilTest.java
+++ b/utils/src/test/java/com/cloud/utils/validation/ChecksumUtilTest.java
@@ -20,37 +20,35 @@
 import org.apache.cloudstack.utils.security.DigestHelper;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
+
 
 import java.io.File;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-@PrepareForTest(value = {Script.class, DigestHelper.class})
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.parsers.*", "javax.xml.*", "org.w3c.dom.*", "org.xml.*"})
+@RunWith(MockitoJUnitRunner.class)
 public class ChecksumUtilTest {
 
     @Test
     public void invalidFileForCheckSumValidationTest() {
-        PowerMockito.mockStatic(Script.class);
+        MockedStatic<Script> scriptMocked = Mockito.mockStatic(Script.class);
         Mockito.when(Script.findScript(Mockito.anyString(), Mockito.anyString())).thenReturn(null);
         try {
             ChecksumUtil.calculateCurrentChecksum(Mockito.anyString(), Mockito.anyString());
         } catch (Exception e) {
             assertTrue(e.getMessage().contains("Unable to find cloudScripts path, cannot update SystemVM"));
         }
+        scriptMocked.close();
     }
 
     @Test
     public void generateChecksumTest() {
-        PowerMockito.mockStatic(Script.class);
-        PowerMockito.mockStatic(DigestHelper.class);
+        MockedStatic<Script> scriptMocked = Mockito.mockStatic(Script.class);
+        MockedStatic<DigestHelper> digestHelperMocked = Mockito.mockStatic(DigestHelper.class);
         Mockito.when(Script.findScript(Mockito.anyString(), Mockito.anyString())).thenReturn("/dummyPath");
         Mockito.when(DigestHelper.calculateChecksum(Mockito.any(File.class))).thenReturn("dummy-checksum");
         try {
@@ -58,5 +56,7 @@
         } catch (Exception e) {
             fail("Failed to generate checksum");
         }
+        scriptMocked.close();
+        digestHelperMocked.close();
     }
 }
diff --git a/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java b/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java
index 1567d89..d181462 100644
--- a/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java
+++ b/utils/src/test/java/org/apache/cloudstack/utils/jsinterpreter/JsInterpreterTest.java
@@ -35,16 +35,12 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
 
 import javax.script.ScriptEngine;
 
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"javax.xml.*", "org.apache.xerces.*", "org.xml.*", "org.w3c.*"})
-@PrepareForTest(JsInterpreter.class)
+@RunWith(MockitoJUnitRunner.class)
 public class JsInterpreterTest {
     private long timeout = 2000;
 
@@ -95,7 +91,6 @@
     public void executeScriptTestReturnResultOfScriptExecution() {
         String script = "5";
         Object expected = new Object();
-        Mockito.doReturn(script).when(jsInterpreterSpy).addVariablesToScript(Mockito.anyString());
         Mockito.doReturn(expected).when(jsInterpreterSpy).executeScript(Mockito.anyString());
 
         Object result = jsInterpreterSpy.executeScript(script);
@@ -173,9 +168,8 @@
     }
 
     @Test
-    @PrepareForTest(NashornScriptEngineFactory.class)
     public void setScriptEngineDisablingJavaLanguageTest() {
-        NashornScriptEngineFactory nashornScriptEngineFactoryMock = Mockito.mock(NashornScriptEngineFactory.class);
+        NashornScriptEngineFactory nashornScriptEngineFactoryMock = Mockito.spy(NashornScriptEngineFactory.class);
         ScriptEngine scriptEngineMock = Mockito.mock(ScriptEngine.class);
 
         Mockito.doReturn(scriptEngineMock).when(nashornScriptEngineFactoryMock).getScriptEngine(Mockito.anyString());
diff --git a/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java b/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java
index 4a2a2a6..75befde 100644
--- a/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java
+++ b/utils/src/test/java/org/apache/cloudstack/utils/reflectiontostringbuilderutils/ReflectionToStringBuilderUtilsTest.java
@@ -33,16 +33,12 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.reflections.ReflectionUtils;
 
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"org.w3c.*", "javax.xml.*", "org.xml.*"})
-@PrepareForTest(ReflectionToStringBuilderUtils.class)
+@RunWith(MockitoJUnitRunner.class)
 public class ReflectionToStringBuilderUtilsTest extends TestCase {
 
     private static final Set<ToStringStyle> TO_STRING_STYLES = new HashSet<>(Arrays.asList(ToStringStyle.DEFAULT_STYLE, ToStringStyle.JSON_STYLE, ToStringStyle.MULTI_LINE_STYLE,
@@ -95,60 +91,63 @@
 
     @Test
     public void validateGetObjectClassInvalidObjectMustReturnNull(){
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(false);
-
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(false);
         Class<?> result = ReflectionToStringBuilderUtils.getObjectClass("test");
 
         Assert.assertNull(result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateGetObjectClassObjectIsNotACollectionMustReturnObjectClass(){
         Class<?> expectedResult = classToReflect;
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(false);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(false);
 
         Class<?> result = ReflectionToStringBuilderUtils.getObjectClass("test");
 
         Assert.assertEquals(expectedResult, result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateGetObjectClassObjectIsAnEmptyCollectionMustReturnNull(){
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
 
         Class<?> result = ReflectionToStringBuilderUtils.getObjectClass(new ArrayList<String>());
 
         Assert.assertNull(result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateGetObjectClassObjectIsACollectionWithOnlyNullValuesMustReturnNull(){
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
 
         Class<?> result = ReflectionToStringBuilderUtils.getObjectClass(new ArrayList<String>(Arrays.asList(null, null)));
 
         Assert.assertNull(result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateGetObjectClassObjectIsACollectionWithAtLeastOneObjectsMustReturnObjectClass(){
         Class<?> expectedResult = classToReflect;
-
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isValidObject(Mockito.any())).thenReturn(true);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
 
         Class<?> result = ReflectionToStringBuilderUtils.getObjectClass(new ArrayList<>(Arrays.asList(null, "test1")));
 
         Assert.assertEquals(expectedResult, result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
@@ -163,12 +162,13 @@
 
     @Test
     public void validateGetNonSelectedFieldsNullObjectClassMustReturnNull(){
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.getObjectClass(Mockito.any())).thenReturn(null);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getObjectClass(Mockito.any())).thenReturn(null);
 
         String[] result = ReflectionToStringBuilderUtils.getNonSelectedFields(null, "test1", "test2");
 
         Assert.assertNull(result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
@@ -250,26 +250,28 @@
 
     @Test
     public void validateReflectOnlySelectedFieldsNullNonSelectedFieldsMustReturnNull() throws Exception{
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(null);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(null);
 
         TO_STRING_STYLES.forEach(style -> {
             String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(null, style, "-");
             Assert.assertNull(result);
         });
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateReflectOnlySelectedFieldsEmptyNonSelectedFieldsMustReturnEmptyString() throws Exception{
         String expectedResult = "";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(new String[0]);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(new String[0]);
 
         TO_STRING_STYLES.forEach(style -> {
             String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(null, style, "-");
             Assert.assertEquals(expectedResult, result);
         });
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
@@ -277,80 +279,85 @@
         String fieldToRemove = classToReflectRemovedField;
         String expectedResult = "test";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(classToReflectFieldsNamesArray);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expectedResult);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(classToReflectFieldsNamesArray);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expectedResult);
 
         TO_STRING_STYLES.forEach(style -> {
             String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(new Object(), style, "-", fieldToRemove);
             Assert.assertEquals(expectedResult, result);
         });
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateReflectOnlySelectedFieldsObjectIsNotACollectionMustReflectObject() throws Exception{
         String expectedResult = "test";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(classToReflectFieldsNamesArray);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(null);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getNonSelectedFields(Mockito.any(), Mockito.any())).thenReturn(classToReflectFieldsNamesArray);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(null);
 
         for (ToStringStyle style : TO_STRING_STYLES){
-            PowerMockito.doReturn(expectedResult).when(ReflectionToStringBuilderUtils.class, "getReflectedObject", Mockito.any(), Mockito.any(), Mockito.any());
+            reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.getReflectedObject(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(expectedResult);
             String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(expectedResult, style, "-", classToReflectFieldsNamesArray);
             Assert.assertEquals(expectedResult, result);
         }
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateReflectOnlySelectedFieldsDefaultStyleReflectionNullMustReturnNull(){
         String expectedResult = null;
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(null);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(null);
 
         String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(new Object(), (String[]) null);
         Assert.assertEquals(expectedResult, result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateReflectOnlySelectedFieldsDefaultStyleReflectCollectionMustReturnValue(){
         String expectedResult = "[test]";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn("test");
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn("test");
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(true);
 
         String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(new Object());
         Assert.assertEquals(expectedResult, result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void validateReflectOnlySelectedFieldsDefaultStyleReflectMustReturnValue(){
         String expectedResult = "test";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expectedResult);
-        PowerMockito.when(ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(false);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectOnlySelectedFields(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expectedResult);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.isCollection(Mockito.any())).thenReturn(false);
 
         String result = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(new Object());
         Assert.assertEquals(expectedResult, result);
+        reflectionToStringBuilderUtilsMocked.close();
     }
 
     @Test
     public void reflectCollectionTestCallBaseReflectCollectionMethodWithDefaultParameters() {
         String expected = "test";
 
-        PowerMockito.spy(ReflectionToStringBuilderUtils.class);
-        PowerMockito.when(ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expected);
+        MockedStatic<ReflectionToStringBuilderUtils> reflectionToStringBuilderUtilsMocked = Mockito.mockStatic(ReflectionToStringBuilderUtils.class, Mockito.CALLS_REAL_METHODS);
+        reflectionToStringBuilderUtilsMocked.when(() -> ReflectionToStringBuilderUtils.reflectCollection(Mockito.any(), Mockito.any(), Mockito.anyString(), Mockito.any())).thenReturn(expected);
 
         Object object = Mockito.mock(Object.class);
         String result = ReflectionToStringBuilderUtils.reflectCollection(object);
 
         Assert.assertEquals(expected, result);
 
-        PowerMockito.verifyStatic(ReflectionToStringBuilderUtils.class);
         String[] excludeFields = null;
-        ReflectionToStringBuilderUtils.reflectCollection(object, DEFAULT_STYLE, DEFAULT_MULTIPLE_VALUES_SEPARATOR, excludeFields);
+        reflectionToStringBuilderUtilsMocked.verify(() -> ReflectionToStringBuilderUtils.reflectCollection(object, DEFAULT_STYLE, DEFAULT_MULTIPLE_VALUES_SEPARATOR, excludeFields));
+        reflectionToStringBuilderUtilsMocked.close();
     }
 }
diff --git a/utils/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/utils/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d4
--- /dev/null
+++ b/utils/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml
index 89bf9c6..8d7b716 100644
--- a/vmware-base/pom.xml
+++ b/vmware-base/pom.xml
@@ -24,7 +24,7 @@
     <parent>
         <groupId>org.apache.cloudstack</groupId>
         <artifactId>cloudstack</artifactId>
-        <version>4.18.1.0-SNAPSHOT</version>
+        <version>4.19.0.0-SNAPSHOT</version>
     </parent>
     <dependencies>
         <dependency>
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java
index 430eb6d..153efe2 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java
@@ -114,7 +114,7 @@
                 key = field.getKey();
             } catch (Exception e) {
                 // assuming the exception is caused by concurrent operation from other places
-                // so we retieve the key again
+                // so we retrieve the key again
                 key = getCustomFieldKey(fieldName);
             }
         }
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java
index 095b20c..e78c843 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java
@@ -69,7 +69,7 @@
 //
 public class ClusterMO extends BaseMO implements VmwareHypervisorHost {
     private static final Logger s_logger = Logger.getLogger(ClusterMO.class);
-    private ManagedObjectReference _environmentBrowser = null;
+    protected ManagedObjectReference _environmentBrowser = null;
 
     public ClusterMO(VmwareContext context, ManagedObjectReference morCluster) {
         super(context, morCluster);
@@ -740,4 +740,22 @@
             throw new CloudRuntimeException(msg);
         }
     }
+
+    @Override
+    public GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception {
+        VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
+        List<GuestOsDescriptor> guestDescriptors = vmConfigOption.getGuestOSDescriptor();
+        for (GuestOsDescriptor descriptor : guestDescriptors) {
+            if (guestOsId != null && guestOsId.equalsIgnoreCase(descriptor.getId())) {
+                return descriptor;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public List<GuestOsDescriptor> getGuestOsDescriptors() throws Exception {
+        VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
+        return vmConfigOption.getGuestOSDescriptor();
+    }
 }
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
index a76e7d9..497c172 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
@@ -281,7 +281,7 @@
                 return false;
             }
         } catch (Exception e) {
-            s_logger.error(String.format("Cannot move file to destination datastore due to file %s due to exeception %s", srcFullPath, e.getMessage()));
+            s_logger.error(String.format("Cannot move file to destination datastore due to file %s due to exception %s", srcFullPath, e.getMessage()));
             return false;
         }
 
@@ -307,7 +307,7 @@
         String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), fullPath);
 
         // TODO, VMware currently does not have a formal API to list Datastore directory content,
-        // folloing hacking may have performance hit if datastore has a large number of files
+        // following hacking may have performance hit if datastore has a large number of files
         return _context.listDatastoreDirContent(url);
     }
 
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java
index 15d80fc..3b96e7e 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java
@@ -36,6 +36,7 @@
 import com.vmware.vim25.CustomFieldStringValue;
 import com.vmware.vim25.DatastoreSummary;
 import com.vmware.vim25.DynamicProperty;
+import com.vmware.vim25.GuestOsDescriptor;
 import com.vmware.vim25.HostConfigManager;
 import com.vmware.vim25.HostConnectInfo;
 import com.vmware.vim25.HostFirewallInfo;
@@ -1088,7 +1089,7 @@
 
         HostListSummaryQuickStats stats = getHostQuickStats();
         if (stats.getOverallCpuUsage() == null || stats.getOverallMemoryUsage() == null)
-            throw new Exception("Unable to get valid overal CPU/Memory usage data, host may be disconnected");
+            throw new Exception("Unable to get valid overall CPU/Memory usage data, host may be disconnected");
 
         resourceSummary.setEffectiveCpu(totalCpu - stats.getOverallCpuUsage());
 
@@ -1165,6 +1166,26 @@
         return null;
     }
 
+    @Override
+    public GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception {
+        ManagedObjectReference morParent = getParentMor();
+        if (morParent.getType().equals("ClusterComputeResource")) {
+            ClusterMO clusterMo = new ClusterMO(_context, morParent);
+            return clusterMo.getGuestOsDescriptor(guestOsId);
+        }
+        return null;
+    }
+
+    @Override
+    public List<GuestOsDescriptor> getGuestOsDescriptors() throws Exception {
+        ManagedObjectReference morParent = getParentMor();
+        if (morParent.getType().equals("ClusterComputeResource")) {
+            ClusterMO clusterMo = new ClusterMO(_context, morParent);
+            return clusterMo.getGuestOsDescriptors();
+        }
+        return null;
+    }
+
     public String getHostManagementIp(String managementPortGroup) throws Exception {
         HostNetworkInfo netInfo = getHostNetworkInfo();
 
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HttpNfcLeaseMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HttpNfcLeaseMO.java
index 6e4980c..367a21b 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HttpNfcLeaseMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HttpNfcLeaseMO.java
@@ -159,11 +159,11 @@
         public void run() {
             while (!_done) {
                 try {
-                    Thread.sleep(1000);            // update progess every 1 second
+                    Thread.sleep(1000);            // update progress every 1 second
                     updateLeaseProgress(_percent);
                 } catch (InterruptedException e) {
                     if (s_logger.isInfoEnabled())
-                        s_logger.info("ProgressReporter is interrupted, quiting");
+                        s_logger.info("ProgressReporter is interrupted, quitting");
                     break;
                 } catch (Exception e) {
                     s_logger.warn("Unexpected exception ", e);
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
index 5cc8899..12ef462 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java
@@ -1866,7 +1866,7 @@
 
     /**
      * deploys a new VM from a ovf spec. It ignores network, defaults locale to 'US'
-     * @throws Exception shoud be a VmwareResourceException
+     * @throws Exception should be a VmwareResourceException
      */
     public static void importVmFromOVF(VmwareHypervisorHost host, String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, ManagedObjectReference morRp,
                                        ManagedObjectReference morHost, String configurationId) throws CloudRuntimeException, IOException {
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
index cecdb47..bd9bc03 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
@@ -1881,7 +1881,7 @@
 
                         // tar files into OVA
                         if (packToOva) {
-                            // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature)
+                            // Important! we need to sync file system before we can safely use tar to work around a linux kernel bug(or feature)
                             s_logger.info("Sync file system before we package OVA...");
 
                             Script commandSync = new Script(true, "sync", 0, s_logger);
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java
index ce2f178..2177b06 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java
@@ -20,6 +20,7 @@
 
 import com.vmware.vim25.ClusterDasConfigInfo;
 import com.vmware.vim25.ComputeResourceSummary;
+import com.vmware.vim25.GuestOsDescriptor;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.ObjectContent;
 import com.vmware.vim25.VirtualMachineConfigSpec;
@@ -91,5 +92,10 @@
     ComputeResourceSummary getHyperHostHardwareSummary() throws Exception;
 
     LicenseAssignmentManagerMO getLicenseAssignmentManager() throws Exception;
+
     String getRecommendedDiskController(String guestOsId) throws Exception;
+
+    GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception;
+
+    List<GuestOsDescriptor> getGuestOsDescriptors() throws Exception;
 }
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
index 1599408..9da2ee3 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
@@ -550,7 +550,7 @@
      * Url for the query
      *     https://vsphere-1.lab.vmops.com/folder/Fedora-clone-test?dcPath=cupertino&dsName=NFS+datastore
      *
-     * Returned conent from vSphere
+     * Returned content from vSphere
      *
         <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
         <html>
diff --git a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/HostMOTest.java b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/HostMOTest.java
new file mode 100644
index 0000000..74cd142
--- /dev/null
+++ b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/HostMOTest.java
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.hypervisor.vmware.mo;
+
+import com.cloud.hypervisor.vmware.util.VmwareClient;
+import com.cloud.hypervisor.vmware.util.VmwareContext;
+import com.vmware.vim25.GuestOsDescriptor;
+import com.vmware.vim25.ManagedObjectReference;
+import com.vmware.vim25.VimPortType;
+import com.vmware.vim25.VirtualMachineConfigOption;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(HostMO.class)
+public class HostMOTest {
+
+    @Mock
+    VmwareContext _context ;
+    @Mock
+    VmwareClient _client;
+    @Mock
+    ManagedObjectReference _mor;
+    @Mock
+    ManagedObjectReference _environmentBrowser;
+
+    @Mock
+    VirtualMachineConfigOption vmConfigOption;
+
+    @Mock
+    GuestOsDescriptor guestOsDescriptor;
+
+    HostMO hostMO ;
+    ClusterMO clusterMO ;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        hostMO = new HostMO(_context, _mor);
+        clusterMO = new ClusterMO(_context, _mor);
+        clusterMO._environmentBrowser = _environmentBrowser;
+        when(_context.getVimClient()).thenReturn(_client);
+        when(_client.getDynamicProperty(any(ManagedObjectReference.class), eq("parent"))).thenReturn(_mor);
+        when(_mor.getType()).thenReturn("ClusterComputeResource");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+
+    }
+
+    @Test
+    public void testGetGuestOsDescriptors() throws Exception {
+        VimPortType vimPortType = PowerMockito.mock(VimPortType.class);
+        Mockito.when(_context.getService()).thenReturn(vimPortType);
+        Mockito.when(vimPortType.queryConfigOption(_environmentBrowser, null, null)).thenReturn(vmConfigOption);
+        PowerMockito.whenNew(ClusterMO.class).withArguments(_context, _mor).thenReturn(clusterMO);
+
+        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
+        guestOsDescriptors.add(guestOsDescriptor);
+        Mockito.when(clusterMO.getGuestOsDescriptors()).thenReturn(guestOsDescriptors);
+        List<GuestOsDescriptor> result = hostMO.getGuestOsDescriptors();
+
+        Assert.assertEquals(guestOsDescriptor, result.get(0));
+    }
+
+    @Test
+    public void testGetGuestOsDescriptor() throws Exception {
+        VimPortType vimPortType = PowerMockito.mock(VimPortType.class);
+        Mockito.when(_context.getService()).thenReturn(vimPortType);
+        Mockito.when(vimPortType.queryConfigOption(_environmentBrowser, null, null)).thenReturn(vmConfigOption);
+        PowerMockito.whenNew(ClusterMO.class).withArguments(_context, _mor).thenReturn(clusterMO);
+
+        Mockito.when(guestOsDescriptor.getId()).thenReturn("1");
+        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
+        guestOsDescriptors.add(guestOsDescriptor);
+        Mockito.when(vmConfigOption.getGuestOSDescriptor()).thenReturn(guestOsDescriptors);
+        GuestOsDescriptor result = hostMO.getGuestOsDescriptor("1");
+
+        Assert.assertEquals(guestOsDescriptor, result);
+    }
+}