Merge remote-tracking branch 'origin/4.15'

This commit is contained in:
Rohit Yadav 2021-04-27 22:20:30 +05:30
commit 1e859a0e2c
11 changed files with 158 additions and 50 deletions

2
debian/control vendored
View File

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

View File

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

View File

@ -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;
@ -319,6 +317,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;
@ -328,6 +327,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;
@ -1118,6 +1118,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);
}

View File

@ -176,22 +176,8 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJo
// populate owner.
ApiResponseHelper.populateOwner(volResponse, volume);
// DiskOfferingVO diskOffering =
// ApiDBUtils.findDiskOfferingById(volume.getDiskOfferingId());
if (volume.getDiskOfferingId() > 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());

View File

@ -1973,14 +1973,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.

View File

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

View File

@ -50,6 +50,13 @@
</div>
</div>
</a-list-item>
<a-list-item slot="renderItem" slot-scope="item" v-else-if="item === 'ip6address' && ipV6Address.length > 0">
<div>
<strong>{{ $t('label.' + String(item).toLowerCase()) }}</strong>
<br/>
<div>{{ ipV6Address }}</div>
</div>
</a-list-item>
<HostInfo :resource="resource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
<DedicateData :resource="resource" v-if="dedicatedSectionActive" />
<VmwareData :resource="resource" v-if="$route.meta.name === 'zone' && 'listVmwareDcs' in $store.getters.apis" />
@ -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)
},

View File

@ -313,6 +313,16 @@
<span v-else>{{ ipaddress }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="ipV6Address && ipV6Address !== null">
<div class="resource-detail-item__label">{{ $t('label.ip6address') }}</div>
<div class="resource-detail-item__details">
<a-icon
type="environment"
@click="$message.success(`${$t('label.copied.clipboard')} : ${ ipV6Address }`)"
v-clipboard:copy="ipV6Address" />
{{ ipV6Address }}
</div>
</div>
<div class="resource-detail-item" v-if="resource.projectid || resource.projectname">
<div class="resource-detail-item__label">{{ $t('label.project') }}</div>
<div class="resource-detail-item__details">
@ -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: {

View File

@ -114,6 +114,9 @@
<a-tag>source-nat</a-tag>
</span>
</span>
<span slot="ip6address" slot-scope="text, record" href="javascript:;">
<span>{{ ipV6Address(text, record) }}</span>
</span>
<a slot="publicip" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</a>
@ -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
}
}
}

View File

@ -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')
}],

View File

@ -47,6 +47,14 @@
</div>
<div>{{ network.vlan }}</div>
</div>
<div class="list__col">
<div class="list__label">
{{ $t('label.traffictype') }}
</div>
<div>
{{ network.traffictype }}
</div>
</div>
<div class="list__col">
<div class="list__label">
{{ $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
})
}