From 96ccd6d3e385253dd03a0936bcfba35327b3885f Mon Sep 17 00:00:00 2001 From: davidjumani Date: Mon, 26 Apr 2021 14:24:20 +0530 Subject: [PATCH 1/5] ui: Show traffic type in physical networks tab (#4952) --- .../views/infra/zone/PhysicalNetworksTab.vue | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ui/src/views/infra/zone/PhysicalNetworksTab.vue b/ui/src/views/infra/zone/PhysicalNetworksTab.vue index 8312b6954cf..85470c42da4 100644 --- a/ui/src/views/infra/zone/PhysicalNetworksTab.vue +++ b/ui/src/views/infra/zone/PhysicalNetworksTab.vue @@ -47,6 +47,14 @@
{{ network.vlan }}
+
+
+ {{ $t('label.traffictype') }} +
+
+ {{ network.traffictype }} +
+
{{ $t('label.broadcastdomainrange') }} @@ -101,9 +109,25 @@ export default { this.fetchLoading = true api('listPhysicalNetworks', { zoneid: this.resource.id }).then(json => { this.networks = json.listphysicalnetworksresponse.physicalnetwork || [] + this.fetchTrafficLabels() }).catch(error => { this.$notifyError(error) - }).finally(() => { + }) + }, + fetchTrafficLabels () { + const promises = [] + for (const network of this.networks) { + promises.push(new Promise((resolve, reject) => { + api('listTrafficTypes', { physicalnetworkid: network.id }).then(json => { + network.traffictype = json.listtraffictypesresponse.traffictype.filter(e => { return e.traffictype }).map(e => { return e.traffictype }).join(', ') + resolve() + }).catch(error => { + this.$notifyError(error) + reject(error) + }) + })) + } + Promise.all(promises).finally(() => { this.fetchLoading = false }) } From 04cdb50e9586c3e2ecad8cb67c29f2e440516437 Mon Sep 17 00:00:00 2001 From: Spaceman1984 <49917670+Spaceman1984@users.noreply.github.com> Date: Tue, 27 Apr 2021 06:50:19 +0200 Subject: [PATCH 2/5] debian: Adding net tools as a dependency (#4951) Fixes #4928 --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 15d3fb5b9fd..e6184090f67 100644 --- a/debian/control +++ b/debian/control @@ -15,7 +15,7 @@ Description: A common package which contains files which are shared by several C Package: cloudstack-management Architecture: all -Depends: ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), sudo, python3-mysql.connector, augeas-tools, mysql-client | mariadb-client, adduser, bzip2, ipmitool, file, gawk, iproute2, qemu-utils, python3-dnspython, lsb-release, init-system-helpers (>= 1.14~), python3-setuptools +Depends: ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), net-tools, sudo, python3-mysql.connector, augeas-tools, mysql-client | mariadb-client, adduser, bzip2, ipmitool, file, gawk, iproute2, qemu-utils, python3-dnspython, lsb-release, init-system-helpers (>= 1.14~), python3-setuptools Conflicts: cloud-server, cloud-client, cloud-client-ui Description: CloudStack server library The CloudStack management server From d4db1015b8b8af5f84ee971888add961ac1cadc1 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Tue, 27 Apr 2021 10:21:48 +0530 Subject: [PATCH 3/5] marvin: fix test_scale_vm for xenserver/Xcp-ng (#4929) Co-authored-by: Pearl Dsilva --- .../resource/CitrixResourceBase.java | 2 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 8 +- test/integration/smoke/test_scale_vm.py | 101 ++++++++++++++---- 3 files changed, 81 insertions(+), 30 deletions(-) diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index a26bcc486d0..315ce17c78a 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -3110,7 +3110,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe s_logger.warn("No recommended value found for dynamic max, setting static max and dynamic max equal"); return dynamicMaxRam; } - final long staticMax = Math.min(recommendedValue, 4l * dynamicMinRam); // XS + final long staticMax = Math.min(recommendedValue, 4L * dynamicMinRam); // XS // constraint // for // stability diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 951f03de65c..53647ea74e6 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -1947,14 +1947,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _itMgr.reConfigureVm(vmInstance.getUuid(), currentServiceOffering, newServiceOffering, customParameters, existingHostHasCapacity); success = true; return success; - } catch (InsufficientCapacityException e) { - s_logger.warn("Received exception while scaling ", e); - } catch (ResourceUnavailableException e) { - s_logger.warn("Received exception while scaling ", e); - } catch (ConcurrentOperationException e) { + } catch (InsufficientCapacityException | ResourceUnavailableException | ConcurrentOperationException e) { s_logger.warn("Received exception while scaling ", e); } catch (Exception e) { - s_logger.warn("Received exception while scaling ", e); + s_logger.warn("Scaling failed with exception: ", e); } finally { if (!success) { // Decrement CPU and Memory count accordingly. diff --git a/test/integration/smoke/test_scale_vm.py b/test/integration/smoke/test_scale_vm.py index ddd6bcfbc71..c58aabb90e6 100644 --- a/test/integration/smoke/test_scale_vm.py +++ b/test/integration/smoke/test_scale_vm.py @@ -22,12 +22,17 @@ from marvin.cloudstackTestCase import cloudstackTestCase from marvin.cloudstackAPI import scaleVirtualMachine from marvin.lib.utils import cleanup_resources from marvin.lib.base import (Account, + Host, VirtualMachine, - ServiceOffering) + ServiceOffering, + Template, + Configurations) from marvin.lib.common import (get_zone, get_template, get_domain) from nose.plugins.attrib import attr +from marvin.sshClient import SshClient +import time _multiprocess_shared_ = True @@ -39,6 +44,8 @@ class TestScaleVm(cloudstackTestCase): testClient = super(TestScaleVm, cls).getClsTestClient() cls.apiclient = testClient.getApiClient() cls.services = testClient.getParsedTestDataConfig() + cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][ + 0].__dict__ cls._cleanup = [] cls.unsupportedHypervisor = False cls.hypervisor = cls.testClient.getHypervisorInfo() @@ -48,21 +55,31 @@ class TestScaleVm(cloudstackTestCase): # Get Zone, Domain and templates domain = get_domain(cls.apiclient) - zone = get_zone(cls.apiclient, testClient.getZoneForTests()) - cls.services['mode'] = zone.networktype + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype - template = get_template( - cls.apiclient, - zone.id, - cls.services["ostype"] - ) - if template == FAILED: - assert False, "get_template() failed to return template\ - with description %s" % cls.services["ostype"] + if cls.hypervisor.lower() == 'simulator': + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"] + ) + if cls.template == FAILED: + assert False, "get_template() failed to return template\ + with description %s" % cls.services["ostype"] + else: + cls.template = Template.register( + cls.apiclient, + cls.services["CentOS7template"], + zoneid=cls.zone.id + ) + cls._cleanup.append(cls.template) + cls.template.download(cls.apiclient) + time.sleep(60) - # Set Zones and disk offerings ?? - cls.services["small"]["zoneid"] = zone.id - cls.services["small"]["template"] = template.id + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["small"]["template"] = cls.template.id # Create account, service offerings, vm. cls.account = Account.create( @@ -70,16 +87,25 @@ class TestScaleVm(cloudstackTestCase): cls.services["account"], domainid=domain.id ) + cls._cleanup.append(cls.account) cls.small_offering = ServiceOffering.create( cls.apiclient, cls.services["service_offerings"]["small"] ) + cls._cleanup.append(cls.small_offering) cls.big_offering = ServiceOffering.create( cls.apiclient, cls.services["service_offerings"]["big"] ) + cls._cleanup.append(cls.big_offering) + + Configurations.update( + cls.apiclient, + name="enable.dynamic.scale.vm", + value="true" + ) # create a virtual machine cls.virtual_machine = VirtualMachine.create( @@ -90,17 +116,10 @@ class TestScaleVm(cloudstackTestCase): serviceofferingid=cls.small_offering.id, mode=cls.services["mode"] ) - cls._cleanup = [ - cls.small_offering, - cls.account - ] @classmethod def tearDownClass(cls): - cls.apiclient = super( - TestScaleVm, - cls).getClsTestClient().getApiClient() - cleanup_resources(cls.apiclient, cls._cleanup) + super(TestScaleVm,cls).tearDownClass() return def setUp(self): @@ -113,10 +132,27 @@ class TestScaleVm(cloudstackTestCase): %s" % self.hypervisor) def tearDown(self): + Configurations.update( + self.apiclient, + name="enable.dynamic.scale.vm", + value="false" + ) # Clean up, terminate the created ISOs - cleanup_resources(self.apiclient, self.cleanup) + super(TestScaleVm,self).tearDown() return + 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 self.skipTest("Unable to create ssh connection: " % e) + + self.assertIsNotNone( + ssh_client, "Failed to setup ssh connection to ip=%s" % ip) + + return ssh_client + @attr(hypervisor="xenserver") @attr(tags=["advanced", "basic"], required_hardware="false") def test_01_scale_vm(self): @@ -145,6 +181,25 @@ class TestScaleVm(cloudstackTestCase): if not "running" in result: self.skipTest("Skipping scale VM operation because\ VMware tools are not installed on the VM") + if self.hypervisor.lower() != 'simulator': + hostid = self.virtual_machine.hostid + host = Host.list( + self.apiclient, + zoneid=self.zone.id, + hostid=hostid, + type='Routing' + )[0] + + try: + username = self.hostConfig["username"] + password = self.hostConfig["password"] + ssh_client = self.get_ssh_client(host.ipaddress, username, password) + res = ssh_client.execute("hostnamectl | grep 'Operating System' | cut -d':' -f2") + except Exception as e: + pass + + if 'XenServer' in res[0]: + self.skipTest("Skipping test for XenServer as it's License does not allow scaling") self.virtual_machine.update( self.apiclient, From d68b098a435b27c62fa46cb21fa1ca8bd3065f32 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Tue, 27 Apr 2021 23:17:49 +0700 Subject: [PATCH 4/5] UI: Show IPv6 address of Instance (#4948) * vm: show IPv6 address * show ipv6 address on listview --- ui/src/components/view/DetailsTab.vue | 16 ++++++++++++++++ ui/src/components/view/InfoCard.vue | 17 +++++++++++++++++ ui/src/components/view/ListView.vue | 10 ++++++++++ ui/src/config/section/compute.js | 2 +- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index e47f3c58a3f..015b44e24d6 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -50,6 +50,13 @@
+ +
+ {{ $t('label.' + String(item).toLowerCase()) }} +
+
{{ ipV6Address }}
+
+
@@ -88,6 +95,15 @@ export default { mounted () { this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name) }, + computed: { + ipV6Address () { + if (this.resource.nic && this.resource.nic.length > 0) { + return this.resource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') + } + + return null + } + }, created () { this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name) }, diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue index d96fa0a8a81..15156b190fa 100644 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@ -313,6 +313,16 @@ {{ ipaddress }} +
+
{{ $t('label.ip6address') }}
+
+ + {{ ipV6Address }} +
+
{{ $t('label.project') }}
@@ -778,6 +788,13 @@ export default { name () { return this.resource.displayname || this.resource.displaytext || this.resource.name || this.resource.username || this.resource.ipaddress || this.resource.virtualmachinename || this.resource.templatetype + }, + ipV6Address () { + if (this.resource.nic && this.resource.nic.length > 0) { + return this.resource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') + } + + return null } }, methods: { diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 1f56dc1027c..c92c852797c 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -114,6 +114,9 @@ source-nat + + {{ ipV6Address(text, record) }} + {{ text }} @@ -564,6 +567,13 @@ export default { }, editTariffValue (record) { this.parentEditTariffAction(true, record) + }, + ipV6Address (text, record) { + if (!record || !record.nic || record.nic.length === 0) { + return '' + } + + return record.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') || text } } } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 39271c44d8a..69a70182218 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -68,7 +68,7 @@ export default { return fields }, searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'], - details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'], + details: ['displayname', 'name', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename', 'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account', 'domain', 'zonename'], tabs: [{ component: () => import('@/views/compute/InstanceTab.vue') }], From 8e31d1e650bd9e92d8f79d639f2dafdb894ac3e6 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 27 Apr 2021 21:54:24 +0530 Subject: [PATCH 5/5] api: fix disk/service offering keys (#4946) Volume can either have an associated disk offering (for DATA disks & ROOT disks for VMs created from ISO) or a compute/service offering (for ROOT disks of VMs created from templates). This fix simplifies and fixes check to return the appropriate response keys in these cases. Signed-off-by: Abhishek Kumar --- .../src/main/java/com/cloud/api/ApiDBUtils.java | 8 ++++++-- .../cloud/api/query/dao/VolumeJoinDaoImpl.java | 16 +--------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index d7445927f37..5ee39cf2d5e 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -27,8 +27,6 @@ import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; -import com.cloud.vm.NicVO; -import com.cloud.vm.dao.NicDao; import org.apache.cloudstack.acl.Role; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.affinity.AffinityGroup; @@ -318,6 +316,7 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroup; import com.cloud.vm.InstanceGroupVO; import com.cloud.vm.NicProfile; +import com.cloud.vm.NicVO; import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; @@ -327,6 +326,7 @@ import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmStats; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; @@ -1117,6 +1117,10 @@ public class ApiDBUtils { return s_serviceOfferingDao.findByIdIncludingRemoved(serviceOfferingId); } + public static ServiceOffering findServiceOfferingByUuid(String serviceOfferingUuid) { + return s_serviceOfferingDao.findByUuidIncludingRemoved(serviceOfferingUuid); + } + public static ServiceOfferingDetailsVO findServiceOfferingDetail(long serviceOfferingId, String key) { return s_serviceOfferingDetailsDao.findDetail(serviceOfferingId, key); } diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 2ee0ca1b1f4..6cbc834c408 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -176,22 +176,8 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation 0) { - boolean isServiceOffering = false; - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - isServiceOffering = true; - } else { - // can't rely on the fact that the volume is the datadisk as it might have been created as a root, and - // then detached later - long offeringId = volume.getDiskOfferingId(); - if (ApiDBUtils.findDiskOfferingById(offeringId) == null) { - isServiceOffering = true; - } - } - - if (isServiceOffering) { + if (ApiDBUtils.findServiceOfferingByUuid(volume.getDiskOfferingUuid()) != null) { volResponse.setServiceOfferingId(volume.getDiskOfferingUuid()); volResponse.setServiceOfferingName(volume.getDiskOfferingName()); volResponse.setServiceOfferingDisplayText(volume.getDiskOfferingDisplayText());