From 1380c604b1eebc3ae1238c38773c9f86067fc12b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 5 Jan 2023 09:59:28 +0100 Subject: [PATCH] server: add Host Control Plane State to uservm and systemvm response (#6946) Co-authored-by: dahn --- .../java/com/cloud/host/ControlState.java | 41 +++ .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/response/DomainRouterResponse.java | 8 + .../api/response/SystemVmResponse.java | 12 + .../api/response/UserVmResponse.java | 12 + .../java/com/cloud/host/ControlStateTest.java | 109 ++++++++ .../META-INF/db/schema-41720to41800.sql | 4 + .../java/com/cloud/api/ApiResponseHelper.java | 2 + .../query/dao/DomainRouterJoinDaoImpl.java | 2 + .../api/query/dao/UserVmJoinDaoImpl.java | 4 + .../api/query/vo/DomainRouterJoinVO.java | 16 ++ .../com/cloud/api/query/vo/UserVmJoinVO.java | 18 +- .../smoke/test_host_control_state.py | 252 ++++++++++++++++++ ui/public/locales/en.json | 3 + ui/src/components/view/DetailsTab.vue | 7 + ui/src/components/widgets/Console.vue | 2 +- ui/src/config/section/compute.js | 10 +- ui/src/config/section/infra/ilbvms.js | 4 +- ui/src/config/section/infra/routers.js | 4 +- ui/src/config/section/infra/systemVms.js | 4 +- 20 files changed, 509 insertions(+), 6 deletions(-) create mode 100644 api/src/main/java/com/cloud/host/ControlState.java create mode 100644 api/src/test/java/com/cloud/host/ControlStateTest.java create mode 100644 test/integration/smoke/test_host_control_state.py diff --git a/api/src/main/java/com/cloud/host/ControlState.java b/api/src/main/java/com/cloud/host/ControlState.java new file mode 100644 index 00000000000..335125dde20 --- /dev/null +++ b/api/src/main/java/com/cloud/host/ControlState.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.host; + +import com.cloud.resource.ResourceState; + +public enum ControlState { + Enabled, + Disabled, + Offline, + Maintenance, + Unknown; + + public static ControlState getControlState(Status hostStatus, ResourceState hostResourceState) { + if (hostStatus == null || Status.Unknown.equals(hostStatus) || hostResourceState == null) { + return ControlState.Unknown; + } else if (hostStatus.lostConnection()) { + return Offline; + } else if (ResourceState.isMaintenanceState(hostResourceState)) { + return Maintenance; + } else if (ResourceState.Enabled.equals(hostResourceState)) { + return Enabled; + } else { + return Disabled; + } + } +} 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 96ed750852e..3cb937e8427 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -203,6 +203,7 @@ public class ApiConstants { public static final String HOST_ID = "hostid"; public static final String HOST_IDS = "hostids"; public static final String HOST_NAME = "hostname"; + public static final String HOST_CONTROL_STATE = "hostcontrolstate"; public static final String HOSTS_MAP = "hostsmap"; public static final String HYPERVISOR = "hypervisor"; public static final String INLINE = "inline"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java index a5fa2bd08c2..99e5f6ccdfa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java @@ -89,6 +89,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements @Param(description = "the hostname for the router") private String hostName; + @SerializedName(ApiConstants.HOST_CONTROL_STATE) + @Param(description = "the control state of the host for the router") + private String hostControlState; + @SerializedName("hypervisor") @Param(description = "the hypervisor on which the template runs") private String hypervisor; @@ -302,6 +306,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements this.hostName = hostName; } + public void setHostControlState(String hostControlState) { + this.hostControlState = hostControlState; + } + public String getHypervisor() { return hypervisor; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java index ce2b406d15f..69b9b4cad9c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java @@ -90,6 +90,10 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the hostname for the system VM") private String hostName; + @SerializedName(ApiConstants.HOST_CONTROL_STATE) + @Param(description = "the control state of the host for the system VM") + private String hostControlState; + @SerializedName("hypervisor") @Param(description = "the hypervisor on which the template runs") private String hypervisor; @@ -283,6 +287,14 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { this.hostName = hostName; } + public String getHostControlState() { + return hostControlState; + } + + public void setHostControlState(String hostControlState) { + this.hostControlState = hostControlState; + } + public String getHypervisor() { return hypervisor; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index 108e480deb0..f2903b2626c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -118,6 +118,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the name of the host for the virtual machine") private String hostName; + @SerializedName(ApiConstants.HOST_CONTROL_STATE) + @Param(description = "the control state of the host for the virtual machine") + private String hostControlState; + @SerializedName(ApiConstants.TEMPLATE_ID) @Param(description = "the ID of the template for the virtual machine. A -1 is returned if the virtual machine was created from an ISO file.") private String templateId; @@ -461,6 +465,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co return hostName; } + public String getHostControlState() { + return hostControlState; + } + public String getTemplateId() { return templateId; } @@ -703,6 +711,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co this.hostName = hostName; } + public void setHostControlState(String hostControlState) { + this.hostControlState = hostControlState; + } + public void setTemplateId(String templateId) { this.templateId = templateId; } diff --git a/api/src/test/java/com/cloud/host/ControlStateTest.java b/api/src/test/java/com/cloud/host/ControlStateTest.java new file mode 100644 index 00000000000..2e8b38cd54a --- /dev/null +++ b/api/src/test/java/com/cloud/host/ControlStateTest.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.host; + +import com.cloud.resource.ResourceState; + +import junit.framework.TestCase; + +import org.junit.Assert; +import org.junit.Test; + +public class ControlStateTest extends TestCase { + + void verifyHostControlState(Status hostStatus, ResourceState hostResourceState, ControlState expectedControlState) { + Assert.assertEquals(expectedControlState, ControlState.getControlState(hostStatus, hostResourceState)); + } + + @Test + public void testHostControlState1() { + // Unknown state + verifyHostControlState(null, null, ControlState.Unknown); + verifyHostControlState(null, ResourceState.Enabled, ControlState.Unknown); + verifyHostControlState(Status.Up, null, ControlState.Unknown); + verifyHostControlState(Status.Disconnected, null, ControlState.Unknown); + verifyHostControlState(Status.Down, null, ControlState.Unknown); + + verifyHostControlState(Status.Unknown, null, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Enabled, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.ErrorInPrepareForMaintenance, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.PrepareForMaintenance, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.ErrorInMaintenance, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Maintenance, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Creating, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Disabled, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Error, ControlState.Unknown); + verifyHostControlState(Status.Unknown, ResourceState.Degraded, ControlState.Unknown); + } + @Test + public void testHostControlState2() { + // Host is Up and Enabled + verifyHostControlState(Status.Creating, ResourceState.Enabled, ControlState.Enabled); + verifyHostControlState(Status.Connecting, ResourceState.Enabled, ControlState.Enabled); + verifyHostControlState(Status.Up, ResourceState.Enabled, ControlState.Enabled); + } + + @Test + public void testHostControlState3() { + // Host is Up and not Enabled + verifyHostControlState(Status.Up, ResourceState.Creating, ControlState.Disabled); + verifyHostControlState(Status.Up, ResourceState.Disabled, ControlState.Disabled); + verifyHostControlState(Status.Up, ResourceState.Error, ControlState.Disabled); + verifyHostControlState(Status.Up, ResourceState.Degraded, ControlState.Disabled); + + // Host is Creating and not Enabled + verifyHostControlState(Status.Creating, ResourceState.Creating, ControlState.Disabled); + verifyHostControlState(Status.Creating, ResourceState.Disabled, ControlState.Disabled); + verifyHostControlState(Status.Creating, ResourceState.Error, ControlState.Disabled); + verifyHostControlState(Status.Creating, ResourceState.Degraded, ControlState.Disabled); + + // Host is Connecting and not Enabled + verifyHostControlState(Status.Connecting, ResourceState.Creating, ControlState.Disabled); + verifyHostControlState(Status.Connecting, ResourceState.Disabled, ControlState.Disabled); + verifyHostControlState(Status.Connecting, ResourceState.Error, ControlState.Disabled); + verifyHostControlState(Status.Connecting, ResourceState.Degraded, ControlState.Disabled); + } + + @Test + public void testHostControlState4() { + // Host is Up and Maintenance mode + verifyHostControlState(Status.Up, ResourceState.ErrorInPrepareForMaintenance, ControlState.Maintenance); + verifyHostControlState(Status.Up, ResourceState.PrepareForMaintenance, ControlState.Maintenance); + verifyHostControlState(Status.Up, ResourceState.ErrorInMaintenance, ControlState.Maintenance); + verifyHostControlState(Status.Up, ResourceState.Maintenance, ControlState.Maintenance); + } + + @Test + public void testHostControlState5() { + // Host in other states and Enabled + verifyHostControlState(Status.Down, ResourceState.Enabled, ControlState.Offline); + verifyHostControlState(Status.Disconnected, ResourceState.Enabled, ControlState.Offline); + verifyHostControlState(Status.Alert, ResourceState.Enabled, ControlState.Offline); + verifyHostControlState(Status.Removed, ResourceState.Enabled, ControlState.Offline); + verifyHostControlState(Status.Error, ResourceState.Enabled, ControlState.Offline); + verifyHostControlState(Status.Rebalancing, ResourceState.Enabled, ControlState.Offline); + + // Host in other states and Disabled + verifyHostControlState(Status.Down, ResourceState.Disabled, ControlState.Offline); + verifyHostControlState(Status.Disconnected, ResourceState.Disabled, ControlState.Offline); + verifyHostControlState(Status.Alert, ResourceState.Disabled, ControlState.Offline); + verifyHostControlState(Status.Removed, ResourceState.Disabled, ControlState.Offline); + verifyHostControlState(Status.Error, ResourceState.Disabled, ControlState.Offline); + verifyHostControlState(Status.Rebalancing, ResourceState.Disabled, ControlState.Offline); + } + +} 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 d1180c8f2c2..c3a18ec7e2d 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 @@ -65,6 +65,8 @@ CREATE VIEW `cloud`.`domain_router_view` AS host.name host_name, host.hypervisor_type, host.cluster_id cluster_id, + host.status host_status, + host.resource_state host_resource_state, vm_template.id template_id, vm_template.uuid template_uuid, service_offering.id service_offering_id, @@ -744,6 +746,8 @@ SELECT `host`.`uuid` AS `host_uuid`, `host`.`name` AS `host_name`, `host`.`cluster_id` AS `cluster_id`, + `host`.`status` AS `host_status`, + `host`.`resource_state` AS `host_resource_state`, `vm_template`.`id` AS `template_id`, `vm_template`.`uuid` AS `template_uuid`, `vm_template`.`name` AS `template_name`, diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index c7cfda4fc67..f6d30d52dfa 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -37,6 +37,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.host.ControlState; import com.cloud.utils.security.CertificateHelper; import com.cloud.user.UserData; import com.cloud.api.query.dao.UserVmJoinDao; @@ -1549,6 +1550,7 @@ public class ApiResponseHelper implements ResponseGenerator { if (host != null) { vmResponse.setHostId(host.getUuid()); vmResponse.setHostName(host.getName()); + vmResponse.setHostControlState(ControlState.getControlState(host.getStatus(), host.getResourceState()).toString()); } } diff --git a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index feeaa8b952a..83a89622bd2 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -38,6 +38,7 @@ import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.DomainRouterJoinVO; import com.cloud.dc.HostPodVO; +import com.cloud.host.ControlState; import com.cloud.network.Networks.TrafficType; import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter.Role; @@ -135,6 +136,7 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase + + +