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:
Wei Zhou 2023-01-05 09:59:28 +01:00 committed by GitHub
parent d0b34b7576
commit 1380c604b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 509 additions and 6 deletions

View 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;
}
}
}

View File

@ -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";

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View 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);
}
}

View File

@ -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`,

View File

@ -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());
}
}

View File

@ -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());

View File

@ -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());

View File

@ -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;
}

View File

@ -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;
}

View 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")

View File

@ -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.",

View File

@ -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" />

View File

@ -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>

View File

@ -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
},

View File

@ -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
}

View File

@ -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
},

View File

@ -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
},