diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 0a0f528fc52..d12da631a31 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -2565,13 +2565,21 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.setParameters("dataCenterId", zoneId); } if (pod != null) { - sc.setParameters("podId", pod); + SearchCriteria ssc = _poolJoinDao.createSearchCriteria(); + ssc.addOr("podId", Op.EQ, pod); + ssc.addOr("podId", Op.NULL); + + sc.addAnd("podId", SearchCriteria.Op.SC, ssc); } if (address != null) { sc.setParameters("hostAddress", address); } if (cluster != null) { - sc.setParameters("clusterId", cluster); + SearchCriteria ssc = _poolJoinDao.createSearchCriteria(); + ssc.addOr("clusterId", Op.EQ, cluster); + ssc.addOr("clusterId", Op.NULL); + + sc.addAnd("clusterId", SearchCriteria.Op.SC, ssc); } if (scopeType != null) { sc.setParameters("scope", scopeType.toString()); diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 372cd3166d6..4111e0a7414 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -470,6 +470,7 @@ "label.asyncbackup": "Async Backup", "label.author.email": "Author e-mail", "label.author.name": "Author name", +"label.auto.assign": "Automatically assign", "label.auto.assign.diskoffering.disk.size": "Automatically assign offering matching the disk size", "label.auto.assign.random.ip": "Automatically assign a random IP address", "label.autoscale": "AutoScale", @@ -550,6 +551,7 @@ "label.certificate.upload.failed": "Certificate Upload Failed", "label.certificate.upload.failed.description": "Failed to update SSL Certificate. Failed to pass certificate validation check", "label.certificateid": "Certificate ID", +"label.change": "Change", "label.change.affinity": "Change Affinity", "label.change.ip.addess": "Change IP Address", "label.change.ipaddress": "Change IP address for NIC", @@ -818,6 +820,7 @@ "label.disksize": "Disk Size (in GB)", "label.disksizeallocated": "Disk Allocated", "label.disksizeallocatedgb": "Allocated", +"label.disksizefree": "Disk Free", "label.disksizetotal": "Disk Total", "label.disksizetotalgb": "Total", "label.disksizeunallocatedgb": "Unallocated", @@ -1425,6 +1428,8 @@ "label.migrate.instance.to": "Migrate instance to", "label.migrate.instance.to.host": "Migrate instance to another host", "label.migrate.instance.to.ps": "Migrate instance to another primary storage", +"label.migrate.instance.single.storage": "Migrate all volume(s) of the instance to a single primary storage", +"label.migrate.instance.specific.storages": "Migrate volume(s) of the instance to specific primary storages", "label.migrate.lb.vm": "Migrate LB VM", "label.migrate.lb.vm.to.ps": "Migrate LB VM to another primary storage", "label.migrate.router.to": "Migrate Router to", @@ -1434,6 +1439,7 @@ "label.migrate.volume": "Migrate Volume", "label.migrate.volume.newdiskoffering.desc": "This option allows administrators to replace the old disk offering, using one that better suits the new placement of the volume.", "label.migrate.volume.to.primary.storage": "Migrate volume to another primary storage", +"label.migrate.with.storage": "Migrate with storage", "label.migrating": "Migrating", "label.migrating.data": "Migrating Data", "label.min.balance": "Min Balance", @@ -1977,6 +1983,7 @@ "label.select.offering": "Select offering", "label.select.project": "Select Project", "label.select.projects": "Select Projects", +"label.select.ps": "Select Primary Storage", "label.select.region": "Select region", "label.select.tier": "Select Tier", "label.select.vm.for.static.nat": "Select VM for static NAT", @@ -3068,17 +3075,20 @@ "message.lock.account": "Please confirm that you want to lock this account. By locking the account, all users for this account will no longer be able to manage their cloud resources. Existing resources can still be accessed.", "message.login.failed": "Login Failed", "message.migrate.instance.confirm": "Please confirm the host you wish to migrate the virtual instance to.", +"message.migrate.instance.host.auto.assign": "Host for the instance will be automatically chosen based on the suitability within the same cluster", "message.migrate.instance.select.host": "Please select a host for migration", -"message.migrate.instance.to.host": "Please confirm that you want to migrate instance to another host.", -"message.migrate.instance.to.ps": "Please confirm that you want to migrate instance to another primary storage.", +"message.migrate.instance.to.host": "Please confirm that you want to migrate this instance to another host. When migration is between hosts of different clusters volume(s) of the instance may get migrated to suitable storage pools.", +"message.migrate.instance.to.ps": "Please confirm that you want to migrate this instance to another primary storage.", "message.migrate.lb.vm.to.ps": "Please confirm that you want to migrate LB VM to another primary storage.", "message.migrate.router.confirm": "Please confirm the host you wish to migrate the router to:", "message.migrate.router.to.ps": "Please confirm that you want to migrate router to another primary storage.", "message.migrate.system.vm.to.ps": "Please confirm that you want to migrate system VM to another primary storage.", "message.migrate.systemvm.confirm": "Please confirm the host you wish to migrate the system VM to:", -"message.migrate.volume": "Please confirm that you want to migrate volume to another primary storage.", +"message.migrate.volume": "Please confirm that you want to migrate this volume to another primary storage.", "message.migrate.volume.failed": "Migrating volume failed", +"message.migrate.volume.pool.auto.assign": "Primary storage for the volume will be automatically chosen based on the suitability and VM destination", "message.migrate.volume.processing": "Migrating volume...", +"message.migrate.with.storage": "Specify storage pool for volumes of the instance.", "message.migrating.failed": "Migration failed", "message.migrating.processing": "Migration in progress for", "message.migrating.vm.to.host.failed": "Failed to migrate VM to host", @@ -3144,6 +3154,7 @@ "message.pod.dedicated": "Pod Dedicated", "message.pod.dedication.released": "Pod dedication released", "message.portable.ip.delete.confirm": "Please confirm you want to delete Portable IP Range", +"message.primary.storage.invalid.state": "Primary storage is not in Up state", "message.processing.complete": "Processing complete!", "message.project.invite.sent": "Invite sent to user; they will be added to the project once they accept the invitation", "message.protocol.description": "For XenServer, choose NFS, iSCSI, or PreSetup. For KVM, choose NFS, SharedMountPoint, RDB, CLVM or Gluster. For vSphere, choose NFS, PreSetup (VMFS or iSCSI or FiberChannel or vSAN or vVols) or DatastoreCluster. For Hyper-V, choose SMB/CIFS. For LXC, choose NFS or SharedMountPoint. For OVM, choose NFS or ocfs2.", @@ -3419,6 +3430,7 @@ "message.volume.state.uploaderror": "Volume upload encountered some error", "message.volume.state.uploadinprogress": "Volume upload is in progress", "message.volume.state.uploadop": "The volume upload operation is in progress or in short the volume is on secondary storage", +"message.volume.state.primary.storage.suitability": "The suitability of a primary storage for a volume depends on the disk offering of the volume and on the virtual machine allocations if the volume is attached to a virtual machine.", "message.waiting.for.builtin.templates.to.load": "Waiting for builtin templates to load...", "message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats", "message.xstools61plus.update.failed": "Failed to update Original XS Version is 6.1+ field. Error:", diff --git a/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue b/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue new file mode 100644 index 00000000000..a0d482c8a17 --- /dev/null +++ b/ui/src/components/view/InstanceVolumesStoragePoolSelectListView.vue @@ -0,0 +1,248 @@ +// 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. + + + + + + diff --git a/ui/src/components/view/StoragePoolSelectView.vue b/ui/src/components/view/StoragePoolSelectView.vue new file mode 100644 index 00000000000..d0dcf257a4b --- /dev/null +++ b/ui/src/components/view/StoragePoolSelectView.vue @@ -0,0 +1,274 @@ +// 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. + + + + + + diff --git a/ui/src/components/view/VolumeStoragePoolSelectForm.vue b/ui/src/components/view/VolumeStoragePoolSelectForm.vue new file mode 100644 index 00000000000..eea416faa1a --- /dev/null +++ b/ui/src/components/view/VolumeStoragePoolSelectForm.vue @@ -0,0 +1,129 @@ +// 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. + + + + + + diff --git a/ui/src/main.js b/ui/src/main.js index 0635b99765e..7cd5d21c9d9 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -26,7 +26,7 @@ import './core/lazy_use' import './core/ext' import './permission' // permission control import './utils/filter' // global filter -import { pollJobPlugin, notifierPlugin, toLocaleDatePlugin, configUtilPlugin, apiMetaUtilPlugin, showIconPlugin, resourceTypePlugin } from './utils/plugins' +import { pollJobPlugin, notifierPlugin, toLocaleDatePlugin, configUtilPlugin, apiMetaUtilPlugin, showIconPlugin, resourceTypePlugin, fileSizeUtilPlugin } from './utils/plugins' import { VueAxios } from './utils/request' import './utils/directives' @@ -60,3 +60,4 @@ fetch('config.json').then(response => response.json()).then(config => { Vue.use(configUtilPlugin) Vue.use(apiMetaUtilPlugin) +Vue.use(fileSizeUtilPlugin) diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index 99db8f7eb6e..6cf48ad2663 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -299,3 +299,30 @@ export const apiMetaUtilPlugin = { } } } + +const KB = 1024 +const MB = 1024 * KB +const GB = 1024 * MB +const TB = 1024 * GB + +export const fileSizeUtilPlugin = { + install (Vue) { + Vue.prototype.$bytesToHumanReadableSize = function (bytes) { + if (bytes == null) { + return '' + } + if (bytes < KB && bytes >= 0) { + return bytes + ' bytes' + } + if (bytes < MB) { + return (bytes / KB).toFixed(2) + ' KB' + } else if (bytes < GB) { + return (bytes / MB).toFixed(2) + ' MB' + } else if (bytes < TB) { + return (bytes / GB).toFixed(2) + ' GB' + } else { + return (bytes / TB).toFixed(2) + ' TB' + } + } + } +} diff --git a/ui/src/views/compute/MigrateVMStorage.vue b/ui/src/views/compute/MigrateVMStorage.vue index c994b56d629..4acc7d61dff 100644 --- a/ui/src/views/compute/MigrateVMStorage.vue +++ b/ui/src/views/compute/MigrateVMStorage.vue @@ -16,47 +16,53 @@ // under the License. diff --git a/ui/tests/common/index.js b/ui/tests/common/index.js index e152c7152bc..bd1d6d6bff8 100644 --- a/ui/tests/common/index.js +++ b/ui/tests/common/index.js @@ -21,7 +21,7 @@ import mockRouter from '../mock/mockRouter' import localVue from '../setup' import { mount } from '@vue/test-utils' -import { pollJobPlugin, notifierPlugin, configUtilPlugin, apiMetaUtilPlugin, toLocaleDatePlugin, showIconPlugin, resourceTypePlugin } from '@/utils/plugins' +import { pollJobPlugin, notifierPlugin, configUtilPlugin, apiMetaUtilPlugin, toLocaleDatePlugin, showIconPlugin, resourceTypePlugin, fileSizeUtilPlugin } from '@/utils/plugins' localVue.use(pollJobPlugin) localVue.use(notifierPlugin) @@ -30,6 +30,7 @@ localVue.use(apiMetaUtilPlugin) localVue.use(toLocaleDatePlugin) localVue.use(showIconPlugin) localVue.use(resourceTypePlugin) +localVue.use(fileSizeUtilPlugin) function createMockRouter (newRoutes = []) { let routes = []