diff --git a/api/src/main/java/com/cloud/agent/api/to/NicTO.java b/api/src/main/java/com/cloud/agent/api/to/NicTO.java index ca95fcfd679..2ed7d9f9a20 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java @@ -33,6 +33,7 @@ public class NicTO extends NetworkTO { boolean dpdkEnabled; Integer mtu; Long networkId; + boolean enabled; String networkSegmentName; @@ -154,4 +155,12 @@ public class NicTO extends NetworkTO { public void setNetworkSegmentName(String networkSegmentName) { this.networkSegmentName = networkSegmentName; } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } } diff --git a/api/src/main/java/com/cloud/vm/Nic.java b/api/src/main/java/com/cloud/vm/Nic.java index afc44b8d39f..cc0b294205c 100644 --- a/api/src/main/java/com/cloud/vm/Nic.java +++ b/api/src/main/java/com/cloud/vm/Nic.java @@ -162,4 +162,6 @@ public interface Nic extends Identity, InternalIdentity { String getIPv6Address(); Integer getMtu(); + + boolean isEnabled(); } diff --git a/api/src/main/java/com/cloud/vm/NicProfile.java b/api/src/main/java/com/cloud/vm/NicProfile.java index a0c80ceb1bf..d3ed7b4b87d 100644 --- a/api/src/main/java/com/cloud/vm/NicProfile.java +++ b/api/src/main/java/com/cloud/vm/NicProfile.java @@ -52,6 +52,7 @@ public class NicProfile implements InternalIdentity, Serializable { boolean defaultNic; Integer networkRate; boolean isSecurityGroupEnabled; + boolean enabled; Integer orderIndex; @@ -87,6 +88,7 @@ public class NicProfile implements InternalIdentity, Serializable { broadcastType = network.getBroadcastDomainType(); trafficType = network.getTrafficType(); format = nic.getAddressFormat(); + enabled = nic.isEnabled(); iPv4Address = nic.getIPv4Address(); iPv4Netmask = nic.getIPv4Netmask(); @@ -414,6 +416,14 @@ public class NicProfile implements InternalIdentity, Serializable { this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + // // OTHER METHODS // diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index 4ad1ffb755b..67aa0534a5f 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -40,6 +40,7 @@ import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd; import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; @@ -152,6 +153,8 @@ public interface UserVmService { */ UserVm updateNicIpForVirtualMachine(UpdateVmNicIpCmd cmd); + UserVm updateVirtualMachineNic(UpdateVmNicCmd cmd); + UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException; /** diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 338c1e738df..b0738cf78e1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -343,6 +343,8 @@ public interface ResponseGenerator { UserVm findUserVmById(Long vmId); + UserVm findUserVmByNicId(Long nicId); + Volume findVolumeById(Long volumeId); Account findAccountByNameDomain(String accountName, Long domainId); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.java new file mode 100644 index 00000000000..363273a4670 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVmNicCmd.java @@ -0,0 +1,95 @@ +// 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. +package org.apache.cloudstack.api.command.user.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; + +import java.util.ArrayList; +import java.util.EnumSet; + +@APICommand(name = "updateVmNic", description = "Updates the specified VM NIC", responseObject = NicResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = { RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User }) +public class UpdateVmNicCmd extends BaseAsyncCmd { + + @ACL + @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "NIC ID") + private Long nicId; + + @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "If true, sets the NIC state to UP; otherwise, sets the NIC state to DOWN") + private Boolean enabled; + + public Long getNicId() { + return nicId; + } + + public Boolean isEnabled() { + return enabled; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_UPDATE; + } + + @Override + public String getEventDescription() { + return String.format("Updating NIC %s.", getResourceUuid(ApiConstants.NIC_ID)); + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmByNicId(nicId); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; + } + return vm.getAccountId(); + } + + @Override + public void execute() { + CallContext.current().setEventDetails(String.format("NIC ID: %s", getResourceUuid(ApiConstants.NIC_ID))); + + UserVm result = _userVmService.updateVirtualMachineNic(this); + + ArrayList dc = new ArrayList<>(); + dc.add(ApiConstants.VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse(ResponseObject.ResponseView.Restricted, "virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update NIC from VM."); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java index f992514b8db..92f25e370fb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NicResponse.java @@ -146,6 +146,10 @@ public class NicResponse extends BaseResponse { @Param(description = "Public IP address associated with this NIC via Static NAT rule") private String publicIp; + @SerializedName(ApiConstants.ENABLED) + @Param(description = "whether the NIC is enabled or not") + private Boolean isEnabled; + public void setVmId(String vmId) { this.vmId = vmId; } @@ -416,4 +420,12 @@ public class NicResponse extends BaseResponse { public void setPublicIp(String publicIp) { this.publicIp = publicIp; } + + public Boolean getEnabled() { + return isEnabled; + } + + public void setEnabled(Boolean enabled) { + isEnabled = enabled; + } } diff --git a/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java b/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java new file mode 100644 index 00000000000..0af6d7fa3a3 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UpdateVmNicAnswer.java @@ -0,0 +1,27 @@ +// 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. +// +package com.cloud.agent.api; + +public class UpdateVmNicAnswer extends Answer { + public UpdateVmNicAnswer() { + } + + public UpdateVmNicAnswer(UpdateVmNicCommand cmd, boolean success, String result) { + super(cmd, success, result); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.java b/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.java new file mode 100644 index 00000000000..a5c5faf32fe --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UpdateVmNicCommand.java @@ -0,0 +1,51 @@ +// 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. +// +package com.cloud.agent.api; + +public class UpdateVmNicCommand extends Command { + + String nicMacAddress; + String instanceName; + Boolean enabled; + + @Override + public boolean executeInSequence() { + return true; + } + + protected UpdateVmNicCommand() { + } + + public UpdateVmNicCommand(String nicMacAdderss, String instanceName, Boolean enabled) { + this.nicMacAddress = nicMacAdderss; + this.instanceName = instanceName; + this.enabled = enabled; + } + + public String getNicMacAddress() { + return nicMacAddress; + } + + public String getVmName() { + return instanceName; + } + + public Boolean isEnabled() { + return enabled; + } +} diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 70240454689..57dc1b7bf72 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -230,6 +230,8 @@ public interface VirtualMachineManager extends Manager { Boolean updateDefaultNicForVM(VirtualMachine vm, Nic nic, Nic defaultNic); + boolean updateVmNic(VirtualMachine vm, Nic nic, Boolean enabled) throws ResourceUnavailableException; + /** * @param vm * @param network diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index e8796fb0252..8fe4c19a7e1 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -153,6 +153,8 @@ import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.UnmanageInstanceCommand; import com.cloud.agent.api.UnregisterVMCommand; +import com.cloud.agent.api.UpdateVmNicAnswer; +import com.cloud.agent.api.UpdateVmNicCommand; import com.cloud.agent.api.VmDiskStatsEntry; import com.cloud.agent.api.VmNetworkStatsEntry; import com.cloud.agent.api.VmStatsEntry; @@ -6270,6 +6272,80 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac _jobMgr.marshallResultObject(result)); } + @Override + public boolean updateVmNic(VirtualMachine vm, Nic nic, Boolean enabled) { + Outcome outcome = updateVmNicThroughJobQueue(vm, nic, enabled); + + retrieveVmFromJobOutcome(outcome, vm.getUuid(), "updateVmNic"); + + try { + Object jobResult = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome); + if (jobResult instanceof Boolean) { + return BooleanUtils.isTrue((Boolean) jobResult); + } + } catch (ResourceUnavailableException | InsufficientCapacityException ex) { + throw new CloudRuntimeException(String.format("Exception while updating VM [%s] NIC. Check the logs for more information.", vm.getUuid())); + } + throw new CloudRuntimeException("Unexpected job execution result."); + } + + private boolean orchestrateUpdateVmNic(final VirtualMachine vm, final Nic nic, final Boolean enabled) throws ResourceUnavailableException { + if (vm.getState() == State.Running) { + try { + UpdateVmNicCommand updateVmNicCmd = new UpdateVmNicCommand(nic.getMacAddress(), vm.getName(), enabled); + Commands cmds = new Commands(Command.OnError.Stop); + cmds.addCommand("updatevmnic", updateVmNicCmd); + + _agentMgr.send(vm.getHostId(), cmds); + + UpdateVmNicAnswer updateVmNicAnswer = cmds.getAnswer(UpdateVmNicAnswer.class); + if (updateVmNicAnswer == null || !updateVmNicAnswer.getResult()) { + logger.warn("Unable to update VM %s NIC [{}].", vm.getName(), nic.getUuid()); + return false; + } + } catch (final OperationTimedoutException e) { + throw new AgentUnavailableException(String.format("Unable to update NIC %s for VM %s.", nic.getUuid(), vm.getUuid()), vm.getHostId(), e); + } + } + + NicVO nicVo = _nicsDao.findById(nic.getId()); + nicVo.setEnabled(enabled); + _nicsDao.persist(nicVo); + + return true; + } + + public Outcome updateVmNicThroughJobQueue(final VirtualMachine vm, final Nic nic, final Boolean isNicEnabled) { + Long vmId = vm.getId(); + String commandName = VmWorkUpdateNic.class.getName(); + Pair pendingWorkJob = retrievePendingWorkJob(vmId, commandName); + + VmWorkJobVO workJob = pendingWorkJob.first(); + + if (workJob == null) { + Pair newVmWorkJobAndInfo = createWorkJobAndWorkInfo(commandName, vmId); + + workJob = newVmWorkJobAndInfo.first(); + VmWorkUpdateNic workInfo = new VmWorkUpdateNic(newVmWorkJobAndInfo.second(), nic.getId(), isNicEnabled); + + setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); + } + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId()); + + return new VmJobVirtualMachineOutcome(workJob, vmId); + } + + @ReflectionUse + private Pair orchestrateUpdateVmNic(final VmWorkUpdateNic work) throws Exception { + VMInstanceVO vm = findVmById(work.getVmId()); + final NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId()); + if (nic == null) { + throw new CloudRuntimeException(String.format("Unable to find NIC with ID %s.", work.getNicId())); + } + final boolean result = orchestrateUpdateVmNic(vm, nic, work.isEnabled()); + return new Pair<>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(result)); + } + private Pair findClusterAndHostIdForVmFromVolumes(long vmId) { Long clusterId = null; Long hostId = null; diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java new file mode 100644 index 00000000000..1c63cf34a19 --- /dev/null +++ b/engine/orchestration/src/main/java/com/cloud/vm/VmWorkUpdateNic.java @@ -0,0 +1,38 @@ +// 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. +package com.cloud.vm; + +public class VmWorkUpdateNic extends VmWork { + private static final long serialVersionUID = -8957066627929113278L; + + Long nicId; + Boolean enabled; + + public VmWorkUpdateNic(VmWork vmWork, Long nicId, Boolean enabled) { + super(vmWork); + this.nicId = nicId; + this.enabled = enabled; + } + + public Long getNicId() { + return nicId; + } + + public Boolean isEnabled() { + return enabled; + } +} diff --git a/engine/schema/src/main/java/com/cloud/vm/NicVO.java b/engine/schema/src/main/java/com/cloud/vm/NicVO.java index 6c569e22dd9..65946b8d821 100644 --- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java @@ -131,6 +131,9 @@ public class NicVO implements Nic { @Column(name = "mtu") Integer mtu; + @Column(name = "enabled") + boolean enabled; + @Transient transient String nsxLogicalSwitchUuid; @@ -143,6 +146,7 @@ public class NicVO implements Nic { this.networkId = configurationId; this.state = State.Allocated; this.vmType = vmType; + this.enabled = true; } @Override @@ -397,6 +401,14 @@ public class NicVO implements Nic { this.nsxLogicalSwitchPortUuid = nsxLogicalSwitchPortUuid; } + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + @Override public int hashCode() { return new HashCodeBuilder(17, 31).append(id).toHashCode(); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql index d69b524b85d..4cb9eb7cb2c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql @@ -114,3 +114,6 @@ CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'deleteUserKey -- Add conserve mode for VPC offerings CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tinyint(1) unsigned NULL DEFAULT 0 COMMENT ''True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers'' '); + +--- Disable/enable NICs +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' '); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql index a12e02bcfdb..d5f17606cb4 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql @@ -76,6 +76,7 @@ select nics.broadcast_uri broadcast_uri, nics.isolation_uri isolation_uri, nics.mtu mtu, + nics.enabled is_nic_enabled, vpc.id vpc_id, vpc.uuid vpc_uuid, vpc.name vpc_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql index 94bc8640fd5..6f31fc17bce 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -141,6 +141,7 @@ SELECT `nics`.`mac_address` AS `mac_address`, `nics`.`broadcast_uri` AS `broadcast_uri`, `nics`.`isolation_uri` AS `isolation_uri`, + `nics`.`enabled` AS `is_nic_enabled`, `vpc`.`id` AS `vpc_id`, `vpc`.`uuid` AS `vpc_uuid`, `networks`.`uuid` AS `network_uuid`, diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index da60f6fd717..e19c3437ba5 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -275,6 +275,7 @@ public class BridgeVifDriver extends VifDriverBase { if (nic.getPxeDisable()) { intf.setPxeDisable(true); } + intf.setLinkStateUp(nic.isEnabled()); return intf; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 55bab118ad0..46cf1da461e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -4851,6 +4851,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } + public InterfaceDef getInterface(final Connect conn, final String vmName, final String macAddress) { + List interfaces = getInterfaces(conn, vmName); + return interfaces.stream().filter(interfaceDef -> interfaceDef.getMacAddress().equals(macAddress)) + .findFirst().orElseThrow(() -> new CloudRuntimeException(String.format("Unable to find NIC with MAC address %s.", macAddress))); + } + public List getDisks(final Connect conn, final String vmName) { final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); Domain dm = null; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUpdateVmNicCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUpdateVmNicCommandWrapper.java new file mode 100644 index 00000000000..85f00e1dbd4 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUpdateVmNicCommandWrapper.java @@ -0,0 +1,67 @@ +// +// 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. +// + +package com.cloud.hypervisor.kvm.resource.wrapper; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.UpdateVmNicAnswer; +import com.cloud.agent.api.UpdateVmNicCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + +@ResourceWrapper(handles = UpdateVmNicCommand.class) +public final class LibvirtUpdateVmNicCommandWrapper extends CommandWrapper { + + @Override + public Answer execute(UpdateVmNicCommand command, LibvirtComputingResource libvirtComputingResource) { + String nicMacAddress = command.getNicMacAddress(); + String vmName = command.getVmName(); + boolean isEnabled = command.isEnabled(); + + Domain vm = null; + try { + final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); + final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName); + vm = libvirtComputingResource.getDomain(conn, vmName); + + final InterfaceDef nic = libvirtComputingResource.getInterface(conn, vmName, nicMacAddress); + nic.setLinkStateUp(isEnabled); + vm.updateDeviceFlags(nic.toString(), Domain.DeviceModifyFlags.LIVE); + + return new UpdateVmNicAnswer(command, true, "success"); + } catch (final LibvirtException e) { + final String msg = String.format(" Update NIC failed due to %s.", e); + logger.warn(msg, e); + return new UpdateVmNicAnswer(command, false, msg); + } finally { + if (vm != null) { + try { + vm.free(); + } catch (final LibvirtException l) { + logger.trace("Ignoring libvirt error.", l); + } + } + } + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index cde87fd9384..b9629524007 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -7203,4 +7203,35 @@ public class LibvirtComputingResourceTest { libvirtComputingResourceSpy.defineDiskForDefaultPoolType(diskDef, volume, false, false, false, physicalDisk, DEV_ID, DISK_BUS_TYPE, DISK_BUS_TYPE_DATA, null); Mockito.verify(diskDef).defFileBasedDisk(PHYSICAL_DISK_PATH, DEV_ID, DISK_BUS_TYPE_DATA, DiskDef.DiskFmtType.QCOW2); } + + @Test + public void getInterfaceTestValidMacAddressReturnInterface() { + String macAddress = "a0:90:27:a9:9e:62"; + final String vmName = "Test"; + final InterfaceDef interfaceDef = Mockito.mock(InterfaceDef.class); + final List interfaces = new ArrayList<>(); + interfaces.add(interfaceDef); + + Mockito.doReturn(macAddress).when(interfaceDef).getMacAddress(); + Mockito.doReturn(interfaces).when(libvirtComputingResourceSpy).getInterfaces(Mockito.any(), Mockito.anyString()); + + InterfaceDef result = libvirtComputingResourceSpy.getInterface(connMock, vmName, macAddress); + + Assert.assertNotNull(result); + } + + @Test(expected = CloudRuntimeException.class) + public void getInterfaceTestInvalidMacAddressThrowCloudRuntimeException() { + String invalidMacAddress = "ea:57:5d:f1:64:05"; + String macAddress = "a0:90:27:a9:9e:62"; + final String vmName = "Test"; + final InterfaceDef interfaceDef = Mockito.mock(InterfaceDef.class); + final List interfaces = new ArrayList<>(); + interfaces.add(interfaceDef); + + Mockito.doReturn(macAddress).when(interfaceDef).getMacAddress(); + Mockito.doReturn(interfaces).when(libvirtComputingResourceSpy).getInterfaces(Mockito.any(), Mockito.anyString()); + + libvirtComputingResourceSpy.getInterface(connMock, vmName, invalidMacAddress); + } } diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 1d6d5d5c500..702b3afddf1 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -2230,6 +2230,10 @@ public class ApiDBUtils { return s_nicSecondaryIpDao.listByNicId(nicId); } + public static NicVO findNicById(long nicId) { + return s_nicDao.findById(nicId); + } + public static TemplateResponse newTemplateUpdateResponse(TemplateJoinVO vr) { return s_templateJoinDao.newUpdateResponse(vr); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 6d2f5d59cd3..bd09d1de48b 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -1929,6 +1929,12 @@ public class ApiResponseHelper implements ResponseGenerator { } + @Override + public UserVm findUserVmByNicId(Long nicId) { + NicVO nic = ApiDBUtils.findNicById(nicId); + return ApiDBUtils.findUserVmById(nic.getInstanceId()); + } + @Override public VolumeVO findVolumeById(Long volumeId) { return ApiDBUtils.findVolumeById(volumeId); @@ -4844,6 +4850,8 @@ public class ApiResponseHelper implements ResponseGenerator { VpcVO vpc = _entityMgr.findByUuidIncludingRemoved(VpcVO.class, userVm.getVpcUuid()); response.setVpcName(vpc.getName()); } + + response.setEnabled(result.isEnabled()); return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index 2c6c6481661..9bc409d455e 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -196,6 +196,7 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase secondaryIps = ApiDBUtils.findNicSecondaryIps(uvo.getNicId()); if (secondaryIps != null) { List ipList = new ArrayList(); diff --git a/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java index 485b8f0a6b9..db7f75b6f2b 100644 --- a/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/DomainRouterJoinVO.java @@ -274,6 +274,9 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti @Column(name = "mtu") private Integer mtu; + @Column(name = "is_nic_enabled") + private boolean isNicEnabled; + public DomainRouterJoinVO() { } @@ -577,4 +580,8 @@ public class DomainRouterJoinVO extends BaseViewVO implements ControlledViewEnti public Integer getMtu() { return mtu; } + + public boolean isNicEnabled() { + return isNicEnabled; + } } diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index eab34081d51..0b60d99adc2 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -345,6 +345,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "is_default_nic") private boolean isDefaultNic; + @Column(name = "is_nic_enabled") + private boolean isNicEnabled; + @Column(name = "ip_address") private String ipAddress; @@ -1089,4 +1092,8 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro public String getLeaseActionExecution() { return leaseActionExecution; } + + public boolean isNicEnabled() { + return isNicEnabled; + } } diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 97c453003a8..c7de3d472e3 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -205,6 +205,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis to.setIp6Dns1(profile.getIPv6Dns1()); to.setIp6Dns2(profile.getIPv6Dns2()); to.setNetworkId(profile.getNetworkId()); + to.setEnabled(profile.isEnabled()); NetworkVO network = networkDao.findById(profile.getNetworkId()); to.setNetworkUuid(network.getUuid()); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 15286eaaf54..dc28b5f98a4 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -567,6 +567,7 @@ import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.StopVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; @@ -4165,6 +4166,7 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen cmdList.add(StopVMCmd.class); cmdList.add(UpdateDefaultNicForVMCmd.class); cmdList.add(UpdateVMCmd.class); + cmdList.add(UpdateVmNicCmd.class); cmdList.add(UpgradeVMCmd.class); cmdList.add(CreateVMGroupCmd.class); cmdList.add(DeleteVMGroupCmd.class); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index f4ea7bd9311..4585c4b01f2 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -99,6 +99,7 @@ import org.apache.cloudstack.api.command.user.vm.SecurityGroupAction; import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd; import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; @@ -1899,6 +1900,54 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return vm; } + @Override + public UserVm updateVirtualMachineNic(UpdateVmNicCmd cmd) { + Long nicId = cmd.getNicId(); + Boolean isNicEnabled = cmd.isEnabled(); + Account caller = CallContext.current().getCallingAccount(); + + NicVO nic = _nicDao.findById(nicId); + if (nic == null) { + throw new InvalidParameterValueException("Unable to find the specified NIC."); + } + + UserVmVO vmInstance = _vmDao.findById(nic.getInstanceId()); + if (vmInstance == null) { + throw new InvalidParameterValueException("Unable to find a virtual machine associated with the specified NIC."); + } + + if (vmInstance.getHypervisorType() != HypervisorType.KVM) { + throw new InvalidParameterValueException("Updating the VM NIC is only supported by the KVM hypervisor."); + } + + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network == null) { + throw new InvalidParameterValueException("Unable to find NIC's network."); + } + + _accountMgr.checkAccess(caller, null, true, vmInstance); + + if (isNicEnabled == null) { + return vmInstance; + } + + boolean success = false; + try { + success = _itMgr.updateVmNic(vmInstance, nic, isNicEnabled); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException(String.format("Unable to update NIC %s of VM %s in network %s due to: %s.", nic, vmInstance, network.getUuid(), e.getMessage())); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException(String.format("Concurrent operations while updating NIC %s for VM %s: %s.", nic, vmInstance, e.getMessage())); + } + + if (!success) { + throw new CloudRuntimeException(String.format("Failed to update NIC %s of VM %s in network %s.", nic, vmInstance, network.getUuid())); + } + + logger.debug("Successfully updated NIC {} in network {} for VM {}.", nic, network.getUuid(), vmInstance); + return vmInstance; + } + private void updatePublicIpDnatVmIp(long vmId, long networkId, String oldIp, String newIp) { if (!_networkModel.areServicesSupportedInNetwork(networkId, Service.StaticNat)) { return; diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 4e6627f4640..1a38c1b0a06 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -77,6 +77,7 @@ import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd; import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd; import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.backup.BackupVO; @@ -245,6 +246,9 @@ public class UserVmManagerImplTest { @Mock private UpdateVMCmd updateVmCommand; + @Mock + private UpdateVmNicCmd updateVmNicCmd; + @Mock private AccountManager accountManager; @@ -272,6 +276,9 @@ public class UserVmManagerImplTest { @Mock private UserVO callerUser; + @Mock + private NicVO nicMock; + @Mock private VMTemplateDao templateDao; @@ -455,6 +462,8 @@ public class UserVmManagerImplTest { private static final long vmId = 1l; private static final long zoneId = 2L; private static final long accountId = 3L; + private static final long nicId = 4L; + private static final long networkId = 5L; private static final long serviceOfferingId = 10L; private static final long templateId = 11L; private static final long volumeId = 1L; @@ -4254,6 +4263,61 @@ public class UserVmManagerImplTest { verify(userVmManagerImpl, never()).addExtraConfig(any(UserVmVO.class), anyString()); } + @Test(expected = InvalidParameterValueException.class) + public void updateVirtualMachineNicTestInvalidNicThrowInvalidParameterValueException() { + Long invalidId = -1L; + Mockito.doReturn(invalidId).when(updateVmNicCmd).getNicId(); + + userVmManagerImpl.updateVirtualMachineNic(updateVmNicCmd); + } + + @Test(expected = InvalidParameterValueException.class) + public void updateVirtualMachineNicTestInvalidNicUserVmThrowInvalidParameterValueException() { + Mockito.doReturn(nicId).when(updateVmNicCmd).getNicId(); + Mockito.doReturn(nicMock).when(nicDao).findById(nicId); + + userVmManagerImpl.updateVirtualMachineNic(updateVmNicCmd); + } + + @Test(expected = InvalidParameterValueException.class) + public void updateVirtualMachineNicTestInvalidNicNetworkThrowInvalidParameterValueException() { + Mockito.doReturn(nicId).when(updateVmNicCmd).getNicId(); + Mockito.doReturn(true).when(updateVmNicCmd).isEnabled(); + Mockito.doReturn(nicMock).when(nicDao).findById(nicId); + Mockito.doReturn(vmId).when(nicMock).getInstanceId(); + Mockito.doReturn(userVmVoMock).when(userVmDao).findById(vmId); + + userVmManagerImpl.updateVirtualMachineNic(updateVmNicCmd); + } + + @Test(expected = CloudRuntimeException.class) + public void updateVirtualMachineNicTestInvalidNicNetworkThrowCloudRuntimeException() { + Mockito.doReturn(nicId).when(updateVmNicCmd).getNicId(); + Mockito.doReturn(true).when(updateVmNicCmd).isEnabled(); + Mockito.doReturn(nicMock).when(nicDao).findById(nicId); + Mockito.doReturn(vmId).when(nicMock).getInstanceId(); + Mockito.doReturn(userVmVoMock).when(userVmDao).findById(vmId); + + userVmManagerImpl.updateVirtualMachineNic(updateVmNicCmd); + } + + @Test + public void updateVirtualMachineNicTestValidInputReturnNicUserVm() throws ResourceUnavailableException { + Mockito.doReturn(nicId).when(updateVmNicCmd).getNicId(); + Mockito.doReturn(true).when(updateVmNicCmd).isEnabled(); + Mockito.doReturn(nicMock).when(nicDao).findById(nicId); + Mockito.doReturn(vmId).when(nicMock).getInstanceId(); + Mockito.doReturn(userVmVoMock).when(userVmDao).findById(vmId); + Mockito.doReturn(Hypervisor.HypervisorType.KVM).when(userVmVoMock).getHypervisorType(); + Mockito.doReturn(networkId).when(nicMock).getNetworkId(); + Mockito.doReturn(networkMock).when(_networkDao).findById(networkId); + Mockito.doReturn(true).when(virtualMachineManager).updateVmNic(Mockito.any(), Mockito.any(), Mockito.any()); + + UserVm result = userVmManagerImpl.updateVirtualMachineNic(updateVmNicCmd); + + Assert.assertNotNull(result); + } + @Test public void testTransitionExpungingToErrorVmInExpungingState() throws Exception { UserVmVO vm = mock(UserVmVO.class); diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index e41a04ff2e1..292f52d809b 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -174,6 +174,7 @@ known_categories = { 'removeIpFromNic': 'Nic', 'updateVmNicIp': 'Nic', 'listNics':'Nic', + 'updateVmNic': 'Nic', 'AffinityGroup': 'Affinity Group', 'ImageStore': 'Image Store', 'addImageStore': 'Image Store', diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index b36e8c1ef1a..6f3a623b809 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -993,6 +993,7 @@ "label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.autoscale.vmprofile": "Edit AutoScale Instance Profile", +"label.edit.nic": "Edit NIC", "label.edit.project.details": "Edit project details", "label.edit.project.role": "Edit project role", "label.edit.role": "Edit Role", @@ -3735,6 +3736,7 @@ "message.network.selection": "Choose one or more Networks to attach the Instance to.", "message.network.selection.new.network": "A new Network can also be created here.", "message.network.updateip": "Please confirm that you would like to change the IP address for this NIC on the Instance.", +"message.network.update.nic": "Please confirm that you would like to update this NIC.", "message.network.usage.info.data.points": "Each data point represents the difference in data traffic since the last data point.", "message.network.usage.info.sum.of.vnics": "The Network usage shown is made up of the sum of data traffic from all the vNICs in the Instance.", "message.nfs.mount.options.description": "Comma separated list of NFS mount options for KVM hosts. Supported options : vers=[3,4.0,4.1,4.2], nconnect=[1...16]", @@ -4018,13 +4020,14 @@ "message.success.delete.vgpu.profile": "Successfully deleted vGPU profile", "message.success.update.custom.action": "Successfully updated Custom Action", "message.success.update.extension": "Successfully updated Extension", -"message.success.update.sharedfs": "Successfully updated Shared FileSystem", "message.success.update.ipaddress": "Successfully updated IP address", "message.success.update.iprange": "Successfully updated IP range", "message.success.update.ipv4.subnet": "Successfully updated IPv4 subnet", "message.success.update.iso": "Successfully updated ISO", "message.success.update.kubeversion": "Successfully updated Kubernetes supported version", "message.success.update.network": "Successfully updated Network", +"message.success.update.nic": "Successfully updated NIC", +"message.success.update.sharedfs": "Successfully updated Shared FileSystem", "message.success.update.template": "Successfully updated Template", "message.success.update.user": "Successfully updated User", "message.success.update.vpn.customer.gateway": "Successfully updated VPN Customer Gateway", @@ -4074,6 +4077,7 @@ "message.two.fa.setup.page": "Two factor authentication (2FA) is an extra layer of security to your account.
Once setup is done, on every login you will be prompted to enter the 2FA code.
", "message.two.fa.view.setup.key": "Click here to view the setup key", "message.two.fa.view.static.pin": "Click here to view the static PIN", +"message.update.nic.processing": "Updating NIC...", "message.update.ipaddress.processing": "Updating IP Address...", "message.update.resource.count": "Please confirm that you want to update resource counts for this Account.", "message.update.resource.count.domain": "Please confirm that you want to update resource counts for this domain.", diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index 59123a5e45c..c7ca36c1278 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -623,6 +623,7 @@ "label.edit": "Editar", "label.edit.acl": "Editar lista ACL", "label.edit.acl.rule": "Editar regra ACL", +"label.edit.nic": "Editar NIC", "label.edit.project.details": "Editar detalhes do projeto", "label.edit.project.role": "Editar fun\u00e7\u00e3o do projeto", "label.edit.role": "Editar fun\u00e7\u00e3o", @@ -2298,6 +2299,7 @@ "message.network.removenic": "Por favor, confirme que deseja remover esta interface de rede, esta a\u00e7\u00e3o tamb\u00e9m ir\u00e1 remover a rede associada \u00e0 VM.", "message.network.secondaryip": "Por favor, confirme que voc\u00ea gostaria de adquirir um novo IP secund\u00e1rio para este NIC. \n NOTA: Voc\u00ea precisa configurar manualmente o IP secund\u00e1rio rec\u00e9m-adquirido dentro da m\u00e1quina virtual.", "message.network.updateip": "Por favor, confirme que voc\u00ea gostaria de mudar o endere\u00e7o IP da NIC em VM.", +"message.network.update.nic": "Por favor, confirme que voc\u00ea gostaria de atualizar esta NIC.", "message.network.usage.info.data.points": "Cada ponto no gr\u00e1fico representa a diferen\u00e7a de dados trafegados desde a \u00faltima coleta de estat\u00edstica realizada (o ponto anterior)", "message.network.usage.info.sum.of.vnics": "O uso de rede apresentado \u00e9 composto pela soma de dados trafegados por todas as vNICs da VM", "message.no.data.to.show.for.period": "Nenhum dado para mostrar no per\u00edodo selecionado.", @@ -2455,6 +2457,7 @@ "message.success.update.iprange": "Intervalo de IP atualizado com sucesso", "message.success.update.kubeversion": "Vers\u00e3o compat\u00edvel com Kubernetes atualizada com sucesso", "message.success.update.network": "Rede atualizada com sucesso", +"message.success.update.nic": "NIC atualizada com sucesso", "message.success.update.user": "Usu\u00e1rio atualizado com sucesso", "message.success.upgrade.kubernetes": "Cluster do Kubernetes atualizado com sucesso", "message.success.upload": "Carregado com sucesso", @@ -2471,6 +2474,7 @@ "message.template.iso": "Selecione o template ou ISO para continuar", "message.tooltip.reserved.system.netmask": "O prefixo de rede que define a subrede deste pod. utilize a nota\u00e7\u00e3o CIDR.", "message.traffic.type.to.basic.zone": "Tipo de tr\u00e1fego para a zona b\u00e1sica", +"message.update.nic.processing": "Atualizando NIC...", "message.update.ipaddress.processing": "Atualizando endere\u00e7o IP...", "message.update.resource.count": "Por favor confirme que voc\u00ea quer atualizar a contagem de recursos para esta conta.", "message.update.resource.count.domain": "Por favor, confirme que voc\u00ea deseja atualizar as contagens de recursos para este dom\u00ednio.", diff --git a/ui/src/views/network/NicsTab.vue b/ui/src/views/network/NicsTab.vue index 9c16865e0c6..4c99f4f1c51 100644 --- a/ui/src/views/network/NicsTab.vue +++ b/ui/src/views/network/NicsTab.vue @@ -54,6 +54,13 @@ icon="environment-outlined" :disabled="(!('addIpToNic' in $store.getters.apis) && !('addIpToNic' in $store.getters.apis))" @onClick="onAcquireSecondaryIPAddress(record)" /> +