mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
compute: NICs and IPs management (#71)
Adds VM nic/ip management, implement some placement fixes. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
200f89bc08
commit
b140b738fb
58
ui/package-lock.json
generated
58
ui/package-lock.json
generated
@ -2341,9 +2341,9 @@
|
||||
}
|
||||
},
|
||||
"@fortawesome/vue-fontawesome": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.8.tgz",
|
||||
"integrity": "sha512-SdFiUD+vFDA/xKuEbnQTVrK8FDxoV0eyQaiHxmCcjAc0+vQe0Kf6oGm28opNPIt8MTgKWR3+Yg3xXP455Ae4tQ=="
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.9.tgz",
|
||||
"integrity": "sha512-h/emhmZz+DfB2zOGLWawNwXq82UYhn9waTfUjLLmeaIqtnIyNt6kYlpQT/vzJjLZRDRvY2IEJAh1di5qKpKVpA=="
|
||||
},
|
||||
"@hapi/address": {
|
||||
"version": "2.1.4",
|
||||
@ -8526,9 +8526,9 @@
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.4.8",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.8.tgz",
|
||||
"integrity": "sha512-b+BBmCZmVgho8KnBUOXpvlqEMguko+0P+kXCwD4vIprsXC6ht1qgPxtb1OK6XgSlrySF71wkwBQ0Hv695bk9gQ=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz",
|
||||
"integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.4.7",
|
||||
@ -10009,9 +10009,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
|
||||
"integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==",
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
|
||||
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@ -20621,9 +20621,9 @@
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
|
||||
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
|
||||
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
|
||||
"dev": true
|
||||
},
|
||||
"serve-index": {
|
||||
@ -22016,16 +22016,16 @@
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz",
|
||||
"integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==",
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
|
||||
"integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cacache": "^12.0.2",
|
||||
"find-cache-dir": "^2.1.0",
|
||||
"is-wsl": "^1.1.0",
|
||||
"schema-utils": "^1.0.0",
|
||||
"serialize-javascript": "^1.7.0",
|
||||
"serialize-javascript": "^2.1.2",
|
||||
"source-map": "^0.6.1",
|
||||
"terser": "^4.1.2",
|
||||
"webpack-sources": "^1.4.0",
|
||||
@ -23005,9 +23005,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"vue": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
|
||||
"integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
||||
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
||||
},
|
||||
"vue-cli-plugin-apollo": {
|
||||
"version": "0.21.3",
|
||||
@ -23259,9 +23259,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"version": "8.15.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.15.1.tgz",
|
||||
"integrity": "sha512-GBbz8qYCu0U2LNu4IcuFLZiuyninG4k26knvhL7GZG5Ncp4RR2VKDEH6g8gQ6I+UUBCvH2MBQVPSdxWe4DBkPw=="
|
||||
"version": "8.15.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.15.3.tgz",
|
||||
"integrity": "sha512-PVNgo6yhOmacZVFjSapZ314oewwLyXHjJwAqjnaPN1GJAJd/dvsrShGzSiJuCX4Hc36G4epJvNXUwO8y7wEKew=="
|
||||
},
|
||||
"vue-i18n-extract": {
|
||||
"version": "0.4.14",
|
||||
@ -23506,9 +23506,9 @@
|
||||
}
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz",
|
||||
"integrity": "sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==",
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
|
||||
"integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"de-indent": "^1.0.2",
|
||||
@ -23607,9 +23607,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.41.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz",
|
||||
"integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==",
|
||||
"version": "4.41.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.3.tgz",
|
||||
"integrity": "sha512-EcNzP9jGoxpQAXq1VOoTet0ik7/VVU1MovIfcUSAjLowc7GhcQku/sOXALvq5nPpSei2HF6VRhibeJSC3i/Law==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.8.5",
|
||||
@ -23632,7 +23632,7 @@
|
||||
"node-libs-browser": "^2.2.1",
|
||||
"schema-utils": "^1.0.0",
|
||||
"tapable": "^1.1.3",
|
||||
"terser-webpack-plugin": "^1.4.1",
|
||||
"terser-webpack-plugin": "^1.4.3",
|
||||
"watchpack": "^1.6.0",
|
||||
"webpack-sources": "^1.4.1"
|
||||
}
|
||||
|
||||
@ -37,10 +37,10 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.8",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"ant-design-vue": "~1.4.10",
|
||||
"axios": "^0.19.0",
|
||||
"core-js": "^3.4.8",
|
||||
"core-js": "^3.5.0",
|
||||
"enquire.js": "^2.1.6",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
@ -51,10 +51,10 @@
|
||||
"npm-check-updates": "^4.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"viser-vue": "^2.4.7",
|
||||
"vue": "^2.6.10",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-cropper": "0.4.9",
|
||||
"vue-i18n": "^8.15.1",
|
||||
"vue-i18n": "^8.15.3",
|
||||
"vue-ls": "^3.2.1",
|
||||
"vue-router": "^3.1.3",
|
||||
"vue-svg-component-runtime": "^1.0.1",
|
||||
@ -87,8 +87,8 @@
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-i18n": "^0.6.0",
|
||||
"vue-svg-icon-loader": "^2.1.1",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.41.2"
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.41.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@ -35,13 +35,36 @@
|
||||
:dataSource="detailOptions[newKey]"
|
||||
placeholder="Value"
|
||||
@change="e => onAddInputChange(e, 'newValue')" />
|
||||
<a-button type="dashed" style="width: 50%" icon="close" @click="showAddDetail = false">Cancel</a-button>
|
||||
<a-button type="primary" style="width: 50%" icon="plus" @click="addDetail">Add Setting</a-button>
|
||||
<a-button type="primary" style="width: 25%" icon="plus" @click="addDetail">Add Setting</a-button>
|
||||
<a-button type="dashed" style="width: 25%" icon="close" @click="showAddDetail = false">Cancel</a-button>
|
||||
</div>
|
||||
<a-list size="large">
|
||||
<a-list-item :key="index" v-for="(item, index) in details">
|
||||
<a-list-item-meta>
|
||||
<span slot="title"><strong>{{ item.name }}</strong></span>
|
||||
<span slot="title">
|
||||
{{ item.name }}
|
||||
<a-button shape="circle" size="small" @click="updateDetail(index)" v-if="item.edit">
|
||||
<a-icon type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
|
||||
</a-button>
|
||||
<a-button shape="circle" size="small" @click="hideEditDetail(index)" v-if="item.edit" style="margin-left: 5px">
|
||||
<a-icon type="close-circle" theme="twoTone" twoToneColor="#f5222d" />
|
||||
</a-button>
|
||||
<a-button shape="circle" size="small" @click="showEditDetail(index)" v-if="!item.edit">
|
||||
<a-icon type="edit" />
|
||||
</a-button>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="Delete setting?"
|
||||
@confirm="deleteDetail(index)"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
placement="right"
|
||||
>
|
||||
<a-button shape="circle" size="small">
|
||||
<a-icon type="delete" theme="twoTone" twoToneColor="#f5222d" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
<span slot="description" style="word-break: break-all">
|
||||
<span v-if="item.edit" style="display: flex">
|
||||
<a-auto-complete
|
||||
@ -54,30 +77,6 @@
|
||||
<span v-else @click="showEditDetail(index)">{{ item.value }}</span>
|
||||
</span>
|
||||
</a-list-item-meta>
|
||||
<div slot="actions">
|
||||
<a-button shape="circle" size="default" @click="updateDetail(index)" v-if="item.edit">
|
||||
<a-icon type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
|
||||
</a-button>
|
||||
<a-button shape="circle" size="default" @click="hideEditDetail(index)" v-if="item.edit">
|
||||
<a-icon type="close-circle" theme="twoTone" twoToneColor="#f5222d" />
|
||||
</a-button>
|
||||
<a-button shape="circle" @click="showEditDetail(index)" v-if="!item.edit">
|
||||
<a-icon type="edit" />
|
||||
</a-button>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<a-popconfirm
|
||||
title="Delete setting?"
|
||||
@confirm="deleteDetail(index)"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
placement="left"
|
||||
>
|
||||
<a-button shape="circle">
|
||||
<a-icon type="delete" theme="twoTone" twoToneColor="#f5222d" />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-spin>
|
||||
|
||||
@ -17,13 +17,13 @@
|
||||
|
||||
<template>
|
||||
<a-table
|
||||
size="small"
|
||||
size="middle"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:rowKey="record => record.id || record.name"
|
||||
:pagination="false"
|
||||
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
|
||||
:rowSelection="['vm', 'event', 'alert'].includes($route.name) ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
|
||||
:rowClassName="getRowClassName"
|
||||
>
|
||||
<template slot="footer">
|
||||
@ -33,7 +33,7 @@
|
||||
</template>
|
||||
|
||||
<a slot="name" slot-scope="text, record" href="javascript:;">
|
||||
<div>
|
||||
<div style="min-width: 150px; padding-left: 5px">
|
||||
<span v-if="$route.path.startsWith('/project')" style="margin-right: 5px">
|
||||
<a-button type="dashed" size="small" shape="circle" icon="login" @click="changeProject(record)" />
|
||||
</span>
|
||||
@ -41,7 +41,7 @@
|
||||
<router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
|
||||
<router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
|
||||
</div>
|
||||
<div v-if="$route.meta.related" style="padding-top: 5px">
|
||||
<div v-if="$route.meta.related" style="padding-top: 10px; padding-left: 5px; display: inline-flex">
|
||||
<span v-for="item in $route.meta.related" :key="item.path">
|
||||
<router-link
|
||||
v-if="$router.resolve('/' + item.name).route.name !== '404'"
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<a-list size="large" class="list" :loading="loading || tabLoading">
|
||||
<a-list-item :key="index" v-for="(item, index) in items" class="item">
|
||||
<a-list-item-meta>
|
||||
<span slot="title" style="word-break: break-all"><strong>{{ item.name }}</strong></span>
|
||||
<span slot="title" style="word-break: break-all">{{ item.name }}</span>
|
||||
<span slot="description" style="word-break: break-all">{{ item.description }}</span>
|
||||
</a-list-item-meta>
|
||||
|
||||
|
||||
@ -173,8 +173,16 @@ export default {
|
||||
icon: 'safety-certificate',
|
||||
label: 'Add certificate',
|
||||
dataView: true,
|
||||
args: ['name', 'certificate', 'privatekey', 'certchain', 'password'],
|
||||
show: (record) => { return record.state === 'enabled' }
|
||||
args: ['name', 'certificate', 'privatekey', 'certchain', 'password', 'account', 'domainid'],
|
||||
show: (record) => { return record.state === 'enabled' },
|
||||
mapping: {
|
||||
account: {
|
||||
value: (record) => { return record.name }
|
||||
},
|
||||
domainid: {
|
||||
value: (record) => { return record.domainid }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'deleteAccount',
|
||||
|
||||
@ -134,6 +134,7 @@
|
||||
"current": "isCurrent",
|
||||
"date": "Date",
|
||||
"dedicated": "Dedicated",
|
||||
"default": "Default",
|
||||
"deleteconfirm": "Please confirm that you would like to delete this {name}",
|
||||
"deleteprofile": "Delete Profile",
|
||||
"deploymentPlanner": "Deployment planner",
|
||||
@ -632,6 +633,10 @@
|
||||
"memused": "Memory Usage",
|
||||
"message.edit.account": "Edit (\"-1\" indicates no limit to the amount of resources create)",
|
||||
"message.assign.instance.another": "Please specify the account type, domain, account name and network (optional) of the new account. <br> If the default nic of the vm is on a shared network, CloudStack will check if the network can be used by the new account if you do not specify one network. <br> If the default nic of the vm is on a isolated network, and the new account has more one isolated networks, you should specify one.",
|
||||
"message.network.addVM.desc":"Please specify the network that you would like to add this VM to. A new NIC will be added for this network.",
|
||||
"message.network.removeNIC": "Please confirm that want to remove this NIC, which will also remove the associated network from the VM.",
|
||||
"message.network.secondaryIP" : "Please confirm that you would like to acquire a new secondary IP for this NIC. \n NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.",
|
||||
"message.network.updateIp": "Please confirm that you would like to change the IP address for this NIC on VM.",
|
||||
"minCPUNumber": "Min CPU Cores",
|
||||
"minInstance": "Min Instances",
|
||||
"minIops": "Min IOPS",
|
||||
@ -729,6 +734,7 @@
|
||||
"protocolnumber": "#Protocol",
|
||||
"provider": "HA Provider",
|
||||
"providername": "Provider",
|
||||
"provisioning": "Provisioning",
|
||||
"provisioningType": "Provisioning Type",
|
||||
"provisioningtype": "Provisioning Type",
|
||||
"publicinterface": "Public Interface",
|
||||
@ -754,6 +760,7 @@
|
||||
"redundantstate": "Redundant state",
|
||||
"redundantvpcrouter": "Redundant VPC",
|
||||
"reenterpassword": "Re-enter Password",
|
||||
"refresh": "Refresh",
|
||||
"relationaloperator": "Operator",
|
||||
"required": "Required",
|
||||
"requireshvm": "HVM",
|
||||
@ -946,6 +953,7 @@
|
||||
"zoneId": "Zone",
|
||||
"zoneid": "Zone",
|
||||
"zonename": "Zone",
|
||||
"zonenamelabel": "Zone Name",
|
||||
"instance": "Instance",
|
||||
"yourInstance": "Your instance",
|
||||
"newInstance": "New instance",
|
||||
|
||||
@ -19,23 +19,25 @@
|
||||
<div>
|
||||
<a-card class="breadcrumb-card">
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<breadcrumb style="padding-top: 6px" />
|
||||
<a-col :span="24" style="display: flex">
|
||||
<breadcrumb />
|
||||
<a-tooltip placement="bottom">
|
||||
<template slot="title">
|
||||
{{ "Refresh" }}
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-left: 8px"
|
||||
:loading="loading"
|
||||
shape="round"
|
||||
size="small"
|
||||
icon="sync"
|
||||
@click="fetchData()">
|
||||
{{ $t('refresh') }}
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<span style="float: right">
|
||||
<a-tooltip placement="bottom">
|
||||
<template slot="title">
|
||||
{{ "Refresh" }}
|
||||
</template>
|
||||
<a-button
|
||||
:loading="loading"
|
||||
shape="circle"
|
||||
type="dashed"
|
||||
icon="reload"
|
||||
style="margin-right: 5px"
|
||||
@click="fetchData()" />
|
||||
</a-tooltip>
|
||||
<a-col :span="24" style="padding-top: 12px; margin-bottom: -6px">
|
||||
<span style="padding-left: 5px">
|
||||
<a-tooltip
|
||||
v-for="(action, actionIndex) in actions"
|
||||
:key="actionIndex"
|
||||
@ -56,7 +58,7 @@
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-input-search
|
||||
style="width: unset"
|
||||
style="width: 50%; padding-left: 6px"
|
||||
placeholder="Search"
|
||||
v-model="searchQuery"
|
||||
v-if="!dataView && !treeView"
|
||||
|
||||
@ -17,18 +17,22 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-collapse v-model="activeKey">
|
||||
<a-collapse v-model="activeKey" :bordered="false">
|
||||
|
||||
<a-collapse-panel :header="'ISO: ' + vm.isoname" v-if="vm.isoid" key="1">
|
||||
<a-list
|
||||
itemLayout="horizontal">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="vm.isoid">
|
||||
<a slot="title" href="">
|
||||
<router-link :to="{ path: '/iso/' + vm.isoid }">{{ vm.isoname }}</router-link>
|
||||
</a> ({{ vm.isoname }})
|
||||
<a-avatar slot="avatar">
|
||||
<a-icon type="usb" />
|
||||
</a-avatar>
|
||||
<a-list-item-meta>
|
||||
<div slot="avatar">
|
||||
<a-avatar>
|
||||
<a-icon type="usb" />
|
||||
</a-avatar>
|
||||
</div>
|
||||
<div slot="title">
|
||||
<router-link :to="{ path: '/iso/' + vm.isoid }">{{ vm.isoname }}</router-link> <br/>
|
||||
<a-icon type="barcode"/> {{ vm.isoid }}
|
||||
</div>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
@ -42,59 +46,220 @@
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item">
|
||||
<a-list-item-meta>
|
||||
<div slot="avatar">
|
||||
<a-avatar>
|
||||
<a-icon type="hdd" />
|
||||
</a-avatar>
|
||||
</div>
|
||||
<div slot="title">
|
||||
<router-link :to="{ path: '/volume/' + item.id }">{{ item.name }}</router-link> ({{ item.type }}) <br/>
|
||||
<status :text="item.state" displayText /><br/>
|
||||
<router-link :to="{ path: '/volume/' + item.id }">{{ item.name }} </router-link>
|
||||
<a-tag v-if="item.type">
|
||||
{{ item.type }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.state">
|
||||
{{ item.state }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.provisioningtype">
|
||||
{{ item.provisioningtype }}
|
||||
</a-tag>
|
||||
<br/>
|
||||
{{ $t('size') }}: {{ (item.size / (1024 * 1024 * 1024.0)).toFixed(2) }} GB<br/>
|
||||
{{ $t('physicalsize') }}: {{ (item.physicalsize / (1024 * 1024 * 1024.0)).toFixed(4) }} GB<br/>
|
||||
{{ $t('storagePool') }}: {{ item.storage }} ({{ item.storagetype }})<br/>
|
||||
<a-icon type="barcode"/> {{ item.id }} <br/>
|
||||
</div>
|
||||
<div slot="description">
|
||||
<a-icon type="barcode"/> {{ item.id }}
|
||||
</div>
|
||||
<a-avatar slot="avatar">
|
||||
<a-icon type="hdd" />
|
||||
</a-avatar>
|
||||
</a-list-item-meta>
|
||||
<p>
|
||||
Size: {{ (item.size / (1024 * 1024 * 1024.0)).toFixed(4) }} GB<br/>
|
||||
Physical Size: {{ (item.physicalsize / (1024 * 1024 * 1024.0)).toFixed(4) }} GB<br/>
|
||||
Provisioning: {{ item.provisioningtype }}<br/>
|
||||
Storage Pool: {{ item.storage }} ({{ item.storagetype }})<br/>
|
||||
</p>
|
||||
<div slot="actions" class="actions">
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="'Network Adapter(s): ' + (vm && vm.nic ? vm.nic.length : 0)" key="3" >
|
||||
<a-button type="primary" @click="showAddModal" :loading="loadingNic">
|
||||
<a-icon type="plus"></a-icon> {{ $t('label.network.addVM') }}
|
||||
</a-button>
|
||||
<a-list
|
||||
size="small"
|
||||
itemLayout="horizontal"
|
||||
:dataSource="vm.nic"
|
||||
class="list"
|
||||
:loading="loadingNic"
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item">
|
||||
<a-list-item slot="renderItem" slot-scope="item" class="list__item">
|
||||
<a-list-item-meta>
|
||||
<div slot="title">
|
||||
<span v-show="item.isdefault">(Default) </span>
|
||||
<router-link :to="{ path: '/guestnetwork/' + item.networkid }">{{ item.networkname }} </router-link><br/>
|
||||
Mac Address: {{ item.macaddress }}<br/>
|
||||
<span v-if="item.ipaddress">Address: {{ item.ipaddress }} <br/></span>
|
||||
Netmask: {{ item.netmask }}<br/>
|
||||
Gateway: {{ item.gateway }}<br/>
|
||||
<div slot="avatar">
|
||||
<a-avatar slot="avatar">
|
||||
<a-icon type="wifi" />
|
||||
</a-avatar>
|
||||
<br/>
|
||||
<a-popconfirm
|
||||
title="Please confirm that you would like to make this NIC the default for this VM."
|
||||
@confirm="setAsDefault(item)"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<a-button
|
||||
style="margin-top: 10px"
|
||||
icon="arrow-right"
|
||||
size="small"
|
||||
shape="round" />
|
||||
</a-popconfirm>
|
||||
<br/>
|
||||
<a-tooltip placement="right" v-if="item.type !== 'L2'">
|
||||
<template slot="title">
|
||||
{{ "Change IP Address" }}
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-top: 10px"
|
||||
icon="swap"
|
||||
size="small"
|
||||
shape="round"
|
||||
@click="editIpAddressNic = item.id; showUpdateIpModal = true" />
|
||||
</a-tooltip>
|
||||
<br/>
|
||||
<a-tooltip placement="right" v-if="item.type !== 'L2'">
|
||||
<template slot="title">
|
||||
{{ "Manage Secondary IP Addresses" }}
|
||||
</template>
|
||||
<a-button
|
||||
style="margin-top: 10px"
|
||||
icon="environment"
|
||||
size="small"
|
||||
shape="round"
|
||||
@click="fetchSecondaryIPs(item.id)" />
|
||||
</a-tooltip>
|
||||
<br/>
|
||||
<a-popconfirm
|
||||
:title="$t('message.network.removeNIC')"
|
||||
@confirm="removeNIC(item)"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
v-if="!item.isdefault"
|
||||
>
|
||||
<a-button
|
||||
style="margin-top: 10px"
|
||||
type="danger"
|
||||
icon="delete"
|
||||
size="small"
|
||||
shape="round" />
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
<div slot="description">
|
||||
<div slot="title">
|
||||
<router-link :to="{ path: '/guestnetwork/' + item.networkid }">{{ item.networkname }} </router-link>
|
||||
<a-tag v-if="item.isdefault">
|
||||
{{ $t('default') }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.type">
|
||||
{{ item.type }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.broadcasturi">
|
||||
{{ item.broadcasturi }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.isolationuri">
|
||||
{{ item.isolationuri }}
|
||||
</a-tag>
|
||||
<br />
|
||||
{{ $t('macaddress') }}: {{ item.macaddress }}<br/>
|
||||
<span v-if="item.ipaddress">
|
||||
{{ $t('IP Address') }}: {{ item.ipaddress }}
|
||||
<br/>
|
||||
</span>
|
||||
<span v-if="item.secondaryip && item.type !== 'L2'">
|
||||
{{ $t('Secondary IPs') }}: {{ item.secondaryip.map(x => x.ipaddress).join(', ') }}
|
||||
<br/>
|
||||
</span>
|
||||
<span v-if="item.netmask">
|
||||
{{ $t('netmask') }}: {{ item.netmask }}
|
||||
<br/>
|
||||
</span>
|
||||
<span v-if="item.gateway">
|
||||
{{ $t('gateway') }}: {{ item.gateway }}
|
||||
<br/>
|
||||
</span>
|
||||
<a-icon type="barcode"/> {{ item.id }}
|
||||
</div>
|
||||
<a-avatar slot="avatar">
|
||||
<a-icon type="wifi" />
|
||||
</a-avatar>
|
||||
</a-list-item-meta>
|
||||
<p>
|
||||
Type: {{ item.type }}<br/>
|
||||
Broadcast URI: {{ item.broadcasturi }}<br/>
|
||||
Isolation URI: {{ item.isolationuri }}<br/>
|
||||
</p>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
<a-modal
|
||||
:visible="showAddNetworkModal"
|
||||
:title="$t('label.network.addVM')"
|
||||
@cancel="closeModals"
|
||||
@ok="submitAddNetwork">
|
||||
{{ $t('message.network.addVM.desc') }}
|
||||
|
||||
<div class="modal-form">
|
||||
<p class="modal-form__label">{{ $t('Network') }}:</p>
|
||||
<a-select :defaultValue="addNetworkData.network" @change="e => addNetworkData.network === e">
|
||||
<a-select-option
|
||||
v-for="network in addNetworkData.allNetworks"
|
||||
:key="network.id"
|
||||
:value="network.id">
|
||||
{{ network.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<p class="modal-form__label">{{ $t('publicip') }}:</p>
|
||||
<a-input v-model="addNetworkData.ip"></a-input>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
:visible="showUpdateIpModal"
|
||||
:title="$t('label.change.ipaddress')"
|
||||
@cancel="closeModals"
|
||||
@ok="submitUpdateIP"
|
||||
>
|
||||
{{ $t('message.network.updateIp') }}
|
||||
|
||||
<div class="modal-form">
|
||||
<p class="modal-form__label">{{ $t('publicip') }}:</p>
|
||||
<a-input v-model="editIpAddressValue"></a-input>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
:visible="showSecondaryIpModal"
|
||||
:title="$t('label.acquire.new.secondary.ip')"
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
class="wide-modal"
|
||||
>
|
||||
<p>
|
||||
{{ $t('message.network.secondaryIP') }}
|
||||
</p>
|
||||
<a-divider />
|
||||
<a-input placeholder="Enter new secondary IP address" v-model="newSecondaryIp"></a-input>
|
||||
<div style="margin-top: 10px; display: flex; justify-content:flex-end;">
|
||||
<a-button @click="submitSecondaryIP" type="primary" style="margin-right: 10px;">Add Secondary IP</a-button>
|
||||
<a-button @click="closeModals">Close</a-button>
|
||||
</div>
|
||||
|
||||
<a-divider />
|
||||
<a-list itemLayout="vertical">
|
||||
<a-list-item v-for="(ip, index) in secondaryIPs" :key="index">
|
||||
<a-popconfirm
|
||||
title="Release IP?"
|
||||
@confirm="removeSecondaryIP(ip.id)"
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<a-button
|
||||
type="danger"
|
||||
shape="circle"
|
||||
size="small"
|
||||
icon="delete" />
|
||||
{{ ip.ipaddress }}
|
||||
</a-popconfirm>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -120,12 +285,27 @@ export default {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
data () {
|
||||
return {
|
||||
vm: {},
|
||||
volumes: [],
|
||||
totalStorage: 0,
|
||||
activeKey: ['1', '2', '3']
|
||||
activeKey: ['1', '2', '3'],
|
||||
showAddNetworkModal: false,
|
||||
showUpdateIpModal: false,
|
||||
showSecondaryIpModal: false,
|
||||
addNetworkData: {
|
||||
allNetworks: [],
|
||||
network: '',
|
||||
ip: ''
|
||||
},
|
||||
loadingNic: false,
|
||||
editIpAddressNic: '',
|
||||
editIpAddressValue: '',
|
||||
secondaryIPs: [],
|
||||
selectedNicId: '',
|
||||
newSecondaryIp: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -154,26 +334,339 @@ export default {
|
||||
}
|
||||
this.$set(this.resource, 'volumes', this.volumes)
|
||||
})
|
||||
},
|
||||
listNetworks () {
|
||||
api('listNetworks', {
|
||||
listAll: 'true',
|
||||
zoneid: this.vm.zoneid
|
||||
}).then(response => {
|
||||
this.addNetworkData.allNetworks = response.listnetworksresponse.network.filter(network => !this.vm.nic.map(nic => nic.networkid).includes(network.id))
|
||||
this.addNetworkData.network = this.addNetworkData.allNetworks[0].id
|
||||
})
|
||||
},
|
||||
fetchSecondaryIPs (nicId) {
|
||||
this.showSecondaryIpModal = true
|
||||
this.selectedNicId = nicId
|
||||
api('listNics', {
|
||||
nicId: nicId,
|
||||
keyword: '',
|
||||
virtualmachineid: this.vm.id
|
||||
}).then(response => {
|
||||
this.secondaryIPs = response.listnicsresponse.nic[0].secondaryip
|
||||
})
|
||||
},
|
||||
showAddModal () {
|
||||
this.showAddNetworkModal = true
|
||||
this.listNetworks()
|
||||
},
|
||||
closeModals () {
|
||||
this.showAddNetworkModal = false
|
||||
this.showUpdateIpModal = false
|
||||
this.showSecondaryIpModal = false
|
||||
this.addNetworkData.network = ''
|
||||
this.addNetworkData.ip = ''
|
||||
this.editIpAddressValue = ''
|
||||
this.newSecondaryIp = ''
|
||||
},
|
||||
submitAddNetwork () {
|
||||
const params = {}
|
||||
params.virtualmachineid = this.vm.id
|
||||
params.networkid = this.addNetworkData.network
|
||||
if (this.addNetworkData.ip) {
|
||||
params.ipaddress = this.addNetworkData.ip
|
||||
}
|
||||
this.showAddNetworkModal = false
|
||||
this.loadingNic = true
|
||||
api('addNicToVirtualMachine', params).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.addnictovirtualmachineresponse.jobid,
|
||||
successMessage: `Successfully added network`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: 'Adding network failed',
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Adding network...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
})
|
||||
},
|
||||
setAsDefault (item) {
|
||||
this.loadingNic = true
|
||||
api('updateDefaultNicForVirtualMachine', {
|
||||
virtualmachineid: this.vm.id,
|
||||
nicid: item.id
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.updatedefaultnicforvirtualmachineresponse.jobid,
|
||||
successMessage: `Successfully set ${item.networkname} to default. Please manually update the default NIC on the VM now.`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: `Error setting ${item.networkname} to default`,
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Setting ${item.networkname} to default...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
})
|
||||
},
|
||||
submitUpdateIP () {
|
||||
this.loadingNic = true
|
||||
this.showUpdateIpModal = false
|
||||
api('updateVmNicIp', {
|
||||
nicId: this.editIpAddressNic,
|
||||
ipaddress: this.editIpAddressValue
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.updatevmnicipresponse.jobid,
|
||||
successMessage: `Successfully updated IP Address`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: `Error`,
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Updating IP Address...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.closeModals()
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
})
|
||||
},
|
||||
removeNIC (item) {
|
||||
this.loadingNic = true
|
||||
|
||||
api('removeNicFromVirtualMachine', {
|
||||
nicid: item.id,
|
||||
virtualmachineid: this.vm.id
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.removenicfromvirtualmachineresponse.jobid,
|
||||
successMessage: `Successfully removed`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: `There was an error`,
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Removing NIC...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
})
|
||||
},
|
||||
submitSecondaryIP () {
|
||||
this.loadingNic = true
|
||||
|
||||
const params = {}
|
||||
params.nicid = this.selectedNicId
|
||||
if (this.newSecondaryIp) {
|
||||
params.ipaddress = this.newSecondaryIp
|
||||
}
|
||||
|
||||
api('addIpToNic', params).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.addiptovmnicresponse.jobid,
|
||||
successMessage: `Successfully added secondary IP Address`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: `There was an error adding the secondary IP Address`,
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Add Secondary IP address...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.addiptovmnicresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
})
|
||||
},
|
||||
removeSecondaryIP (id) {
|
||||
this.loadingNic = true
|
||||
|
||||
api('removeIpFromNic', { id }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.removeipfromnicresponse.jobid,
|
||||
successMessage: `Successfully removed secondary IP Address`,
|
||||
successMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
},
|
||||
errorMessage: `There was an error removing the secondary IP Address`,
|
||||
errorMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
},
|
||||
loadingMessage: `Removing Secondary IP address...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
this.parentFetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.loadingNic = false
|
||||
this.fetchSecondaryIPs(this.selectedNicId)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page-header-wrapper-grid-content-main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
transition: 0.3s;
|
||||
.vm-detail {
|
||||
.svg-inline--fa {
|
||||
margin-left: -1px;
|
||||
margin-right: 8px;
|
||||
<style lang="scss" scoped>
|
||||
.page-header-wrapper-grid-content-main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
transition: 0.3s;
|
||||
.vm-detail {
|
||||
.svg-inline--fa {
|
||||
margin-left: -1px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 20px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__label {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
|
||||
&--no-margin {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
margin-left: -24px;
|
||||
|
||||
button {
|
||||
&:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: column;
|
||||
margin-left: 24px;
|
||||
|
||||
button {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.wide-modal {
|
||||
min-width: 50vw;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -19,23 +19,24 @@
|
||||
<a-row :gutter="24">
|
||||
<a-col :md="24">
|
||||
<a-card class="breadcrumb-card">
|
||||
<a-col :md="14">
|
||||
<a-col :md="24" style="display: flex">
|
||||
<breadcrumb style="padding-top: 6px" />
|
||||
</a-col>
|
||||
<a-col :md="10">
|
||||
<a-button
|
||||
style="margin-left: 10px; float: right"
|
||||
@click="fetchData()"
|
||||
icon="reload"
|
||||
style="margin-left: 12px; margin-top: 4px"
|
||||
:loading="loading"
|
||||
type="primary">
|
||||
{{ $t('Refresh') }}
|
||||
icon="reload"
|
||||
size="small"
|
||||
shape="round"
|
||||
@click="fetchData()" >
|
||||
{{ $t('refresh') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
style="margin-left: 10px; float: right"
|
||||
@click="sslFormVisible = true"
|
||||
icon="safety-certificate">
|
||||
{{ $t('SSL Certificate') }}
|
||||
style="margin-left: 12px; margin-top: 4px"
|
||||
icon="safety-certificate"
|
||||
size="small"
|
||||
shape="round"
|
||||
@click="sslFormVisible = true">
|
||||
{{ $t('Setup SSL Certificate') }}
|
||||
</a-button>
|
||||
<a-modal
|
||||
:title="$t('SSL Certificate')"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user