This commit is contained in:
Gabriel P Santos 2024-09-05 10:42:47 -03:00 committed by Gabriel P. Santos
parent 5893ba5a8c
commit 8b0cd2ca54
10 changed files with 284 additions and 216 deletions

View File

@ -996,6 +996,7 @@ public class ApiConstants {
public static final String VPC_OFF_NAME = "vpcofferingname";
public static final String VPC_OFFERING_CONSERVE_MODE = "vpcofferingconservemode";
public static final String NETWORK = "network";
public static final String VPC_ACCESS = "vpcaccess";
public static final String VPC_ID = "vpcid";
public static final String VPC_NAME = "vpcname";
public static final String VPC_GATEWAY_ID = "vpcgatewayid";

View File

@ -240,7 +240,7 @@ public class ListNetworksCmd extends BaseListRetrieveOnlyResourceCountCmd implem
private void updateNetworkResponse(List<NetworkResponse> response) {
for (NetworkResponse networkResponse : response) {
ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Network, networkResponse.getId());
if (resourceIcon == null && networkResponse.getVpcId() != null) {
if (resourceIcon == null && networkResponse.getVpcId() != null && networkResponse.getVpcAccess()) {
resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Vpc, networkResponse.getVpcId());
}
if (resourceIcon == null) {

View File

@ -143,6 +143,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
@Param(description = "Purpose of the IP address. In Acton this value is not null for IPs with isSystem=true, and can have either StaticNat or LB value")
private String purpose;
@SerializedName(ApiConstants.VPC_ACCESS)
@Param(description = "Whether the calling account has access to this network's VPC", since = "4.20")
private boolean vpcAccess;
@SerializedName(ApiConstants.VPC_ID)
@Param(description = "VPC ID the IP belongs to")
private String vpcId;
@ -301,6 +305,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
this.purpose = purpose;
}
public void setVpcAccess(boolean vpcAccess) {
this.vpcAccess = vpcAccess;
}
public void setVpcId(String vpcId) {
this.vpcId = vpcId;
}

View File

@ -203,6 +203,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
@Param(description = "True if Network supports specifying IP ranges, false otherwise")
private Boolean specifyIpRanges;
@SerializedName(ApiConstants.VPC_ACCESS)
@Param(description = "Whether the calling account has access to this network's VPC", since = "4.20")
private boolean vpcAccess;
@SerializedName(ApiConstants.VPC_ID)
@Param(description = "VPC the Network belongs to")
private String vpcId;
@ -523,6 +527,14 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
this.specifyIpRanges = specifyIpRanges;
}
public void setVpcAccess(boolean vpcAccess) {
this.vpcAccess = vpcAccess;
}
public boolean getVpcAccess() {
return vpcAccess;
}
public void setVpcId(String vpcId) {
this.vpcId = vpcId;
}

View File

@ -1167,7 +1167,7 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
}
setVpcIdInResponse(ipAddr.getVpcId(), ipResponse::setVpcId, ipResponse::setVpcName);
setVpcIdInResponse(ipAddr.getVpcId(), ipResponse::setVpcId, ipResponse::setVpcName, ipResponse::setVpcAccess);
// Network id the ip is associated with (if associated networkId is
@ -1242,20 +1242,24 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
return ipResponse;
}
private void setVpcIdInResponse(Long vpcId, Consumer<String> vpcUuidSetter, Consumer<String> vpcNameSetter) {
if (vpcId != null) {
Vpc vpc = ApiDBUtils.findVpcById(vpcId);
if (vpc != null) {
try {
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, false, vpc);
vpcUuidSetter.accept(vpc.getUuid());
} catch (PermissionDeniedException e) {
logger.debug("Not setting the vpcId to the response because the caller does not have access to the VPC");
}
vpcNameSetter.accept(vpc.getName());
}
private void setVpcIdInResponse(Long vpcId, Consumer<String> vpcUuidSetter, Consumer<String> vpcNameSetter, Consumer<Boolean> vpcAccessSetter) {
if (vpcId == null) {
return;
}
Vpc vpc = ApiDBUtils.findVpcById(vpcId);
if (vpc == null) {
return;
}
try {
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, false, vpc);
vpcAccessSetter.accept(true);
} catch (PermissionDeniedException e) {
vpcAccessSetter.accept(false);
logger.debug("Setting [%s] as false because the caller does not have access to the VPC [%s].", ApiConstants.VPC_ACCESS, vpc);
}
vpcNameSetter.accept(vpc.getName());
vpcUuidSetter.accept(vpc.getUuid());
}
private void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress ipAddr, IPAddressResponse ipResponse) {
@ -2799,7 +2803,7 @@ public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
response.setSpecifyIpRanges(network.getSpecifyIpRanges());
setVpcIdInResponse(network.getVpcId(), response::setVpcId, response::setVpcName);
setVpcIdInResponse(network.getVpcId(), response::setVpcId, response::setVpcName, response::setVpcAccess);
setResponseAssociatedNetworkInformation(response, network.getId());

View File

@ -247,7 +247,7 @@ public class ApiResponseHelperTest {
public void setResponseIpAddressTestIpv4() {
NicSecondaryIp result = Mockito.mock(NicSecondaryIp.class);
NicSecondaryIpResponse response = new NicSecondaryIpResponse();
setResult(result, "ipv4", "ipv6");
setResult(result, "ipv4", "");
ApiResponseHelper.setResponseIpAddress(result, response);

View File

@ -504,6 +504,127 @@
<gold-outlined />
<router-link :to="{ path: '/autoscalevmgroup/' + resource.autoscalevmgroupid }">{{ resource.autoscalevmgroupname || resource.autoscalevmgroupid }}</router-link>
</div>
</div>
</div>
<div class="resource-detail-item" v-if="resource.resourcetype && resource.resourceid && routeFromResourceType">
<div class="resource-detail-item__label">{{ $t('label.resource') }}</div>
<div class="resource-detail-item__details">
<resource-label :resourceType="resource.resourcetype" :resourceId="resource.resourceid" :resourceName="resource.resourcename" />
</div>
</div>
<div class="resource-detail-item" v-if="resource.virtualmachineid">
<div class="resource-detail-item__label">{{ $t('label.vmname') }}</div>
<div class="resource-detail-item__details">
<desktop-outlined />
<router-link :to="{ path: createPathBasedOnVmType(resource.vmtype, resource.virtualmachineid) }">{{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} </router-link>
<status class="status status--end" :text="resource.vmstate" v-if="resource.vmstate"/>
</div>
</div>
<div class="resource-detail-item" v-if="resource.volumeid">
<div class="resource-detail-item__label">{{ $t('label.volume') }}</div>
<div class="resource-detail-item__details">
<hdd-outlined />
<router-link v-if="validLinks.volume" :to="{ path: '/volume/' + resource.volumeid }">{{ resource.volumename || resource.volume || resource.volumeid }} </router-link>
<span v-else>{{ resource.volumename || resource.volume || resource.volumeid }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="resource.associatednetworkid">
<div class="resource-detail-item__label">{{ $t('label.associatednetwork') }}</div>
<div class="resource-detail-item__details">
<wifi-outlined />
<router-link :to="{ path: '/guestnetwork/' + resource.associatednetworkid }">{{ resource.associatednetworkname || resource.associatednetwork || resource.associatednetworkid }} </router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.sourceipaddressnetworkid">
<div class="resource-detail-item__label">{{ $t('label.network') }}</div>
<div class="resource-detail-item__details">
<wifi-outlined />
<router-link :to="{ path: '/guestnetwork/' + resource.sourceipaddressnetworkid }">{{ resource.sourceipaddressnetworkname || resource.sourceipaddressnetworkid }} </router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.guestnetworkid">
<div class="resource-detail-item__label">{{ $t('label.guestnetwork') }}</div>
<div class="resource-detail-item__details">
<gateway-outlined />
<router-link :to="{ path: '/guestnetwork/' + resource.guestnetworkid }">{{ resource.guestnetworkname || resource.guestnetworkid }} </router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.publicip">
<div class="resource-detail-item__label">{{ $t('label.public.ip') }}</div>
<div class="resource-detail-item__details">
<gateway-outlined />
<router-link v-if="resource.publicipid" :to="{ path: '/publicip/' + resource.publicipid }">{{ resource.publicip }} </router-link>
<copy-label v-if="resource.publicipid" :copyValue="resource.publicip" :showIcon=true />
<copy-label v-else :label="resource.publicip" />
</div>
</div>
<div class="resource-detail-item" v-if="resource.vpcid">
<div class="resource-detail-item__label">{{ $t('label.vpcname') }}</div>
<div class="resource-detail-item__details">
<span v-if="images.vpc">
<resource-icon :image="getImage(images.vpc)" size="1x" style="margin-right: 5px"/>
</span>
<deployment-unit-outlined v-else />
<router-link v-if="resource.vpcaccess" :to="{ path: '/vpc/' + resource.vpcid }">{{ resource.vpcname || resource.vpcid }}</router-link>
<span v-else>{{ resource.vpcname || resource.vpcid }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="resource.aclid">
<div class="resource-detail-item__label">{{ $t('label.aclid') }}</div>
<div class="resource-detail-item__details">
<span v-if="images.acl">
<resource-icon :image="getImage(images.acl)" size="1x" style="margin-right: 5px"/>
</span>
<deployment-unit-outlined v-else />
<router-link :to="{ path: '/acllist/' + resource.aclid }">{{ resource.aclname || resource.aclid }}</router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.affinitygroup && resource.affinitygroup.length > 0">
<div class="resource-detail-item__label">{{ $t('label.affinitygroup') }}</div>
<SwapOutlined />
<span
v-for="(group, index) in resource.affinitygroup"
:key="group.id"
>
<router-link :to="{ path: '/affinitygroup/' + group.id }">{{ group.name }}</router-link>
<span v-if="index + 1 < resource.affinitygroup.length">, </span>
</span>
</div>
<div class="resource-detail-item" v-if="resource.templateid">
<div class="resource-detail-item__label">{{ resource.templateformat === 'ISO'? $t('label.iso') : $t('label.templatename') }}</div>
<div class="resource-detail-item__details">
<resource-icon v-if="resource.icon" :image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 5px"/>
<SaveOutlined v-else />
<router-link :to="{ path: (resource.templateformat === 'ISO' ? '/iso/' : '/template/') + resource.templateid }">{{ resource.templatedisplaytext || resource.templatename || resource.templateid }} </router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.isoid">
<div class="resource-detail-item__label">{{ $t('label.isoname') }}</div>
<div class="resource-detail-item__details">
<resource-icon v-if="resource.icon" :image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 5px"/>
<UsbOutlined v-else />
<router-link :to="{ path: '/iso/' + resource.isoid }">{{ resource.isodisplaytext || resource.isoname || resource.isoid }} </router-link>
</div>
</div>
<div class="resource-detail-item" v-if="resource.serviceofferingname && resource.serviceofferingid">
<div class="resource-detail-item__label" v-if="$route.meta.name === 'router' || $route.meta.name === 'systemvm'">{{ $t('label.system.offering') }}</div>
<div class="resource-detail-item__label" v-else >{{ $t('label.serviceofferingname') }}</div>
<div class="resource-detail-item__details">
<cloud-outlined />
<router-link v-if="!isStatic && ($route.meta.name === 'router' || $route.meta.name === 'systemvm')" :to="{ path: '/systemoffering/' + resource.serviceofferingid}">{{ resource.serviceofferingname || resource.serviceofferingid }} </router-link>
<router-link v-else-if="$router.resolve('/computeoffering/' + resource.serviceofferingid).matched[0].redirect !== '/exception/404'" :to="{ path: '/computeoffering/' + resource.serviceofferingid }">{{ resource.serviceofferingname || resource.serviceofferingid }} </router-link>
<span v-else>{{ resource.serviceofferingname || resource.serviceofferingid }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="resource.rootdiskofferingid && resource.rootdiskofferingdisplaytext || resource.datadiskofferingid && resource.datadiskofferingdisplaytext">
<div class="resource-detail-item__label">{{ $t('label.diskoffering') }}</div>
<div class="resource-detail-item__details">
<hdd-outlined />
<div v-if="resource.rootdiskofferingid">
<router-link v-if="!isStatic && $router.resolve('/diskoffering/' + resource.rootdiskofferingid).matched[0].redirect !== '/exception/404'" :to="{ path: '/diskoffering/' + resource.rootdiskofferingid }">{{ resource.rootdiskofferingdisplaytext }}</router-link>
<span v-else>{{ resource.rootdiskofferingdisplaytext }}</span>
</div>
<div class="resource-detail-item" v-if="resource.keypairs && resource.keypairs.length > 0">
<div class="resource-detail-item__label">{{ $t('label.keypairs') }}</div>

View File

@ -582,7 +582,7 @@
<router-link :to="{ path: '/guestnetwork/' + record.associatednetworkid }">{{ text }}</router-link>
</template>
<template v-if="column.key === 'vpcname'">
<a v-if="record.vpcid">
<a v-if="record.vpcid && record.vpcaccess">
<router-link :to="{ path: '/vpc/' + record.vpcid }">{{ text || record.vpcid }}</router-link>
</a>
<span v-else>{{ text }}</span>

View File

@ -47,12 +47,12 @@
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option key="all" value="">
{{ $t('label.view.all') }}
</a-select-option>
<a-select-option v-for="network in networksList" :key="network.id" :value="network.id" :label="network.name">
<a-select-option v-for="network in networksList" :key="network.id" :value="network.id">
{{ network.name }}
</a-select-option>
</a-select>
@ -65,57 +65,35 @@
:rowKey="item => item.id"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
:pagination="false" >
<template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'ipaddress'">
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }}&nbsp;</router-link>
<div v-else>{{ text }}</div>
<template v-if="record.issourcenat === true">
<a-tag>{{ $t('label.sourcenat') }}</a-tag>
</template>
<template v-else-if="record.isstaticnat === true">
<a-tag>{{ $t('label.staticnat') }}</a-tag>
</template>
<template v-else-if="record.hasrules === false">
<tooltip-button
v-if="record.forvirtualnetwork === true"
:tooltip="$t('label.action.set.as.source.nat.ip')"
type="primary"
:danger="false"
icon="aim-outlined"
:disabled="!('updateNetwork' in $store.getters.apis)"
@onClick="showChangeSourceNat(record)"></tooltip-button>
</template>
<template v-else><!-- -if="record.hasrules === true" -->
<Tooltip placement="topLeft" :title="$t('message.sourcenatip.change.inhibited')" >
<a-tag>{{ $t('label.hasrules') }}</a-tag>
</Tooltip>
</template>
</template>
<template #ipaddress="{ text, record }">
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
<div v-else>{{ text }}</div>
<a-tag v-if="record.issourcenat === true">source-nat</a-tag>
</template>
<template v-if="column.key === 'state'">
<status :text="record.state" displayText />
</template>
<template #state="{ record }">
<status :text="record.state" displayText />
</template>
<template v-if="column.key === 'virtualmachineid'">
<desktop-outlined v-if="record.virtualmachineid" />
<router-link :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
</template>
<template #virtualmachineid="{ record }">
<desktop-outlined v-if="record.virtualmachineid" />
<router-link :to="{ path: '/vm/' + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
</template>
<template v-if="column.key === 'associatednetworkname'">
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
<div v-else>{{ record.networkname }}</div>
</template>
<template #associatednetworkname="{ record }">
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
<div v-else>{{ record.networkname }}</div>
</template>
<template v-if="column.key === 'actions'">
<tooltip-button
v-if="record.issourcenat !== true && record.forvirtualnetwork === true"
:tooltip="$t('label.action.release.ip')"
type="primary"
:danger="true"
icon="delete-outlined"
:disabled="!('disassociateIpAddress' in $store.getters.apis)"
@onClick="releaseIpAddress(record)" />
</template>
<template #action="{ record }">
<tooltip-button
v-if="record.issourcenat !== true && record.forvirtualnetwork === true"
:tooltip="$t('label.action.release.ip')"
type="primary"
:danger="true"
icon="delete-outlined"
:disabled="!('disassociateIpAddress' in $store.getters.apis)"
@onClick="releaseIpAddress(record)" />
</template>
</a-table>
<a-divider/>
@ -148,17 +126,19 @@
<a-alert :message="$t('message.action.acquire.ip')" type="warning" />
<a-form layout="vertical" style="margin-top: 10px">
<a-form-item :label="$t('label.ipaddress')">
<infinite-scroll-select
<a-select
v-focus="true"
style="width: 100%;"
v-model:value="acquireIp"
api="listPublicIpAddresses"
:apiParams="listApiParamsForAssociate"
resourceType="publicipaddress"
optionValueKey="ipaddress"
:optionLabelFn="ip => ip.ipaddress + ' (' + ip.state + ')'"
defaultIcon="environment-outlined"
:autoSelectFirstOption="true"
@change-option-value="(ip) => acquireIp = ip" />
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="ip in listPublicIpAddress"
:key="ip.ipaddress">{{ ip.ipaddress }} ({{ ip.state }})</a-select-option>
</a-select>
</a-form-item>
<div :span="24" class="action-button">
<a-button @click="onCloseModal">{{ $t('label.cancel') }}</a-button>
@ -167,24 +147,6 @@
</a-form>
</a-spin>
</a-modal>
<a-modal
v-if="changeSourceNat"
:title="$t('message.sourcenatip.change.warning')"
:visible="changeSourceNat"
:closable="true"
:footer="null"
@cancel="cancelChangeSourceNat"
centered
:disabled="!('updateNetwork' in $store.getters.apis)"
width="450px">
<template>
<a-alert :message="$t('message.sourcenatip.change.warning')" type="warning" />
</template>
<div :span="24" class="action-button">
<a-button @click="cancelChangeSourceNat">{{ $t('label.cancel') }}</a-button>
<a-button ref="submit" type="primary" @click="setSourceNatIp(record)">{{ $t('label.ok') }}</a-button>
</div>
</a-modal>
<bulk-action-view
v-if="showConfirmationAction || showGroupActionModal"
:showConfirmationAction="showConfirmationAction"
@ -202,22 +164,19 @@
@close-modal="closeModal" />
</div>
</template>
<script>
import { getAPI, postAPI } from '@/api'
import { api } from '@/api'
import Status from '@/components/widgets/Status'
import TooltipButton from '@/components/widgets/TooltipButton'
import BulkActionView from '@/components/view/BulkActionView'
import eventBus from '@/config/eventBus'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect'
export default {
name: 'IpAddressesTab',
components: {
Status,
TooltipButton,
BulkActionView,
InfiniteScrollSelect
BulkActionView
},
props: {
resource: {
@ -245,7 +204,7 @@ export default {
showGroupActionModal: false,
selectedItems: [],
selectedColumns: [],
filterColumns: ['Actions'],
filterColumns: ['Action'],
showConfirmationAction: false,
message: {
title: this.$t('label.action.bulk.release.public.ip.address'),
@ -253,39 +212,37 @@ export default {
},
columns: [
{
key: 'ipaddress',
title: this.$t('label.ipaddress'),
dataIndex: 'ipaddress'
dataIndex: 'ipaddress',
slots: { customRender: 'ipaddress' }
},
{
key: 'state',
title: this.$t('label.state'),
dataIndex: 'state'
dataIndex: 'state',
slots: { customRender: 'state' }
},
{
key: 'virtualmachineid',
title: this.$t('label.vm'),
dataIndex: 'virtualmachineid'
dataIndex: 'virtualmachineid',
slots: { customRender: 'virtualmachineid' }
},
{
key: 'associatednetworkname',
title: this.$t('label.network'),
dataIndex: 'associatednetworkname'
dataIndex: 'associatednetworkname',
slots: { customRender: 'associatednetworkname' }
},
{
key: 'actions',
title: ''
title: '',
slots: { customRender: 'action' }
}
],
showAcquireIp: false,
acquireLoading: false,
acquireIp: null,
changeSourceNat: false,
zoneExtNetProvider: ''
listPublicIpAddress: []
}
},
async created () {
await this.fetchZones()
created () {
this.fetchData()
},
watch: {
@ -300,26 +257,6 @@ export default {
}
},
inject: ['parentFetchData'],
computed: {
listApiParams () {
const params = {
zoneid: this.resource.zoneid,
domainid: this.resource.domainid,
account: this.resource.account,
forvirtualnetwork: true,
allocatedonly: false
}
if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
params.forprovider = true
}
return params
},
listApiParamsForAssociate () {
const params = this.listApiParams
params.state = 'Free,Reserved'
return params
}
},
methods: {
fetchData () {
const params = {
@ -341,30 +278,24 @@ export default {
} else {
params.associatednetworkid = this.resource.id
}
if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
params.forprovider = true
}
this.fetchLoading = true
getAPI('listPublicIpAddresses', params).then(json => {
api('listPublicIpAddresses', params).then(json => {
this.totalIps = json.listpublicipaddressesresponse.count || 0
this.ips = json.listpublicipaddressesresponse.publicipaddress || []
}).finally(() => {
this.fetchLoading = false
})
},
fetchZones () {
fetchListPublicIpAddress () {
return new Promise((resolve, reject) => {
getAPI('listZones', {
id: this.resource.zoneid
}).then(json => {
this.zoneExtNetProvider = json?.listzonesresponse?.zone?.[0]?.provider || null
resolve(this.zoneExtNetProvider)
}).catch(reject)
})
},
fetchListPublicIpAddress (state) {
return new Promise((resolve, reject) => {
getAPI('listPublicIpAddresses', this.listApiParams).then(json => {
const params = {
zoneid: this.resource.zoneid,
domainid: this.resource.domainid,
account: this.resource.account,
forvirtualnetwork: true,
allocatedonly: false
}
api('listPublicIpAddresses', params).then(json => {
const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
resolve(listPublicIps)
}).catch(reject)
@ -381,50 +312,6 @@ export default {
return selection.indexOf(item.id) !== -1
}))
},
setSourceNatIp (ipaddress) {
if (this.settingsourcenat) return
if (this.$route.path.startsWith('/vpc')) {
this.updateVpc(ipaddress)
} else {
this.updateNetwork(ipaddress)
}
},
updateNetwork (ipaddress) {
const params = {}
params.sourcenatipaddress = this.sourceNatIp.ipaddress
params.id = this.resource.id
this.settingsourcenat = true
postAPI('updateNetwork', params).then(response => {
this.fetchData()
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.updatenetworkresponse.errortext || error.response.data.errorresponse.errortext,
duration: 0
})
}).finally(() => {
this.settingsourcenat = false
this.cancelChangeSourceNat()
})
},
updateVpc (ipaddress) {
const params = {}
params.sourcenatipaddress = this.sourceNatIp.ipaddress
params.id = this.resource.id
this.settingsourcenat = true
postAPI('updateVPC', params).then(response => {
this.fetchData()
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.updatevpcresponse.errortext || error.response.data.errorresponse.errortext,
duration: 0
})
}).finally(() => {
this.settingsourcenat = false
this.cancelChangeSourceNat()
})
},
resetSelection () {
this.setSelection([])
},
@ -462,7 +349,7 @@ export default {
params.ipaddress = this.acquireIp
this.acquireLoading = true
postAPI('associateIpAddress', params).then(response => {
api('associateIpAddress', params).then(response => {
this.$pollJob({
jobId: response.associateipaddressresponse.jobid,
successMessage: `${this.$t('message.success.acquire.ip')} ${this.$t('label.for')} ${this.resource.name}`,
@ -479,8 +366,8 @@ export default {
this.onCloseModal()
}).catch(error => {
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.associateipaddressresponse.errortext || error.response.data.errorresponse.errortext,
duration: 0
})
}).finally(() => {
@ -498,9 +385,9 @@ export default {
releaseIpAddresses (e) {
this.showConfirmationAction = false
this.selectedColumns.splice(0, 0, {
key: 'status',
dataIndex: 'status',
title: this.$t('label.operation.status'),
slots: { customRender: 'status' },
filters: [
{ text: 'In Progress', value: 'InProgress' },
{ text: 'Success', value: 'success' },
@ -516,7 +403,7 @@ export default {
},
releaseIpAddress (ip) {
this.fetchLoading = true
postAPI('disassociateIpAddress', {
api('disassociateIpAddress', {
id: ip.id
}).then(response => {
const jobId = response.disassociateipaddressresponse.jobid
@ -552,29 +439,38 @@ export default {
})
})
},
getVmRouteUsingType (record) {
switch (record.virtualmachinetype) {
case 'DomainRouter' : return '/router/'
case 'ConsoleProxy' :
case 'SecondaryStorageVm': return '/systemvm/'
default: return '/vm/'
}
},
async onShowAcquireIp () {
this.showAcquireIp = true
this.acquireLoading = true
this.listPublicIpAddress = []
try {
const listPublicIpAddress = await this.fetchListPublicIpAddress()
listPublicIpAddress.forEach(item => {
if (item.state === 'Free' || item.state === 'Reserved') {
this.listPublicIpAddress.push({
ipaddress: item.ipaddress,
state: item.state
})
}
})
this.listPublicIpAddress.sort(function (a, b) {
if (a.ipaddress < b.ipaddress) { return -1 }
if (a.ipaddress > b.ipaddress) { return 1 }
return 0
})
this.acquireIp = this.listPublicIpAddress && this.listPublicIpAddress.length > 0 ? this.listPublicIpAddress[0].ipaddress : null
this.acquireLoading = false
} catch (e) {
this.acquireLoading = false
this.$notifyError(e)
}
},
onCloseModal () {
this.showAcquireIp = false
},
closeModal () {
this.showConfirmationAction = false
},
showChangeSourceNat (ipaddress) {
this.changeSourceNat = true
this.sourceNatIp = ipaddress
},
cancelChangeSourceNat () {
this.changeSourceNat = false
}
}
}

View File

@ -166,6 +166,9 @@
:footer="null"
@cancel="showCreateNetworkModal = false">
<a-spin :spinning="modalLoading" v-ctrl-enter="handleAddNetworkSubmit">
<div v-if="!isNormalUserOrProject()">
<ownership-selection @fetch-owner="fetchOwnerOptions"/>
</div>
<a-form
layout="vertical"
:ref="formRef"
@ -370,11 +373,13 @@ import { mixinForm } from '@/utils/mixin'
import Status from '@/components/widgets/Status'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import { getNetmaskFromCidr } from '@/utils/network'
import OwnershipSelection from '@/views/compute/wizard/OwnershipSelection.vue'
export default {
name: 'VpcTiersTab',
mixins: [mixinForm],
components: {
OwnershipSelection,
Status,
TooltipLabel
},
@ -506,6 +511,9 @@ export default {
isObjectEmpty (obj) {
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
},
isNormalUserOrProject () {
return ['User'].includes(this.$store.getters.userInfo.roletype) || this.$store.getters.project?.id
},
initForm () {
this.formRef = ref()
this.form = reactive({})
@ -536,6 +544,23 @@ export default {
}
this.publicLBNetworkExists()
},
fetchOwnerOptions (OwnerOptions) {
this.owner = {}
if (OwnerOptions.selectedAccountType === this.$t('label.account')) {
if (!OwnerOptions.selectedAccount) {
this.owner = undefined
return
}
this.owner.account = OwnerOptions.selectedAccount
this.owner.domainid = OwnerOptions.selectedDomain
} else if (OwnerOptions.selectedAccountType === this.$t('label.project')) {
if (!OwnerOptions.selectedProject) {
this.owner = undefined
return
}
this.owner.projectid = OwnerOptions.selectedProject
}
},
fetchMtuForZone () {
getAPI('listZones', {
id: this.resource.zoneid
@ -748,8 +773,9 @@ export default {
this.showCreateNetworkModal = false
var params = {
vpcid: this.resource.id,
domainid: this.resource.domainid,
account: this.resource.account,
domainid: this.owner?.domainid ? this.owner.domainid : this.resource.domainid,
account: this.owner?.projectid ? null : (this.owner?.account ? this.owner.account : this.resource.account),
projectid: this.owner?.projectid ? this.owner.projectid : null,
networkOfferingId: values.networkOffering,
name: values.name,
displayText: values.name,