mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
server: add Host Control Plane State to uservm and systemvm response (#6946)
Co-authored-by: dahn <daan.hoogland@gmail.com>
This commit is contained in:
parent
d0b34b7576
commit
1380c604b1
41
api/src/main/java/com/cloud/host/ControlState.java
Normal file
41
api/src/main/java/com/cloud/host/ControlState.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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";
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
109
api/src/test/java/com/cloud/host/ControlStateTest.java
Normal file
109
api/src/test/java/com/cloud/host/ControlStateTest.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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`,
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<DomainRouterJoinVO,
|
||||
if (router.getHostId() != null) {
|
||||
routerResponse.setHostId(router.getHostUuid());
|
||||
routerResponse.setHostName(router.getHostName());
|
||||
routerResponse.setHostControlState(ControlState.getControlState(router.getHostStatus(), router.getHostResourceState()).toString());
|
||||
}
|
||||
routerResponse.setPodId(router.getPodUuid());
|
||||
HostPodVO pod = ApiDBUtils.findPodById(router.getPodId());
|
||||
|
||||
@ -52,6 +52,7 @@ import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.api.ApiResponseHelper;
|
||||
import com.cloud.api.query.vo.UserVmJoinVO;
|
||||
import com.cloud.gpu.GPU;
|
||||
import com.cloud.host.ControlState;
|
||||
import com.cloud.service.ServiceOfferingDetailsVO;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.user.Account;
|
||||
@ -172,6 +173,9 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
||||
userVmResponse.setHostId(userVm.getHostUuid());
|
||||
userVmResponse.setHostName(userVm.getHostName());
|
||||
}
|
||||
if (userVm.getHostStatus() != null) {
|
||||
userVmResponse.setHostControlState(ControlState.getControlState(userVm.getHostStatus(), userVm.getHostResourceState()).toString());
|
||||
}
|
||||
|
||||
if (details.contains(VMDetails.all) || details.contains(VMDetails.tmpl)) {
|
||||
userVmResponse.setTemplateId(userVm.getTemplateUuid());
|
||||
|
||||
@ -26,11 +26,13 @@ import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.network.Network.GuestType;
|
||||
import com.cloud.network.Networks.TrafficType;
|
||||
import com.cloud.network.router.VirtualRouter;
|
||||
import com.cloud.network.router.VirtualRouter.RedundantState;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
@ -129,6 +131,12 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
|
||||
@Column(name = "host_name", nullable = false)
|
||||
private String hostName;
|
||||
|
||||
@Column(name = "host_status")
|
||||
private Status hostStatus;
|
||||
|
||||
@Column(name = "host_resource_state")
|
||||
private ResourceState hostResourceState;
|
||||
|
||||
@Column(name="hypervisor_type")
|
||||
@Enumerated(value=EnumType.STRING)
|
||||
private Hypervisor.HypervisorType hypervisorType;
|
||||
@ -354,6 +362,14 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti
|
||||
return hostName;
|
||||
}
|
||||
|
||||
public Status getHostStatus() {
|
||||
return hostStatus;
|
||||
}
|
||||
|
||||
public ResourceState getHostResourceState() {
|
||||
return hostResourceState;
|
||||
}
|
||||
|
||||
public Hypervisor.HypervisorType getHypervisorType() {
|
||||
return hypervisorType;
|
||||
}
|
||||
|
||||
@ -29,9 +29,11 @@ import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.network.Network.GuestType;
|
||||
import com.cloud.network.Networks.TrafficType;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.user.Account;
|
||||
@ -171,9 +173,15 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
||||
@Column(name = "host_uuid")
|
||||
private String hostUuid;
|
||||
|
||||
@Column(name = "host_name", nullable = false)
|
||||
@Column(name = "host_name")
|
||||
private String hostName;
|
||||
|
||||
@Column(name = "host_status")
|
||||
private Status hostStatus;
|
||||
|
||||
@Column(name = "host_resource_state")
|
||||
private ResourceState hostResourceState;
|
||||
|
||||
@Column(name = "template_id", updatable = true, nullable = true, length = 17)
|
||||
private long templateId;
|
||||
|
||||
@ -604,6 +612,14 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
||||
return hostName;
|
||||
}
|
||||
|
||||
public Status getHostStatus() {
|
||||
return hostStatus;
|
||||
}
|
||||
|
||||
public ResourceState getHostResourceState() {
|
||||
return hostResourceState;
|
||||
}
|
||||
|
||||
public long getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
|
||||
252
test/integration/smoke/test_host_control_state.py
Normal file
252
test/integration/smoke/test_host_control_state.py
Normal file
@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env python
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Tests for host control state
|
||||
"""
|
||||
|
||||
from marvin.cloudstackAPI import updateHost
|
||||
from nose.plugins.attrib import attr
|
||||
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||
from marvin.lib.common import (get_domain,
|
||||
get_zone,
|
||||
get_template,
|
||||
list_hosts,
|
||||
list_routers,
|
||||
list_ssvms)
|
||||
from marvin.lib.base import (Account,
|
||||
Domain,
|
||||
Host,
|
||||
ServiceOffering,
|
||||
VirtualMachine)
|
||||
from marvin.sshClient import SshClient
|
||||
import time
|
||||
|
||||
|
||||
class TestHostControlState(cloudstackTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.testClient = super(TestHostControlState, 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__
|
||||
|
||||
cls.template = get_template(
|
||||
cls.apiclient,
|
||||
cls.zone.id,
|
||||
cls.hypervisor
|
||||
)
|
||||
|
||||
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
||||
cls.services["template"] = cls.template.id
|
||||
cls.services["zoneid"] = cls.zone.id
|
||||
|
||||
cls._cleanup = []
|
||||
|
||||
cls.domain = Domain.create(
|
||||
cls.apiclient,
|
||||
cls.services["acl"]["domain1"]
|
||||
)
|
||||
cls._cleanup.append(cls.domain)
|
||||
cls.account = Account.create(
|
||||
cls.apiclient,
|
||||
cls.services["account"],
|
||||
domainid=cls.domain.id
|
||||
)
|
||||
cls._cleanup.append(cls.account)
|
||||
cls.service_offering = ServiceOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["service_offerings"]["tiny"]
|
||||
)
|
||||
cls._cleanup.append(cls.service_offering)
|
||||
cls.vm = VirtualMachine.create(
|
||||
cls.apiclient,
|
||||
cls.services["virtual_machine"],
|
||||
templateid=cls.template.id,
|
||||
accountid=cls.account.name,
|
||||
domainid=cls.account.domainid,
|
||||
serviceofferingid=cls.service_offering.id
|
||||
)
|
||||
cls._cleanup.append(cls.vm)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestHostControlState, cls).tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
self.apiclient = self.testClient.getApiClient()
|
||||
self.cleanup = []
|
||||
return
|
||||
|
||||
def tearDown(self):
|
||||
super(TestHostControlState, self).tearDown()
|
||||
|
||||
def disable_host(self, id):
|
||||
cmd = updateHost.updateHostCmd()
|
||||
cmd.id = id
|
||||
cmd.allocationstate = "Disable"
|
||||
response = self.apiclient.updateHost(cmd)
|
||||
self.assertEqual(response.resourcestate, "Disabled")
|
||||
|
||||
def enable_host(self, id):
|
||||
cmd = updateHost.updateHostCmd()
|
||||
cmd.id = id
|
||||
cmd.allocationstate = "Enable"
|
||||
response = self.apiclient.updateHost(cmd)
|
||||
self.assertEqual(response.resourcestate, "Enabled")
|
||||
|
||||
def get_host_ipaddress(self, hostId):
|
||||
hosts = list_hosts(
|
||||
self.apiclient,
|
||||
type='Routing',
|
||||
id=hostId
|
||||
)
|
||||
return hosts[0].ipaddress
|
||||
|
||||
def stop_agent(self, host_ipaddress):
|
||||
SshClient(host_ipaddress, port=22, user=self.hostConfig["username"], passwd=self.hostConfig["password"]).execute\
|
||||
("systemctl stop cloudstack-agent || service cloudstack-agent stop")
|
||||
|
||||
def start_agent(self, host_ipaddress):
|
||||
SshClient(host_ipaddress, port=22, user=self.hostConfig["username"], passwd=self.hostConfig["password"]).execute\
|
||||
("systemctl start cloudstack-agent || service cloudstack-agent start")
|
||||
|
||||
def verify_uservm_host_control_state(self, vm_id, state):
|
||||
list_vms = VirtualMachine.list(
|
||||
self.apiclient,
|
||||
id=vm_id
|
||||
)
|
||||
vm = list_vms[0]
|
||||
self.assertEqual(vm.hostcontrolstate,
|
||||
state,
|
||||
msg="host control state should be %s, but it is %s" % (state, vm.hostcontrolstate))
|
||||
|
||||
def verify_ssvm_host_control_state(self, vm_id, state):
|
||||
list_ssvm_response = list_ssvms(
|
||||
self.apiclient,
|
||||
id=vm_id
|
||||
)
|
||||
vm = list_ssvm_response[0]
|
||||
self.assertEqual(vm.hostcontrolstate,
|
||||
state,
|
||||
msg="host control state should be %s, but it is %s" % (state, vm.hostcontrolstate))
|
||||
|
||||
def verify_router_host_control_state(self, vm_id, state):
|
||||
list_router_response = list_routers(
|
||||
self.apiclient,
|
||||
id=vm_id
|
||||
)
|
||||
vm = list_router_response[0]
|
||||
self.assertEqual(vm.hostcontrolstate,
|
||||
state,
|
||||
msg="host control state should be %s, but it is %s" % (state, vm.hostcontrolstate))
|
||||
|
||||
@attr(tags=["basic", "advanced"], required_hardware="false")
|
||||
def test_uservm_host_control_state(self):
|
||||
""" Verify host control state for user vm """
|
||||
# 1. verify hostcontrolstate = Enabled
|
||||
# 2. Disable the host, verify hostcontrolstate = Disabled
|
||||
|
||||
list_vms = VirtualMachine.list(
|
||||
self.apiclient,
|
||||
id=self.vm.id
|
||||
)
|
||||
host_id = list_vms[0].hostid
|
||||
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Enabled")
|
||||
|
||||
self.disable_host(host_id)
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Disabled")
|
||||
|
||||
if self.hypervisor == "kvm":
|
||||
host_ipaddress = self.get_host_ipaddress(host_id)
|
||||
|
||||
self.stop_agent(host_ipaddress)
|
||||
time.sleep(5) # wait for the host to be Disconnected
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Offline")
|
||||
|
||||
self.enable_host(host_id)
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Offline")
|
||||
|
||||
self.start_agent(host_ipaddress)
|
||||
time.sleep(10) # wait for the host to be Up
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Enabled")
|
||||
|
||||
else:
|
||||
self.enable_host(host_id)
|
||||
self.verify_uservm_host_control_state(self.vm.id, "Enabled")
|
||||
|
||||
@attr(tags=["basic", "advanced"], required_hardware="false")
|
||||
def test_ssvm_host_control_state(self):
|
||||
""" Verify host control state for systemvm """
|
||||
# 1. verify hostcontrolstate = Enabled
|
||||
# 2. Disable the host, verify hostcontrolstate = Disabled
|
||||
|
||||
list_ssvm_response = list_ssvms(
|
||||
self.apiclient,
|
||||
systemvmtype='secondarystoragevm',
|
||||
state='Running',
|
||||
zoneid=self.zone.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_ssvm_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
ssvm = list_ssvm_response[0]
|
||||
host_id = ssvm.hostid
|
||||
|
||||
self.verify_ssvm_host_control_state(ssvm.id, "Enabled")
|
||||
|
||||
self.disable_host(host_id)
|
||||
self.verify_ssvm_host_control_state(ssvm.id, "Disabled")
|
||||
|
||||
self.enable_host(host_id)
|
||||
self.verify_ssvm_host_control_state(ssvm.id, "Enabled")
|
||||
|
||||
@attr(tags=["basic", "advanced"], required_hardware="false")
|
||||
def test_router_host_control_state(self):
|
||||
""" Verify host control state for router """
|
||||
# 1. verify hostcontrolstate = Enabled
|
||||
# 2. Disable the host, verify hostcontrolstate = Disabled
|
||||
|
||||
list_router_response = list_routers(
|
||||
self.apiclient,
|
||||
state='Running',
|
||||
listall=True,
|
||||
zoneid=self.zone.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_router_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
router = list_router_response[0]
|
||||
host_id = router.hostid
|
||||
|
||||
self.verify_router_host_control_state(router.id, "Enabled")
|
||||
|
||||
self.disable_host(host_id)
|
||||
self.verify_router_host_control_state(router.id, "Disabled")
|
||||
|
||||
self.enable_host(host_id)
|
||||
self.verify_router_host_control_state(router.id, "Enabled")
|
||||
@ -821,6 +821,7 @@
|
||||
"label.host.alerts": "Hosts in alert state",
|
||||
"label.host.name": "Host name",
|
||||
"label.host.tag": "Host tag",
|
||||
"label.hostcontrolstate": "Control Plane Status",
|
||||
"label.hostid": "Host",
|
||||
"label.hostname": "Host",
|
||||
"label.hostnamelabel": "Host name",
|
||||
@ -2424,6 +2425,8 @@
|
||||
"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.",
|
||||
"message.host.controlstate": "The Control Plane Status of this instance is ",
|
||||
"message.host.controlstate.retry": "Some actions on this instance will fail, if so please wait a while and retry.",
|
||||
"message.host.dedicated": "Host Dedicated",
|
||||
"message.host.dedication.released": "Host dedication released.",
|
||||
"message.info.cloudian.console": "Cloudian Management Console should open in another window.",
|
||||
|
||||
@ -16,6 +16,13 @@
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-alert type="error" v-if="['vm', 'systemvm', 'router', 'ilbvm'].includes($route.meta.name) && 'hostcontrolstate' in resource && resource.hostcontrolstate !== 'Enabled'">
|
||||
<template #message>
|
||||
<div class="title">
|
||||
{{ $t('message.host.controlstate') }} {{ resource.hostcontrolstate }}. {{ $t('message.host.controlstate.retry') }}
|
||||
</div>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-alert v-if="ip6routes" type="info" :showIcon="true" :message="$t('label.add.upstream.ipv6.routes')">
|
||||
<template #description>
|
||||
<p v-html="ip6routes" />
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<a
|
||||
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)" >
|
||||
<a-button style="margin-left: 5px" shape="circle" type="dashed" :size="size" :disabled="['Stopped', 'Error', 'Destroyed'].includes(resource.state) || resource.hostcontrolstate === 'Offline'" >
|
||||
<code-outlined />
|
||||
</a-button>
|
||||
</a>
|
||||
|
||||
@ -79,7 +79,7 @@ export default {
|
||||
details: () => {
|
||||
var fields = ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename',
|
||||
'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account',
|
||||
'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy']
|
||||
'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy', 'hostcontrolstate']
|
||||
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)
|
||||
if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) {
|
||||
return fields
|
||||
@ -142,6 +142,7 @@ export default {
|
||||
docHelp: 'adminguide/virtual_machines.html#stopping-and-starting-vms',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Running'].includes(record.state) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
args: (record, store) => {
|
||||
var fields = []
|
||||
fields.push('forced')
|
||||
@ -169,6 +170,7 @@ export default {
|
||||
value: (record) => { return record.id }
|
||||
}
|
||||
},
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
successMethod: (obj, result) => {
|
||||
const vm = result.jobresult.virtualmachine || {}
|
||||
if (result.jobstatus === 1 && vm.password) {
|
||||
@ -193,6 +195,7 @@ export default {
|
||||
(['Stopped'].includes(record.state) && ((record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') ||
|
||||
(record.hypervisor === 'KVM' && record.pooltype === 'PowerFlex'))))
|
||||
},
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
|
||||
mapping: {
|
||||
virtualmachineid: {
|
||||
value: (record, params) => { return record.id }
|
||||
@ -210,6 +213,7 @@ export default {
|
||||
return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') ||
|
||||
(['Stopped'].includes(record.state) && !['KVM', 'LXC'].includes(record.hypervisor)))
|
||||
},
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateSnapshotWizard.vue')))
|
||||
},
|
||||
{
|
||||
@ -283,6 +287,7 @@ export default {
|
||||
dataView: true,
|
||||
popup: true,
|
||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue')))
|
||||
},
|
||||
{
|
||||
@ -299,6 +304,7 @@ export default {
|
||||
return args
|
||||
},
|
||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
||||
mapping: {
|
||||
virtualmachineid: {
|
||||
value: (record, params) => { return record.id }
|
||||
@ -334,6 +340,7 @@ export default {
|
||||
docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration',
|
||||
dataView: true,
|
||||
show: (record, store) => { return ['Running'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard.vue')))
|
||||
},
|
||||
@ -345,6 +352,7 @@ export default {
|
||||
docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration',
|
||||
dataView: true,
|
||||
show: (record, store) => { return ['Stopped'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))),
|
||||
popup: true
|
||||
},
|
||||
|
||||
@ -23,7 +23,7 @@ export default {
|
||||
permission: ['listInternalLoadBalancerVMs'],
|
||||
params: { projectid: '-1' },
|
||||
columns: ['name', 'state', 'publicip', 'guestnetworkname', 'vpcname', 'version', 'softwareversion', 'hostname', 'account', 'zonename', 'requiresupgrade'],
|
||||
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created'],
|
||||
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created', 'hostcontrolstate'],
|
||||
actions: [
|
||||
{
|
||||
api: 'startInternalLoadBalancerVM',
|
||||
@ -53,6 +53,7 @@ export default {
|
||||
label: 'label.action.migrate.router',
|
||||
dataView: true,
|
||||
show: (record, store) => { return record.state === 'Running' && ['Admin'].includes(store.userInfo.roletype) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard'))),
|
||||
popup: true
|
||||
},
|
||||
@ -62,6 +63,7 @@ export default {
|
||||
label: 'label.action.migrate.systemvm.to.ps',
|
||||
dataView: true,
|
||||
show: (record, store) => { return ['Stopped'].includes(record.state) && ['VMware'].includes(record.hypervisor) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))),
|
||||
popup: true
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ export default {
|
||||
return columns
|
||||
},
|
||||
searchFilters: ['name', 'zoneid', 'podid', 'clusterid'],
|
||||
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created'],
|
||||
details: ['name', 'id', 'version', 'softwareversion', 'requiresupgrade', 'guestnetworkname', 'vpcname', 'publicip', 'guestipaddress', 'linklocalip', 'serviceofferingname', 'networkdomain', 'isredundantrouter', 'redundantstate', 'hostname', 'account', 'zonename', 'created', 'hostcontrolstate'],
|
||||
resourceType: 'VirtualRouter',
|
||||
tabs: [{
|
||||
name: 'details',
|
||||
@ -188,6 +188,7 @@ export default {
|
||||
message: 'message.migrate.router.confirm',
|
||||
dataView: true,
|
||||
show: (record, store) => { return record.state === 'Running' && ['Admin'].includes(store.userInfo.roletype) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard'))),
|
||||
popup: true
|
||||
},
|
||||
@ -197,6 +198,7 @@ export default {
|
||||
label: 'label.action.migrate.systemvm.to.ps',
|
||||
dataView: true,
|
||||
show: (record, store) => { return ['Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))),
|
||||
popup: true
|
||||
},
|
||||
|
||||
@ -25,7 +25,7 @@ export default {
|
||||
docHelp: 'adminguide/systemvm.html',
|
||||
permission: ['listSystemVms'],
|
||||
columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'hostname', 'zonename'],
|
||||
details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable'],
|
||||
details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'],
|
||||
resourceType: 'SystemVm',
|
||||
tabs: [
|
||||
{
|
||||
@ -105,6 +105,7 @@ export default {
|
||||
message: 'message.migrate.systemvm.confirm',
|
||||
dataView: true,
|
||||
show: (record, store) => { return record.state === 'Running' && ['Admin'].includes(store.userInfo.roletype) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard'))),
|
||||
popup: true
|
||||
},
|
||||
@ -114,6 +115,7 @@ export default {
|
||||
label: 'label.action.migrate.systemvm.to.ps',
|
||||
dataView: true,
|
||||
show: (record, store) => { return ['Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) },
|
||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))),
|
||||
popup: true
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user