New feature: VNF templates and appliances integration (#8022)

This commit is contained in:
Wei Zhou 2023-10-27 10:23:00 +02:00 committed by GitHub
parent a06f8a8763
commit bd52fa8a12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 9026 additions and 63 deletions

View File

@ -0,0 +1,112 @@
// 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.network;
import org.apache.commons.lang3.StringUtils;
public class VNF {
public enum AccessMethod {
SSH_WITH_PASSWORD("ssh-password"),
SSH_WITH_KEY("ssh-key"),
HTTP("http"),
HTTPS("https"),
CONSOLE("console");
String _method;
AccessMethod(String method) {
_method = method;
}
@Override
public String toString() {
return _method;
}
public static AccessMethod fromValue(String method) {
if (StringUtils.isBlank(method)) {
return null;
} else {
for (AccessMethod accessMethod : AccessMethod.values()) {
if (accessMethod.toString().equalsIgnoreCase(method)) {
return accessMethod;
}
}
}
return null;
}
}
public enum AccessDetail {
ACCESS_METHODS,
USERNAME,
PASSWORD,
SSH_USER,
SSH_PASSWORD,
SSH_PORT,
WEB_USER,
WEB_PASSWORD,
HTTP_PATH,
HTTP_PORT,
HTTPS_PATH,
HTTPS_PORT
}
public enum VnfDetail {
ICON,
VERSION,
VENDOR,
MAINTAINER
}
public static class VnfNic {
long deviceId;
String name;
boolean required;
boolean management;
String description;
public VnfNic(long deviceId, String nicName, boolean required, boolean management, String nicDescription) {
this.deviceId = deviceId;
this.name = nicName;
this.required = required;
this.management = management;
this.description = nicDescription;
}
public long getDeviceId() {
return deviceId;
}
public String getName() {
return name;
}
public boolean isRequired() {
return required;
}
public boolean isManagement() {
return management;
}
public String getDescription() {
return description;
}
}
}

View File

@ -30,6 +30,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit
public enum ResourceObjectType { public enum ResourceObjectType {
UserVm(true, true, true), UserVm(true, true, true),
Template(true, true, true), Template(true, true, true),
VnfTemplate(true, true, true),
ISO(true, false, true), ISO(true, false, true),
Volume(true, true), Volume(true, true),
Snapshot(true, false), Snapshot(true, false),

View File

@ -125,6 +125,7 @@ public class Storage {
BUILTIN, /* buildin template */ BUILTIN, /* buildin template */
PERHOST, /* every host has this template, don't need to install it in secondary storage */ PERHOST, /* every host has this template, don't need to install it in secondary storage */
USER, /* User supplied template/iso */ USER, /* User supplied template/iso */
VNF, /* VNFs (virtual network functions) template */
DATADISK, /* Template corresponding to a datadisk(non root disk) present in an OVA */ DATADISK, /* Template corresponding to a datadisk(non root disk) present in an OVA */
ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */ ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */
} }

View File

@ -436,6 +436,7 @@ public class ApiConstants {
public static final String TEMPLATE_ID = "templateid"; public static final String TEMPLATE_ID = "templateid";
public static final String TEMPLATE_IDS = "templateids"; public static final String TEMPLATE_IDS = "templateids";
public static final String TEMPLATE_NAME = "templatename"; public static final String TEMPLATE_NAME = "templatename";
public static final String TEMPLATE_TYPE = "templatetype";
public static final String TIMEOUT = "timeout"; public static final String TIMEOUT = "timeout";
public static final String TIMEZONE = "timezone"; public static final String TIMEZONE = "timezone";
public static final String TIMEZONEOFFSET = "timezoneoffset"; public static final String TIMEZONEOFFSET = "timezoneoffset";
@ -1013,7 +1014,6 @@ public class ApiConstants {
public static final String DEPLOY_AS_IS = "deployasis"; public static final String DEPLOY_AS_IS = "deployasis";
public static final String DEPLOY_AS_IS_DETAILS = "deployasisdetails"; public static final String DEPLOY_AS_IS_DETAILS = "deployasisdetails";
public static final String CROSS_ZONES = "crossZones"; public static final String CROSS_ZONES = "crossZones";
public static final String TEMPLATETYPE = "templatetype";
public static final String SOURCETEMPLATEID = "sourcetemplateid"; public static final String SOURCETEMPLATEID = "sourcetemplateid";
public static final String DYNAMIC_SCALING_ENABLED = "dynamicscalingenabled"; public static final String DYNAMIC_SCALING_ENABLED = "dynamicscalingenabled";
public static final String IOTHREADS_ENABLED = "iothreadsenabled"; public static final String IOTHREADS_ENABLED = "iothreadsenabled";
@ -1047,6 +1047,15 @@ public class ApiConstants {
public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid"; public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
public static final String HAS_RULES = "hasrules"; public static final String HAS_RULES = "hasrules";
public static final String MANAGEMENT = "management";
public static final String IS_VNF = "isvnf";
public static final String VNF_NICS = "vnfnics";
public static final String VNF_DETAILS = "vnfdetails";
public static final String CLEAN_UP_VNF_DETAILS = "cleanupvnfdetails";
public static final String CLEAN_UP_VNF_NICS = "cleanupvnfnics";
public static final String VNF_CONFIGURE_MANAGEMENT = "vnfconfiguremanagement";
public static final String VNF_CIDR_LIST = "vnfcidrlist";
/** /**
* This enum specifies IO Drivers, each option controls specific policies on I/O. * This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
@ -1092,7 +1101,7 @@ public class ApiConstants {
} }
public enum VMDetails { public enum VMDetails {
all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp; all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp, vnfnics;
} }
public enum DomainDetails { public enum DomainDetails {

View File

@ -43,6 +43,7 @@ import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService;
import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.storage.ImageStoreService; import org.apache.cloudstack.storage.ImageStoreService;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageService;
import org.apache.commons.collections.MapUtils; import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -213,6 +214,8 @@ public abstract class BaseCmd {
public ResourceIconManager resourceIconManager; public ResourceIconManager resourceIconManager;
@Inject @Inject
public Ipv6Service ipv6Service; public Ipv6Service ipv6Service;
@Inject
public VnfTemplateManager vnfTemplateManager;
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException; ResourceAllocationException, NetworkRuleConflictException;

View File

@ -0,0 +1,32 @@
// 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.admin.template;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.command.user.template.ListVnfTemplatesCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
import com.cloud.template.VirtualMachineTemplate;
@APICommand(name = "listVnfTemplates", description = "List all public, private, and privileged VNF templates.",
responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Full,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.19.0")
public class ListVnfTemplatesCmdByAdmin extends ListVnfTemplatesCmd implements AdminCmd {
}

View File

@ -18,9 +18,11 @@ package org.apache.cloudstack.api.command.admin.template;
import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.TemplateResponse;
@APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, @APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud.", responseObject = TemplateResponse.class, responseView = ResponseView.Full,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class RegisterTemplateCmdByAdmin extends RegisterTemplateCmd {} public class RegisterTemplateCmdByAdmin extends RegisterTemplateCmd implements AdminCmd {
}

View File

@ -0,0 +1,31 @@
// 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.admin.template;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
@APICommand(name = "registerVnfTemplate",
description = "Registers an existing VNF template into the CloudStack cloud. ",
responseObject = TemplateResponse.class, responseView = ResponseView.Full,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.19.0")
public class RegisterVnfTemplateCmdByAdmin extends RegisterVnfTemplateCmd implements AdminCmd {
}

View File

@ -24,4 +24,5 @@ import org.apache.cloudstack.api.response.TemplateResponse;
@APICommand(name = "updateTemplate", description = "Updates attributes of a template.", responseObject = TemplateResponse.class, responseView = ResponseView.Full, @APICommand(name = "updateTemplate", description = "Updates attributes of a template.", responseObject = TemplateResponse.class, responseView = ResponseView.Full,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class UpdateTemplateCmdByAdmin extends UpdateTemplateCmd implements AdminCmd {} public class UpdateTemplateCmdByAdmin extends UpdateTemplateCmd implements AdminCmd {
}

View File

@ -0,0 +1,31 @@
// 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.admin.template;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
@APICommand(name = "updateVnfTemplate",
description = "Updates a template to VNF template or attributes of a VNF template.",
responseObject = TemplateResponse.class, responseView = ResponseView.Full,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.19.0")
public class UpdateVnfTemplateCmdByAdmin extends UpdateVnfTemplateCmd implements AdminCmd {
}

View File

@ -0,0 +1,34 @@
// 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.admin.vm;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.command.admin.AdminCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.apache.cloudstack.api.response.UserVmResponse;
@APICommand(name = "deployVnfAppliance",
description = "Creates and automatically starts a VNF appliance based on a service offering, disk offering, and template.",
responseObject = UserVmResponse.class,
responseView = ResponseObject.ResponseView.Full,
entityType = {VirtualMachine.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true,
since = "4.19.0")
public class DeployVnfApplianceCmdByAdmin extends DeployVnfApplianceCmd implements AdminCmd {
}

View File

@ -0,0 +1,47 @@
// 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.template;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.response.SuccessResponse;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
@APICommand(name = "deleteVnfTemplate",
responseObject = SuccessResponse.class,
description = "Deletes a VNF template from the system. All virtual machines using the deleted template will not be affected.",
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
since = "4.19.0")
public class DeleteVnfTemplateCmd extends DeleteTemplateCmd {
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public long getEntityOwnerId() {
VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, getId());
if (template != null) {
return template.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -96,6 +96,15 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
description = "comma separated list of template details requested, value can be a list of [ all, min]") description = "comma separated list of template details requested, value can be a list of [ all, min]")
private List<String> viewDetails; private List<String> viewDetails;
@Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING,
description = "the type of the template", since = "4.19.0")
private String templateType;
@Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN,
description = "flag to list VNF templates or not; true if need to list VNF templates, false otherwise.",
since = "4.19.0")
private Boolean isVnf;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -151,6 +160,10 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
return parentTemplateId; return parentTemplateId;
} }
public String getTemplateType() {
return templateType;
}
public boolean listInReadyState() { public boolean listInReadyState() {
Account account = CallContext.current().getCallingAccount(); Account account = CallContext.current().getCallingAccount();
@ -175,6 +188,10 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
return showIcon != null ? showIcon : false; return showIcon != null ? showIcon : false;
} }
public Boolean getVnf() {
return isVnf;
}
@Override @Override
public String getCommandName() { public String getCommandName() {
return s_name; return s_name;

View File

@ -0,0 +1,33 @@
// 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.template;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
import com.cloud.template.VirtualMachineTemplate;
@APICommand(name = "listVnfTemplates", description = "List all public, private, and privileged VNF templates.",
responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
since = "4.19.0")
public class ListVnfTemplatesCmd extends ListTemplatesCmd implements UserCmd {
}

View File

@ -143,6 +143,7 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd {
description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory") description = "true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory")
protected Boolean isDynamicallyScalable; protected Boolean isDynamicallyScalable;
@Deprecated
@Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "true if the template type is routing i.e., if template is used to deploy router") @Parameter(name = ApiConstants.ROUTING, type = CommandType.BOOLEAN, description = "true if the template type is routing i.e., if template is used to deploy router")
protected Boolean isRoutingType; protected Boolean isRoutingType;
@ -168,6 +169,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd {
description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1")
protected Boolean deployAsIs; protected Boolean deployAsIs;
@Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING,
description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).",
since = "4.19.0")
private String templateType;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -285,6 +291,10 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd {
Boolean.TRUE.equals(deployAsIs); Boolean.TRUE.equals(deployAsIs);
} }
public String getTemplateType() {
return templateType;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -315,7 +325,7 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd {
VirtualMachineTemplate template = _templateService.registerTemplate(this); VirtualMachineTemplate template = _templateService.registerTemplate(this);
if (template != null) { if (template != null) {
ListResponse<TemplateResponse> response = new ListResponse<TemplateResponse>(); ListResponse<TemplateResponse> response = new ListResponse<>();
List<TemplateResponse> templateResponses = _responseGenerator.createTemplateResponses(getResponseView(), List<TemplateResponse> templateResponses = _responseGenerator.createTemplateResponses(getResponseView(),
template, getZoneIds(), false); template, getZoneIds(), false);
response.setResponses(templateResponses); response.setResponses(templateResponses);

View File

@ -0,0 +1,79 @@
// 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.template;
import java.util.List;
import java.util.Map;
import com.cloud.network.VNF;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.storage.template.VnfTemplateUtils;
@APICommand(name = "registerVnfTemplate",
description = "Registers an existing VNF template into the CloudStack cloud. ",
responseObject = TemplateResponse.class, responseView = ResponseView.Restricted,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
since = "4.19.0")
public class RegisterVnfTemplateCmd extends RegisterTemplateCmd implements UserCmd {
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.VNF_NICS,
type = CommandType.MAP,
description = "VNF nics in key/value pairs using format vnfnics[i].keyname=keyvalue. "
+ " Example: vnfnics[0].deviceid=0&&vnfnics[0].name=FirstNIC&&vnfnics[0].required=true"
+ "&&vnfnics[1].deviceid=1&&vnfnics[1].name=SecondNIC")
protected Map vnfNics;
@Parameter(name = ApiConstants.VNF_DETAILS,
type = CommandType.MAP,
description = "VNF details in key/value pairs using format vnfdetails[i].keyname=keyvalue. "
+ "Example: vnfdetails[0].vendor=xxx&&vnfdetails[0].version=2.0")
protected Map vnfDetails;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public List<VNF.VnfNic> getVnfNics() {
return VnfTemplateUtils.getVnfNicsList(this.vnfNics);
}
public Map<String, String> getVnfDetails() {
return convertDetailsToMap(vnfDetails);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
protected void validateParameters() {
super.validateParameters();
VnfTemplateUtils.validateApiCommandParams(this, null);
}
}

View File

@ -16,10 +16,11 @@
// under the License. // under the License.
package org.apache.cloudstack.api.command.user.template; package org.apache.cloudstack.api.command.user.template;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoCmd; import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoCmd;
import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.Parameter;
@ -41,7 +42,8 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use
//////////////// API parameters ///////////////////// //////////////// API parameters /////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@Parameter(name = "templatetype", type = CommandType.STRING, description = "the type of the template") @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING,
description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).")
private String templateType; private String templateType;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////

View File

@ -0,0 +1,87 @@
// 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.template;
import com.cloud.network.VNF;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.storage.template.VnfTemplateUtils;
import java.util.List;
import java.util.Map;
@APICommand(name = "updateVnfTemplate", description = "Updates a template to VNF template or attributes of a VNF template.",
responseObject = TemplateResponse.class, responseView = ResponseView.Restricted,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
since = "4.19.0")
public class UpdateVnfTemplateCmd extends UpdateTemplateCmd implements UserCmd {
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.VNF_NICS,
type = CommandType.MAP,
description = "VNF nics in key/value pairs using format vnfnics[i].keyname=keyvalue. "
+ " Example: vnfnics[0].deviceid=0&&vnfnics[0].name=FirstNIC&&vnfnics[0].required=true"
+ "&&vnfnics[1].deviceid=1&&vnfnics[1].name=SecondNIC")
protected Map vnfNics;
@Parameter(name = ApiConstants.VNF_DETAILS,
type = CommandType.MAP,
description = "VNF details in key/value pairs using format vnfdetails[i].keyname=keyvalue. "
+ "Example: vnfdetails[0].vendor=xxx&&vnfdetails[0].version=2.0")
protected Map vnfDetails;
@Parameter(name = ApiConstants.CLEAN_UP_VNF_DETAILS,
type = CommandType.BOOLEAN,
description = "optional boolean field, which indicates if VNF details will be cleaned up or not")
private Boolean cleanupVnfDetails = null;
@Parameter(name = ApiConstants.CLEAN_UP_VNF_NICS,
type = CommandType.BOOLEAN,
description = "optional boolean field, which indicates if VNF nics will be cleaned up or not")
private Boolean cleanupVnfNics = null;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public List<VNF.VnfNic> getVnfNics() {
return VnfTemplateUtils.getVnfNicsList(this.vnfNics);
}
public Map<String, String> getVnfDetails() {
return convertDetailsToMap(vnfDetails);
}
public boolean isCleanupVnfDetails(){
return cleanupVnfDetails == null ? false : cleanupVnfDetails.booleanValue();
}
public boolean isCleanupVnfNics(){
return cleanupVnfNics == null ? false : cleanupVnfNics.booleanValue();
}
}

View File

@ -0,0 +1,74 @@
// 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 com.cloud.exception.ResourceAllocationException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.storage.template.VnfTemplateUtils;
import org.apache.commons.collections.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@APICommand(name = "deployVnfAppliance",
description = "Creates and automatically starts a VNF appliance based on a service offering, disk offering, and template.",
responseObject = UserVmResponse.class,
responseView = ResponseObject.ResponseView.Restricted,
entityType = {VirtualMachine.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true,
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
since = "4.19.0")
public class DeployVnfApplianceCmd extends DeployVMCmd implements UserCmd {
@Parameter(name = ApiConstants.VNF_CONFIGURE_MANAGEMENT, type = CommandType.BOOLEAN, required = false,
description = "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise. " +
"Network rules are configured if management network is an isolated network or shared network with security groups.")
private Boolean vnfConfigureManagement;
@Parameter(name = ApiConstants.VNF_CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING,
description = "the CIDR list to forward traffic from to the VNF management interface. Multiple entries must be separated by a single comma character (,). The default value is 0.0.0.0/0.")
private List<String> vnfCidrlist;
public Boolean getVnfConfigureManagement() {
return vnfConfigureManagement != null && vnfConfigureManagement;
}
public List<String> getVnfCidrlist() {
if (CollectionUtils.isNotEmpty(vnfCidrlist)) {
return vnfCidrlist;
} else {
List<String> defaultCidrList = new ArrayList<String>();
defaultCidrList.add(NetUtils.ALL_IP4_CIDRS);
return defaultCidrList;
}
}
@Override
public void create() throws ResourceAllocationException {
VnfTemplateUtils.validateVnfCidrList(this.getVnfCidrlist());
super.create();
}
}

View File

@ -189,6 +189,10 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
return zoneId; return zoneId;
} }
@Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN,
description = "flag to list vms created from VNF templates (as known as VNF appliances) or not; true if need to list VNF appliances, false otherwise.",
since = "4.19.0")
private Boolean isVnf;
public Long getNetworkId() { public Long getNetworkId() {
return networkId; return networkId;
@ -266,6 +270,10 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
return accumulate; return accumulate;
} }
public Boolean getVnf() {
return isVnf;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////

View File

@ -39,7 +39,7 @@ public class ChildTemplateResponse extends BaseResponse {
@Param(description = "the size of the template") @Param(description = "the size of the template")
private Integer size; private Integer size;
@SerializedName("templatetype") @SerializedName(ApiConstants.TEMPLATE_TYPE)
@Param(description = "the type of the template") @Param(description = "the type of the template")
private String templateType; private String templateType;

View File

@ -138,6 +138,14 @@ public class NicResponse extends BaseResponse {
@Param(description = "MTU configured on the NIC", since="4.18.0") @Param(description = "MTU configured on the NIC", since="4.18.0")
private Integer mtu; private Integer mtu;
@SerializedName(ApiConstants.PUBLIC_IP_ID)
@Param(description = "public IP address id associated with this nic via Static nat rule")
private String publicIpId;
@SerializedName(ApiConstants.PUBLIC_IP)
@Param(description = "public IP address associated with this nic via Static nat rule")
private String publicIp;
public void setVmId(String vmId) { public void setVmId(String vmId) {
this.vmId = vmId; this.vmId = vmId;
} }
@ -400,4 +408,12 @@ public class NicResponse extends BaseResponse {
public String getVpcId() { public String getVpcId() {
return vpcId; return vpcId;
} }
public void setPublicIpId(String publicIpId) {
this.publicIpId = publicIpId;
}
public void setPublicIp(String publicIp) {
this.publicIp = publicIp;
}
} }

View File

@ -123,7 +123,7 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements
@Param(description = "the physical size of the template") @Param(description = "the physical size of the template")
private Long physicalSize; private Long physicalSize;
@SerializedName(ApiConstants.TEMPLATETYPE) @SerializedName(ApiConstants.TEMPLATE_TYPE)
@Param(description = "the type of the template") @Param(description = "the type of the template")
private String templateType; private String templateType;

View File

@ -16,9 +16,12 @@
// under the License. // under the License.
package org.apache.cloudstack.api.response; package org.apache.cloudstack.api.response;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
@ -130,6 +133,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
@Param(description = "the name of the template for the virtual machine") @Param(description = "the name of the template for the virtual machine")
private String templateName; private String templateName;
@SerializedName(ApiConstants.TEMPLATE_TYPE)
@Param(description = "the type of the template for the virtual machine", since = "4.19.0")
private String templateType;
@SerializedName("templatedisplaytext") @SerializedName("templatedisplaytext")
@Param(description = " an alternate display text of the template for the virtual machine") @Param(description = " an alternate display text of the template for the virtual machine")
private String templateDisplayText; private String templateDisplayText;
@ -360,6 +367,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
@SerializedName(ApiConstants.USER_DATA_DETAILS) @Param(description="list of variables and values for the variables declared in userdata", since = "4.18.0") @SerializedName(ApiConstants.USER_DATA_DETAILS) @Param(description="list of variables and values for the variables declared in userdata", since = "4.18.0")
private String userDataDetails; private String userDataDetails;
@SerializedName(ApiConstants.VNF_NICS)
@Param(description = "NICs of the VNF appliance", since = "4.19.0")
private List<VnfNicResponse> vnfNics;
@SerializedName(ApiConstants.VNF_DETAILS)
@Param(description = "VNF details", since = "4.19.0")
private Map<String, String> vnfDetails;
public UserVmResponse() { public UserVmResponse() {
securityGroupList = new LinkedHashSet<>(); securityGroupList = new LinkedHashSet<>();
nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId()))); nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId())));
@ -1045,4 +1060,49 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
this.userDataDetails = userDataDetails; this.userDataDetails = userDataDetails;
} }
public Long getBytesReceived() {
return bytesReceived;
}
public Long getBytesSent() {
return bytesSent;
}
public String getTemplateType() {
return templateType;
}
public void setTemplateType(String templateType) {
this.templateType = templateType;
}
public List<VnfNicResponse> getVnfNics() {
return vnfNics;
}
public void setVnfNics(List<VnfNicResponse> vnfNics) {
this.vnfNics = vnfNics;
}
public Map<String, String> getVnfDetails() {
return vnfDetails;
}
public void setVnfDetails(Map<String, String> vnfDetails) {
this.vnfDetails = vnfDetails;
}
public void addVnfNic(VnfNicResponse vnfNic) {
if (this.vnfNics == null) {
this.vnfNics = new ArrayList<>();
}
this.vnfNics.add(vnfNic);
}
public void addVnfDetail(String key, String value) {
if (this.vnfDetails == null) {
this.vnfDetails = new LinkedHashMap<>();
}
this.vnfDetails.put(key,value);
}
} }

View File

@ -0,0 +1,119 @@
// 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.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
@SuppressWarnings("unused")
public class VnfNicResponse {
@SerializedName(ApiConstants.DEVICE_ID)
@Param(description = "Device id of the NIC")
private long deviceId;
@SerializedName(ApiConstants.NAME)
@Param(description = "Name of the NIC")
private String name;
@SerializedName(ApiConstants.REQUIRED)
@Param(description = "True if the NIC is required. False if optional")
private Boolean required;
@SerializedName(ApiConstants.MANAGEMENT)
@Param(description = "True if the NIC is a management interface. False otherwise")
private Boolean management;
@SerializedName(ApiConstants.DESCRIPTION)
@Param(description = "Description of the NIC")
private String description;
@SerializedName(ApiConstants.NETWORK_ID)
@Param(description = "Network id of the NIC")
private String networkId;
@SerializedName(ApiConstants.NETWORK_NAME)
@Param(description = "Network name of the NIC")
private String networkName;
public void setDeviceId(long deviceId) {
this.deviceId = deviceId;
}
public void setName(String name) {
this.name = name;
}
public void setRequired(Boolean required) {
this.required = required;
}
public void setManagement(Boolean management) {
this.management = management;
}
public void setDescription(String description) {
this.description = description;
}
public void setNetworkId(String networkId) {
this.networkId = networkId;
}
public void setNetworkName(String networkName) {
this.networkName = networkName;
}
public VnfNicResponse() {
}
public VnfNicResponse(long deviceId, String name, Boolean required, Boolean management, String description) {
this.deviceId = deviceId;
this.name = name;
this.required = required;
this.management = management;
this.description = description;
}
public long getDeviceId() {
return deviceId;
}
public String getName() {
return name;
}
public Boolean isRequired() {
return required;
}
public Boolean isManagement() {
return management;
}
public String getDescription() {
return description;
}
public String getNetworkId() {
return networkId;
}
public String getNetworkName() {
return networkName;
}
}

View File

@ -0,0 +1,60 @@
// 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.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("unused")
public class VnfTemplateResponse extends TemplateResponse {
@SerializedName(ApiConstants.VNF_NICS)
@Param(description = "NICs of the VNF template")
private List<VnfNicResponse> vnfNics;
@SerializedName(ApiConstants.VNF_DETAILS)
@Param(description = "VNF details")
private Map<String, String> vnfDetails;
public void addVnfNic(VnfNicResponse vnfNic) {
if (this.vnfNics == null) {
this.vnfNics = new ArrayList<>();
}
this.vnfNics.add(vnfNic);
}
public void addVnfDetail(String key, String value) {
if (this.vnfDetails == null) {
this.vnfDetails = new LinkedHashMap<>();
}
this.vnfDetails.put(key,value);
}
public List<VnfNicResponse> getVnfNics() {
return vnfNics;
}
public Map<String, String> getVnfDetails() {
return vnfDetails;
}
}

View File

@ -0,0 +1,52 @@
// 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.storage.template;
import com.cloud.dc.DataCenter;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.security.SecurityGroup;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.apache.cloudstack.framework.config.ConfigKey;
import java.util.List;
public interface VnfTemplateManager {
ConfigKey<Boolean> VnfTemplateAndApplianceEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
"vnf.template.appliance.enabled",
"true",
"Indicates whether the creation of VNF templates and VNF appliances is enabled or not.",
false);
void persistVnfTemplate(long templateId, RegisterVnfTemplateCmd cmd);
void updateVnfTemplate(long templateId, UpdateVnfTemplateCmd cmd);
void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds);
SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, DeployVnfApplianceCmd cmd);
void createIsolatedNetworkRulesForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner,
UserVm vm, DeployVnfApplianceCmd cmd)
throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException;
}

View File

@ -0,0 +1,152 @@
// 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.storage.template;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.VNF;
import com.cloud.storage.Storage;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.utils.net.NetUtils;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.command.user.template.DeleteVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class VnfTemplateUtils {
private VnfTemplateUtils() {
}
public static List<VNF.VnfNic> getVnfNicsList(Map vnfNics) {
List<VNF.VnfNic> nicsList = new ArrayList<>();
if (MapUtils.isNotEmpty(vnfNics)) {
Collection nicsCollection = vnfNics.values();
Iterator iter = nicsCollection.iterator();
while (iter.hasNext()) {
HashMap<String, String> nicDetails = (HashMap<String, String>)iter.next();
String deviceIdString = nicDetails.get("deviceid");
String name = nicDetails.get("name");
String requiredString = nicDetails.get("required");
String managementString = nicDetails.get("management");
String description = nicDetails.get("description");
Integer deviceId = null;
if (StringUtils.isAnyBlank(name, deviceIdString)) {
throw new InvalidParameterValueException("VNF nic name and deviceid cannot be null");
}
try {
deviceId = Integer.parseInt(deviceIdString);
} catch (NumberFormatException e) {
throw new InvalidParameterValueException("Unable to parse VNF nic deviceId to Integer: " + deviceId);
}
boolean required = StringUtils.isBlank(requiredString) || Boolean.parseBoolean(requiredString);
boolean management = StringUtils.isBlank(managementString) || Boolean.parseBoolean(managementString);
nicsList.add(new VNF.VnfNic(deviceId, name, required, management, description));
}
Collections.sort(nicsList, Comparator.comparing(VNF.VnfNic::getDeviceId));
}
return nicsList;
}
public static void validateApiCommandParams(Map<String, String> vnfDetails, List<VNF.VnfNic> vnfNics, String templateType) {
if (templateType != null && !Storage.TemplateType.VNF.name().equals(templateType)) {
throw new InvalidParameterValueException("The template type must be VNF for VNF templates.");
}
if (vnfDetails != null) {
for (String vnfDetail : vnfDetails.keySet()) {
if (!EnumUtils.isValidEnumIgnoreCase(VNF.VnfDetail.class, vnfDetail) &&
!EnumUtils.isValidEnumIgnoreCase(VNF.AccessDetail.class, vnfDetail)) {
throw new InvalidParameterValueException(String.format("Invalid VNF detail found: %s. Valid values are %s and %s", vnfDetail,
Arrays.stream(VNF.AccessDetail.values()).map(method -> method.toString()).collect(Collectors.joining(", ")),
Arrays.stream(VNF.VnfDetail.values()).map(method -> method.toString()).collect(Collectors.joining(", "))));
}
if (vnfDetails.get(vnfDetail) == null) {
throw new InvalidParameterValueException("Empty value found for VNF detail: " + vnfDetail);
}
if (VNF.AccessDetail.ACCESS_METHODS.name().equalsIgnoreCase(vnfDetail)) {
String[] accessMethods = vnfDetails.get(vnfDetail).split(",");
for (String accessMethod : accessMethods) {
if (VNF.AccessMethod.fromValue(accessMethod.trim()) == null) {
throw new InvalidParameterValueException(String.format("Invalid VNF access method found: %s. Valid values are %s", accessMethod,
Arrays.stream(VNF.AccessMethod.values()).map(method -> method.toString()).sorted().collect(Collectors.joining(", "))));
}
}
}
}
}
validateVnfNics(vnfNics);
}
public static void validateVnfNics(List<VNF.VnfNic> nicsList) {
long deviceId = 0L;
boolean required = true;
for (VNF.VnfNic nic : nicsList) {
if (nic.getDeviceId() != deviceId) {
throw new InvalidParameterValueException(String.format("deviceid must be consecutive and start from 0. Nic deviceid should be %s but actual is %s.", deviceId, nic.getDeviceId()));
}
if (!required && nic.isRequired()) {
throw new InvalidParameterValueException(String.format("required cannot be true if a preceding nic is optional. Nic with deviceid %s should be required but actual is optional.", deviceId));
}
deviceId ++;
required = nic.isRequired();
}
}
public static void validateApiCommandParams(BaseCmd cmd, VirtualMachineTemplate template) {
if (cmd instanceof RegisterVnfTemplateCmd) {
RegisterVnfTemplateCmd registerCmd = (RegisterVnfTemplateCmd) cmd;
validateApiCommandParams(registerCmd.getVnfDetails(), registerCmd.getVnfNics(), registerCmd.getTemplateType());
} else if (cmd instanceof UpdateVnfTemplateCmd) {
UpdateVnfTemplateCmd updateCmd = (UpdateVnfTemplateCmd) cmd;
if (!Storage.TemplateType.VNF.equals(template.getTemplateType())) {
throw new InvalidParameterValueException(String.format("Cannot update as template %s is not a VNF template. The template type is %s.", updateCmd.getId(), template.getTemplateType()));
}
validateApiCommandParams(updateCmd.getVnfDetails(), updateCmd.getVnfNics(), updateCmd.getTemplateType());
} else if (cmd instanceof DeleteVnfTemplateCmd) {
if (!Storage.TemplateType.VNF.equals(template.getTemplateType())) {
DeleteVnfTemplateCmd deleteCmd = (DeleteVnfTemplateCmd) cmd;
throw new InvalidParameterValueException(String.format("Cannot delete as Template %s is not a VNF template. The template type is %s.", deleteCmd.getId(), template.getTemplateType()));
}
}
}
public static void validateVnfCidrList(List<String> cidrList) {
if (CollectionUtils.isEmpty(cidrList)) {
return;
}
for (String cidr : cidrList) {
if (!NetUtils.isValidIp4Cidr(cidr)) {
throw new InvalidParameterValueException(String.format("Invalid cidr for VNF appliance: %s", cidr));
}
}
}
}

View File

@ -0,0 +1,58 @@
// 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.network;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class VNFTest {
static long deviceId = 0L;
static String deviceName = "eth0";
static boolean required = true;
static boolean management = false;
static String description = "description of vnf nic";
@Before
public void setUp() {
}
@Test
public void testAccessMethods() {
Assert.assertEquals(VNF.AccessMethod.CONSOLE, VNF.AccessMethod.fromValue("console"));
Assert.assertEquals(VNF.AccessMethod.HTTP, VNF.AccessMethod.fromValue("http"));
Assert.assertEquals(VNF.AccessMethod.HTTPS, VNF.AccessMethod.fromValue("https"));
Assert.assertEquals(VNF.AccessMethod.SSH_WITH_KEY, VNF.AccessMethod.fromValue("ssh-key"));
Assert.assertEquals(VNF.AccessMethod.SSH_WITH_PASSWORD, VNF.AccessMethod.fromValue("ssh-password"));
}
@Test
public void testVnfNic() {
VNF.VnfNic vnfNic = new VNF.VnfNic(deviceId, deviceName, required, management, description);
Assert.assertEquals(deviceId, vnfNic.getDeviceId());
Assert.assertEquals(deviceName, vnfNic.getName());
Assert.assertEquals(required, vnfNic.isRequired());
Assert.assertEquals(management, vnfNic.isManagement());
Assert.assertEquals(description, vnfNic.getDescription());
}
}

View File

@ -0,0 +1,54 @@
// 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.response;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public final class VnfNicResponseTest {
static long deviceId = 0L;
static String deviceName = "eth0";
static boolean required = true;
static boolean management = false;
static String description = "description of vnf nic";
static String networkUuid = "networkuuid";
static String networkName = "networkname";
@Test
public void testNewVnfNicResponse() {
final VnfNicResponse response = new VnfNicResponse(deviceId, deviceName, required, management, description);
Assert.assertEquals(deviceId, response.getDeviceId());
Assert.assertEquals(deviceName, response.getName());
Assert.assertEquals(required, response.isRequired());
Assert.assertEquals(management, response.isManagement());
Assert.assertEquals(description, response.getDescription());
}
@Test
public void testSetVnfNicResponse() {
final VnfNicResponse response = new VnfNicResponse();
response.setNetworkId(networkUuid);
response.setNetworkName(networkName);
Assert.assertEquals(networkUuid, response.getNetworkId());
Assert.assertEquals(networkName, response.getNetworkName());
}
}

View File

@ -0,0 +1,47 @@
// 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.response;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public final class VnfTemplateResponseTest {
@Test
public void testAddVnfNicToResponse() {
final VnfTemplateResponse response = new VnfTemplateResponse();
response.addVnfNic(new VnfNicResponse());
response.addVnfNic(new VnfNicResponse());
Assert.assertEquals(2, response.getVnfNics().size());
}
@Test
public void testAddVnfDetailToResponse() {
final VnfTemplateResponse response = new VnfTemplateResponse();
response.addVnfDetail("key1", "value1");
response.addVnfDetail("key2", "value2");
response.addVnfDetail("key3", "value3");
Assert.assertEquals(3, response.getVnfDetails().size());
}
}

View File

@ -0,0 +1,261 @@
// 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.storage.template;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.VNF.VnfNic;
import com.cloud.storage.Storage;
import com.cloud.template.VirtualMachineTemplate;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class VnfTemplateUtilsTest {
@Test
public void testGetVnfNicsListAllGood() {
final Map<String, Object> vnfNics = new HashMap<>();
vnfNics.put("0", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "1"),
Map.entry("name", "eth1"),
Map.entry("required", "true"),
Map.entry("description", "The second NIC of VNF appliance")
)));
vnfNics.put("1", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "2"),
Map.entry("name", "eth2"),
Map.entry("required", "false"),
Map.entry("description", "The third NIC of VNF appliance")
)));
vnfNics.put("2", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "0"),
Map.entry("name", "eth0"),
Map.entry("description", "The first NIC of VNF appliance")
)));
Map<String, Object> vnfNicsMock = Mockito.mock(Map.class);
Mockito.when(vnfNicsMock.values()).thenReturn(vnfNics.values());
List<VnfNic> nicsList = VnfTemplateUtils.getVnfNicsList(vnfNicsMock);
Mockito.verify(vnfNicsMock).values();
Assert.assertEquals(3, nicsList.size());
Assert.assertEquals(0, nicsList.get(0).getDeviceId());
Assert.assertEquals("eth0", nicsList.get(0).getName());
Assert.assertTrue(nicsList.get(0).isRequired());
Assert.assertEquals(1, nicsList.get(1).getDeviceId());
Assert.assertEquals("eth1", nicsList.get(1).getName());
Assert.assertTrue(nicsList.get(1).isRequired());
Assert.assertEquals(2, nicsList.get(2).getDeviceId());
Assert.assertEquals("eth2", nicsList.get(2).getName());
Assert.assertFalse(nicsList.get(2).isRequired());
}
@Test(expected = InvalidParameterValueException.class)
public void testGetVnfNicsListWithEmptyName() {
final Map<String, Object> vnfNics = new HashMap<>();
vnfNics.put("0", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "1"),
Map.entry("required", "true"),
Map.entry("description", "The second NIC of VNF appliance")
)));
Map<String, Object> vnfNicsMock = Mockito.mock(Map.class);
Mockito.when(vnfNicsMock.values()).thenReturn(vnfNics.values());
List<VnfNic> nicsList = VnfTemplateUtils.getVnfNicsList(vnfNicsMock);
}
@Test(expected = InvalidParameterValueException.class)
public void testGetVnfNicsListWithEmptyDeviceId() {
final Map<String, Object> vnfNics = new HashMap<>();
vnfNics.put("0", new HashMap<>(Map.ofEntries(
Map.entry("name", "eth1"),
Map.entry("required", "true"),
Map.entry("description", "The second NIC of VNF appliance")
)));
Map<String, Object> vnfNicsMock = Mockito.mock(Map.class);
Mockito.when(vnfNicsMock.values()).thenReturn(vnfNics.values());
List<VnfNic> nicsList = VnfTemplateUtils.getVnfNicsList(vnfNicsMock);
}
@Test(expected = InvalidParameterValueException.class)
public void testGetVnfNicsListWithInvalidDeviceId() {
final Map<String, Object> vnfNics = new HashMap<>();
vnfNics.put("0", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "invalid"),
Map.entry("name", "eth1"),
Map.entry("required", "true"),
Map.entry("description", "The second NIC of VNF appliance")
)));
Map<String, Object> vnfNicsMock = Mockito.mock(Map.class);
Mockito.when(vnfNicsMock.values()).thenReturn(vnfNics.values());
List<VnfNic> nicsList = VnfTemplateUtils.getVnfNicsList(vnfNicsMock);
}
@Test
public void testValidateVnfNicsAllGood() {
VnfNic nic1 = Mockito.mock(VnfNic.class);
Mockito.when(nic1.getDeviceId()).thenReturn(0L);
Mockito.when(nic1.isRequired()).thenReturn(true);
VnfNic nic2 = Mockito.mock(VnfNic.class);
Mockito.when(nic2.getDeviceId()).thenReturn(1L);
Mockito.when(nic2.isRequired()).thenReturn(true);
VnfNic nic3 = Mockito.mock(VnfNic.class);
Mockito.when(nic3.getDeviceId()).thenReturn(2L);
Mockito.when(nic3.isRequired()).thenReturn(false);
List<VnfNic> nicsList = Arrays.asList(nic1, nic2, nic3);
VnfTemplateUtils.validateVnfNics(nicsList);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfNicsStartWithNonzero() {
VnfNic nic1 = Mockito.mock(VnfNic.class);
Mockito.when(nic1.getDeviceId()).thenReturn(1L);
VnfNic nic2 = Mockito.mock(VnfNic.class);
VnfNic nic3 = Mockito.mock(VnfNic.class);
List<VnfNic> nicsList = Arrays.asList(nic1, nic2, nic3);
VnfTemplateUtils.validateVnfNics(nicsList);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfNicsWithNonConstantDeviceIds() {
VnfNic nic1 = Mockito.mock(VnfNic.class);
Mockito.when(nic1.getDeviceId()).thenReturn(0L);
Mockito.when(nic1.isRequired()).thenReturn(true);
VnfNic nic2 = Mockito.mock(VnfNic.class);
Mockito.when(nic2.getDeviceId()).thenReturn(2L);
VnfNic nic3 = Mockito.mock(VnfNic.class);
List<VnfNic> nicsList = Arrays.asList(nic1, nic2, nic3);
VnfTemplateUtils.validateVnfNics(nicsList);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfNicsWithInvalidRequired() {
VnfNic nic1 = Mockito.mock(VnfNic.class);
Mockito.when(nic1.getDeviceId()).thenReturn(0L);
Mockito.when(nic1.isRequired()).thenReturn(true);
VnfNic nic2 = Mockito.mock(VnfNic.class);
Mockito.when(nic2.getDeviceId()).thenReturn(1L);
Mockito.when(nic2.isRequired()).thenReturn(false);
VnfNic nic3 = Mockito.mock(VnfNic.class);
Mockito.when(nic3.getDeviceId()).thenReturn(2L);
Mockito.when(nic3.isRequired()).thenReturn(true);
List<VnfNic> nicsList = Arrays.asList(nic1, nic2, nic3);
VnfTemplateUtils.validateVnfNics(nicsList);
}
@Test
public void testValidateApiCommandParamsAllGood() {
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
RegisterVnfTemplateCmd cmd = Mockito.mock(RegisterVnfTemplateCmd.class);
Map<String, String> vnfDetails = Mockito.spy(new HashMap<>());
vnfDetails.put("username", "admin");
vnfDetails.put("password", "password");
vnfDetails.put("version", "4.19.0");
vnfDetails.put("vendor", "cloudstack");
Mockito.when(cmd.getVnfDetails()).thenReturn(vnfDetails);
VnfTemplateUtils.validateApiCommandParams(cmd, template);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateApiCommandParamsInvalidAccessMethods() {
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateType()).thenReturn(Storage.TemplateType.VNF);
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
Map<String, String> vnfDetails = Mockito.spy(new HashMap<>());
vnfDetails.put("access_methods", "invalid");
Mockito.when(cmd.getVnfDetails()).thenReturn(vnfDetails);
VnfTemplateUtils.validateApiCommandParams(cmd, template);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateApiCommandParamsInvalidAccessDetails() {
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateType()).thenReturn(Storage.TemplateType.VNF);
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
Map<String, String> vnfDetails = Mockito.spy(new HashMap<>());
vnfDetails.put("invalid", "value");
Mockito.when(cmd.getVnfDetails()).thenReturn(vnfDetails);
VnfTemplateUtils.validateApiCommandParams(cmd, template);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateApiCommandParamsInvalidTemplateType() {
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateType()).thenReturn(Storage.TemplateType.USER);
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
VnfTemplateUtils.validateApiCommandParams(cmd, template);
}
@Test
public void testValidateVnfCidrList() {
List<String> cidrList = new ArrayList<>();
cidrList.add("10.10.10.0/24");
VnfTemplateUtils.validateVnfCidrList(cidrList);
}
@Test
public void testValidateVnfCidrListWithEmptyList() {
List<String> cidrList = new ArrayList<>();
VnfTemplateUtils.validateVnfCidrList(cidrList);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfCidrListwithInvalidList() {
List<String> cidrList = new ArrayList<>();
cidrList.add("10.10.10.0/24");
cidrList.add("10.10.10.0/33");
VnfTemplateUtils.validateVnfCidrList(cidrList);
}
}

View File

@ -18,6 +18,7 @@ package com.cloud.template;
import java.util.List; import java.util.List;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey;
@ -29,6 +30,7 @@ import com.cloud.deploy.DeployDestination;
import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.StorageUnavailableException; import com.cloud.exception.StorageUnavailableException;
import com.cloud.storage.DataStoreRole; import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
@ -133,5 +135,7 @@ public interface TemplateManager {
public static final String MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT = "Message.RegisterPublicTemplate.Event"; public static final String MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT = "Message.RegisterPublicTemplate.Event";
public static final String MESSAGE_RESET_TEMPLATE_PERMISSION_EVENT = "Message.ResetTemplatePermission.Event"; public static final String MESSAGE_RESET_TEMPLATE_PERMISSION_EVENT = "Message.ResetTemplatePermission.Event";
TemplateType validateTemplateType(BaseCmd cmd, boolean isAdmin, boolean isCrossZones);
List<DatadiskTO> getTemplateDisksOnImageStore(Long templateId, DataStoreRole role, String configurationId); List<DatadiskTO> getTemplateDisksOnImageStore(Long templateId, DataStoreRole role, String configurationId);
} }

View File

@ -0,0 +1,101 @@
// 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.storage;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import org.apache.cloudstack.api.ResourceDetail;
@Entity
@Table(name = "vnf_template_details")
public class VnfTemplateDetailVO implements ResourceDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "template_id")
private long resourceId;
@Column(name = "name")
private String name;
@Lob
@Column(name = "value", length = 65535)
private String value;
@Column(name = "display")
private boolean display = true;
public VnfTemplateDetailVO() {
}
public VnfTemplateDetailVO(long templateId, String name, String value, boolean display) {
this.resourceId = templateId;
this.name = name;
this.value = value;
this.display = display;
}
@Override
public long getId() {
return id;
}
@Override
public long getResourceId() {
return resourceId;
}
@Override
public String getName() {
return name;
}
@Override
public String getValue() {
return value;
}
@Override
public boolean isDisplay() {
return display;
}
public void setId(long id) {
this.id = id;
}
public void setResourceId(long resourceId) {
this.resourceId = resourceId;
}
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,101 @@
// 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.storage;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "vnf_template_nics")
public class VnfTemplateNicVO implements InternalIdentity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "template_id")
private long templateId;
@Column(name = "device_id")
private long deviceId;
@Column(name = "device_name")
private String deviceName;
@Column(name = "required")
private boolean required = true;
@Column(name = "management")
private boolean management = true;
@Column(name = "description")
private String description;
public VnfTemplateNicVO() {
}
public VnfTemplateNicVO(long templateId, long deviceId, String deviceName, boolean required, boolean management, String description) {
this.templateId = templateId;
this.deviceId = deviceId;
this.deviceName = deviceName;
this.required = required;
this.management = management;
this.description = description;
}
@Override
public String toString() {
return String.format("Template %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "templateId", "deviceId", "required"));
}
@Override
public long getId() {
return id;
}
public long getTemplateId() {
return templateId;
}
public long getDeviceId() {
return deviceId;
}
public String getDeviceName() {
return deviceName;
}
public boolean isRequired() {
return required;
}
public boolean isManagement() {
return management;
}
public String getDescription() {
return description;
}
}

View File

@ -0,0 +1,26 @@
// 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.storage.dao;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.resourcedetail.ResourceDetailsDao;
public interface VnfTemplateDetailsDao extends GenericDao<VnfTemplateDetailVO, Long>, ResourceDetailsDao<VnfTemplateDetailVO> {
}

View File

@ -0,0 +1,31 @@
// 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.storage.dao;
import com.cloud.storage.VnfTemplateDetailVO;
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
import org.springframework.stereotype.Component;
@Component
public class VnfTemplateDetailsDaoImpl extends ResourceDetailsDaoBase<VnfTemplateDetailVO> implements VnfTemplateDetailsDao {
@Override
public void addDetail(long resourceId, String key, String value, boolean display) {
super.addDetail(new VnfTemplateDetailVO(resourceId, key, value, display));
}
}

View File

@ -0,0 +1,29 @@
// 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.storage.dao;
import java.util.List;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.utils.db.GenericDao;
public interface VnfTemplateNicDao extends GenericDao<VnfTemplateNicVO, Long> {
List<VnfTemplateNicVO> listByTemplateId(long templateId);
void deleteByTemplateId(long templateId);
}

View File

@ -0,0 +1,53 @@
// 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.storage.dao;
import java.util.List;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
public class VnfTemplateNicDaoImpl extends GenericDaoBase<VnfTemplateNicVO, Long> implements VnfTemplateNicDao {
protected SearchBuilder<VnfTemplateNicVO> TemplateSearch;
public VnfTemplateNicDaoImpl() {
TemplateSearch = createSearchBuilder();
TemplateSearch.and("templateId", TemplateSearch.entity().getTemplateId(), SearchCriteria.Op.EQ);
TemplateSearch.done();
}
@Override
public List<VnfTemplateNicVO> listByTemplateId(long templateId) {
SearchCriteria<VnfTemplateNicVO> sc = TemplateSearch.create();
sc.setParameters("templateId", templateId);
return listBy(sc);
}
@Override
public void deleteByTemplateId(long templateId) {
SearchCriteria<VnfTemplateNicVO> sc = TemplateSearch.create();
sc.setParameters("templateId", templateId);
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
remove(sc);
txn.commit();
}
}

View File

@ -333,6 +333,8 @@ public class NicVO implements Nic {
.append("-") .append("-")
.append(instanceId) .append(instanceId)
.append("-") .append("-")
.append(deviceId)
.append("-")
.append(reservationId) .append(reservationId)
.append("-") .append("-")
.append(iPv4Address) .append(iPv4Address)

View File

@ -278,6 +278,8 @@
<bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" /> <bean id="PassphraseDaoImpl" class="org.apache.cloudstack.secret.dao.PassphraseDaoImpl" />
<bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" /> <bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" />
<bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" /> <bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" />
<bean id="vnfTemplateDetailsDaoImpl" class="com.cloud.storage.dao.VnfTemplateDetailsDaoImpl" />
<bean id="vnfTemplateNicDaoImpl" class="com.cloud.storage.dao.VnfTemplateNicDaoImpl" />
<bean id="ClusterDrsPlanDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanDaoImpl" /> <bean id="ClusterDrsPlanDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanDaoImpl" />
<bean id="ClusterDrsPlanDetailsDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanMigrationDaoImpl" /> <bean id="ClusterDrsPlanDetailsDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanMigrationDaoImpl" />
</beans> </beans>

View File

@ -184,6 +184,230 @@ ALTER TABLE `cloud`.`kubernetes_cluster` MODIFY COLUMN `kubernetes_version_id` b
-- Set removed state for all removed accounts -- Set removed state for all removed accounts
UPDATE `cloud`.`account` SET state='removed' WHERE `removed` IS NOT NULL; UPDATE `cloud`.`account` SET state='removed' WHERE `removed` IS NOT NULL;
-- New tables for VNF
CREATE TABLE IF NOT EXISTS `cloud`.`vnf_template_nics` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`template_id` bigint unsigned NOT NULL COMMENT 'id of the VNF template',
`device_id` bigint unsigned NOT NULL COMMENT 'Device id of the NIC when plugged into the VNF appliances',
`device_name` varchar(1024) NOT NULL COMMENT 'Name of the NIC',
`required` tinyint NOT NULL DEFAULT '1' COMMENT 'True if the NIC is required. False if optional',
`management` tinyint NOT NULL DEFAULT '1' COMMENT 'True if the NIC is a management interface',
`description` varchar(1024) COMMENT 'Description of the NIC',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_template_id_device_id` (`template_id`, `device_id`),
KEY `fk_vnf_template_nics__template_id` (`template_id`),
CONSTRAINT `fk_vnf_template_nics__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `cloud`.`vnf_template_details` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`template_id` bigint unsigned NOT NULL COMMENT 'id of the VNF template',
`name` varchar(255) NOT NULL,
`value` varchar(1024) NOT NULL,
`display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user',
PRIMARY KEY (`id`),
KEY `fk_vnf_template_details__template_id` (`template_id`),
CONSTRAINT `fk_vnf_template_details__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
CREATE
VIEW `user_vm_view` AS
SELECT
`vm_instance`.`id` AS `id`,
`vm_instance`.`name` AS `name`,
`user_vm`.`display_name` AS `display_name`,
`user_vm`.`user_data` AS `user_data`,
`account`.`id` AS `account_id`,
`account`.`uuid` AS `account_uuid`,
`account`.`account_name` AS `account_name`,
`account`.`type` AS `account_type`,
`domain`.`id` AS `domain_id`,
`domain`.`uuid` AS `domain_uuid`,
`domain`.`name` AS `domain_name`,
`domain`.`path` AS `domain_path`,
`projects`.`id` AS `project_id`,
`projects`.`uuid` AS `project_uuid`,
`projects`.`name` AS `project_name`,
`instance_group`.`id` AS `instance_group_id`,
`instance_group`.`uuid` AS `instance_group_uuid`,
`instance_group`.`name` AS `instance_group_name`,
`vm_instance`.`uuid` AS `uuid`,
`vm_instance`.`user_id` AS `user_id`,
`vm_instance`.`last_host_id` AS `last_host_id`,
`vm_instance`.`vm_type` AS `type`,
`vm_instance`.`limit_cpu_use` AS `limit_cpu_use`,
`vm_instance`.`created` AS `created`,
`vm_instance`.`state` AS `state`,
`vm_instance`.`update_time` AS `update_time`,
`vm_instance`.`removed` AS `removed`,
`vm_instance`.`ha_enabled` AS `ha_enabled`,
`vm_instance`.`hypervisor_type` AS `hypervisor_type`,
`vm_instance`.`instance_name` AS `instance_name`,
`vm_instance`.`guest_os_id` AS `guest_os_id`,
`vm_instance`.`display_vm` AS `display_vm`,
`guest_os`.`uuid` AS `guest_os_uuid`,
`vm_instance`.`pod_id` AS `pod_id`,
`host_pod_ref`.`uuid` AS `pod_uuid`,
`vm_instance`.`private_ip_address` AS `private_ip_address`,
`vm_instance`.`private_mac_address` AS `private_mac_address`,
`vm_instance`.`vm_type` AS `vm_type`,
`data_center`.`id` AS `data_center_id`,
`data_center`.`uuid` AS `data_center_uuid`,
`data_center`.`name` AS `data_center_name`,
`data_center`.`is_security_group_enabled` AS `security_group_enabled`,
`data_center`.`networktype` AS `data_center_network_type`,
`host`.`id` AS `host_id`,
`host`.`uuid` AS `host_uuid`,
`host`.`name` AS `host_name`,
`host`.`cluster_id` AS `cluster_id`,
`host`.`status` AS `host_status`,
`host`.`resource_state` AS `host_resource_state`,
`vm_template`.`id` AS `template_id`,
`vm_template`.`uuid` AS `template_uuid`,
`vm_template`.`name` AS `template_name`,
`vm_template`.`type` AS `template_type`,
`vm_template`.`display_text` AS `template_display_text`,
`vm_template`.`enable_password` AS `password_enabled`,
`iso`.`id` AS `iso_id`,
`iso`.`uuid` AS `iso_uuid`,
`iso`.`name` AS `iso_name`,
`iso`.`display_text` AS `iso_display_text`,
`service_offering`.`id` AS `service_offering_id`,
`service_offering`.`uuid` AS `service_offering_uuid`,
`disk_offering`.`uuid` AS `disk_offering_uuid`,
`disk_offering`.`id` AS `disk_offering_id`,
(CASE
WHEN ISNULL(`service_offering`.`cpu`) THEN `custom_cpu`.`value`
ELSE `service_offering`.`cpu`
END) AS `cpu`,
(CASE
WHEN ISNULL(`service_offering`.`speed`) THEN `custom_speed`.`value`
ELSE `service_offering`.`speed`
END) AS `speed`,
(CASE
WHEN ISNULL(`service_offering`.`ram_size`) THEN `custom_ram_size`.`value`
ELSE `service_offering`.`ram_size`
END) AS `ram_size`,
`backup_offering`.`uuid` AS `backup_offering_uuid`,
`backup_offering`.`id` AS `backup_offering_id`,
`service_offering`.`name` AS `service_offering_name`,
`disk_offering`.`name` AS `disk_offering_name`,
`backup_offering`.`name` AS `backup_offering_name`,
`storage_pool`.`id` AS `pool_id`,
`storage_pool`.`uuid` AS `pool_uuid`,
`storage_pool`.`pool_type` AS `pool_type`,
`volumes`.`id` AS `volume_id`,
`volumes`.`uuid` AS `volume_uuid`,
`volumes`.`device_id` AS `volume_device_id`,
`volumes`.`volume_type` AS `volume_type`,
`security_group`.`id` AS `security_group_id`,
`security_group`.`uuid` AS `security_group_uuid`,
`security_group`.`name` AS `security_group_name`,
`security_group`.`description` AS `security_group_description`,
`nics`.`id` AS `nic_id`,
`nics`.`uuid` AS `nic_uuid`,
`nics`.`device_id` AS `nic_device_id`,
`nics`.`network_id` AS `network_id`,
`nics`.`ip4_address` AS `ip_address`,
`nics`.`ip6_address` AS `ip6_address`,
`nics`.`ip6_gateway` AS `ip6_gateway`,
`nics`.`ip6_cidr` AS `ip6_cidr`,
`nics`.`default_nic` AS `is_default_nic`,
`nics`.`gateway` AS `gateway`,
`nics`.`netmask` AS `netmask`,
`nics`.`mac_address` AS `mac_address`,
`nics`.`broadcast_uri` AS `broadcast_uri`,
`nics`.`isolation_uri` AS `isolation_uri`,
`vpc`.`id` AS `vpc_id`,
`vpc`.`uuid` AS `vpc_uuid`,
`networks`.`uuid` AS `network_uuid`,
`networks`.`name` AS `network_name`,
`networks`.`traffic_type` AS `traffic_type`,
`networks`.`guest_type` AS `guest_type`,
`user_ip_address`.`id` AS `public_ip_id`,
`user_ip_address`.`uuid` AS `public_ip_uuid`,
`user_ip_address`.`public_ip_address` AS `public_ip_address`,
`ssh_details`.`value` AS `keypair_names`,
`resource_tags`.`id` AS `tag_id`,
`resource_tags`.`uuid` AS `tag_uuid`,
`resource_tags`.`key` AS `tag_key`,
`resource_tags`.`value` AS `tag_value`,
`resource_tags`.`domain_id` AS `tag_domain_id`,
`domain`.`uuid` AS `tag_domain_uuid`,
`domain`.`name` AS `tag_domain_name`,
`resource_tags`.`account_id` AS `tag_account_id`,
`account`.`account_name` AS `tag_account_name`,
`resource_tags`.`resource_id` AS `tag_resource_id`,
`resource_tags`.`resource_uuid` AS `tag_resource_uuid`,
`resource_tags`.`resource_type` AS `tag_resource_type`,
`resource_tags`.`customer` AS `tag_customer`,
`async_job`.`id` AS `job_id`,
`async_job`.`uuid` AS `job_uuid`,
`async_job`.`job_status` AS `job_status`,
`async_job`.`account_id` AS `job_account_id`,
`affinity_group`.`id` AS `affinity_group_id`,
`affinity_group`.`uuid` AS `affinity_group_uuid`,
`affinity_group`.`name` AS `affinity_group_name`,
`affinity_group`.`description` AS `affinity_group_description`,
`autoscale_vmgroups`.`id` AS `autoscale_vmgroup_id`,
`autoscale_vmgroups`.`uuid` AS `autoscale_vmgroup_uuid`,
`autoscale_vmgroups`.`name` AS `autoscale_vmgroup_name`,
`vm_instance`.`dynamically_scalable` AS `dynamically_scalable`,
`user_data`.`id` AS `user_data_id`,
`user_data`.`uuid` AS `user_data_uuid`,
`user_data`.`name` AS `user_data_name`,
`user_vm`.`user_data_details` AS `user_data_details`,
`vm_template`.`user_data_link_policy` AS `user_data_policy`
FROM
(((((((((((((((((((((((((((((((((((`user_vm`
JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`)
AND ISNULL(`vm_instance`.`removed`))))
JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`)))
JOIN `domain` ON ((`vm_instance`.`domain_id` = `domain`.`id`)))
LEFT JOIN `guest_os` ON ((`vm_instance`.`guest_os_id` = `guest_os`.`id`)))
LEFT JOIN `host_pod_ref` ON ((`vm_instance`.`pod_id` = `host_pod_ref`.`id`)))
LEFT JOIN `projects` ON ((`projects`.`project_account_id` = `account`.`id`)))
LEFT JOIN `instance_group_vm_map` ON ((`vm_instance`.`id` = `instance_group_vm_map`.`instance_id`)))
LEFT JOIN `instance_group` ON ((`instance_group_vm_map`.`group_id` = `instance_group`.`id`)))
LEFT JOIN `data_center` ON ((`vm_instance`.`data_center_id` = `data_center`.`id`)))
LEFT JOIN `host` ON ((`vm_instance`.`host_id` = `host`.`id`)))
LEFT JOIN `vm_template` ON ((`vm_instance`.`vm_template_id` = `vm_template`.`id`)))
LEFT JOIN `vm_template` `iso` ON ((`iso`.`id` = `user_vm`.`iso_id`)))
LEFT JOIN `volumes` ON ((`vm_instance`.`id` = `volumes`.`instance_id`)))
LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`)))
LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`volumes`.`disk_offering_id` = `svc_disk_offering`.`id`)))
LEFT JOIN `disk_offering` ON ((`volumes`.`disk_offering_id` = `disk_offering`.`id`)))
LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`)))
LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`)))
LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`)))
LEFT JOIN `security_group` ON ((`security_group_vm_map`.`security_group_id` = `security_group`.`id`)))
LEFT JOIN `user_data` ON ((`user_data`.`id` = `user_vm`.`user_data_id`)))
LEFT JOIN `nics` ON (((`vm_instance`.`id` = `nics`.`instance_id`)
AND ISNULL(`nics`.`removed`))))
LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`)))
LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`)
AND ISNULL(`vpc`.`removed`))))
LEFT JOIN `user_ip_address` ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`)))
LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`)
AND (`ssh_details`.`name` = 'SSH.KeyPairNames'))))
LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`)
AND (`resource_tags`.`resource_type` = 'UserVm'))))
LEFT JOIN `async_job` ON (((`async_job`.`instance_id` = `vm_instance`.`id`)
AND (`async_job`.`instance_type` = 'VirtualMachine')
AND (`async_job`.`job_status` = 0))))
LEFT JOIN `affinity_group_vm_map` ON ((`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id`)))
LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`)))
LEFT JOIN `autoscale_vmgroup_vm_map` ON ((`autoscale_vmgroup_vm_map`.`instance_id` = `vm_instance`.`id`)))
LEFT JOIN `autoscale_vmgroups` ON ((`autoscale_vmgroup_vm_map`.`vmgroup_id` = `autoscale_vmgroups`.`id`)))
LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`)
AND (`custom_cpu`.`name` = 'CpuNumber'))))
LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`)
AND (`custom_speed`.`name` = 'CpuSpeed'))))
LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`)
AND (`custom_ram_size`.`name` = 'memory'))));
-- Add tables for Cluster DRS -- Add tables for Cluster DRS
DROP TABLE IF EXISTS `cloud`.`cluster_drs_plan`; DROP TABLE IF EXISTS `cloud`.`cluster_drs_plan`;
CREATE TABLE `cloud`.`cluster_drs_plan` ( CREATE TABLE `cloud`.`cluster_drs_plan` (

View File

@ -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.storage;
import org.junit.Assert;
import org.junit.Test;
public class VnfTemplateDetailVOTest {
static long templateId = 100L;
static String name = "key1";
static String value = "value1";
static boolean display = true;
@Test
public void testVnfTemplateNicVOProperties() {
VnfTemplateDetailVO detailVO = new VnfTemplateDetailVO(templateId, name, value, display);
Assert.assertEquals(templateId, detailVO.getResourceId());
Assert.assertEquals(name, detailVO.getName());
Assert.assertEquals(value, detailVO.getValue());
Assert.assertEquals(display, detailVO.isDisplay());
}
}

View File

@ -0,0 +1,46 @@
// 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.storage;
import org.junit.Assert;
import org.junit.Test;
public class VnfTemplateNicVOTest {
static long templateId = 100L;
static long deviceId = 0L;
static String deviceName = "eth0";
static boolean required = true;
static boolean management = false;
static String description = "description of vnf nic";
@Test
public void testVnfTemplateNicVOProperties() {
VnfTemplateNicVO nicVO = new VnfTemplateNicVO(templateId, deviceId, deviceName, required, management, description);
Assert.assertEquals(templateId, nicVO.getTemplateId());
Assert.assertEquals(deviceId, nicVO.getDeviceId());
Assert.assertEquals(deviceName, nicVO.getDeviceName());
Assert.assertEquals(required, nicVO.isRequired());
Assert.assertEquals(management, nicVO.isManagement());
Assert.assertEquals(description, nicVO.getDescription());
String expected = String.format("Template {\"deviceId\":%d,\"id\":0,\"required\":%s,\"templateId\":%d}", deviceId, required, templateId);
Assert.assertEquals(expected, nicVO.toString());
}
}

View File

@ -0,0 +1,88 @@
// 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.storage.dao;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class VnfTemplateNicDaoImplTest {
@Mock
SearchBuilder<VnfTemplateNicVO> searchBuilderVnfTemplateNicVOMock;
@Mock
SearchCriteria<VnfTemplateNicVO> searchCriteriaVnfTemplateNicVOMock;
@Mock
List<VnfTemplateNicVO> listVnfTemplateNicVOMock;
@Mock
private TransactionLegacy transactionMock;
@Spy
VnfTemplateNicDaoImpl vnfTemplateNicDaoImplSpy;
@Before
public void setUp() {
vnfTemplateNicDaoImplSpy.TemplateSearch = searchBuilderVnfTemplateNicVOMock;
Mockito.doReturn(searchCriteriaVnfTemplateNicVOMock).when(searchBuilderVnfTemplateNicVOMock).create();
Mockito.doNothing().when(searchCriteriaVnfTemplateNicVOMock).setParameters(Mockito.anyString(), Mockito.any());
}
@Test
public void testListByTemplateId() {
Mockito.doReturn(listVnfTemplateNicVOMock).when(vnfTemplateNicDaoImplSpy).listBy(Mockito.any(SearchCriteria.class));
long templateId = 100L;
List<VnfTemplateNicVO> result = vnfTemplateNicDaoImplSpy.listByTemplateId(templateId);
Assert.assertEquals(listVnfTemplateNicVOMock, result);
Mockito.verify(searchCriteriaVnfTemplateNicVOMock).setParameters("templateId", templateId);
}
@Test
public void testDeleteByTemplateId() {
Mockito.doReturn(0).when(vnfTemplateNicDaoImplSpy).remove(searchCriteriaVnfTemplateNicVOMock);
long templateId = 100L;
try (MockedStatic<TransactionLegacy> ignore = Mockito.mockStatic(TransactionLegacy.class)) {
Mockito.when(TransactionLegacy.currentTxn()).thenReturn(transactionMock);
Mockito.doNothing().when(transactionMock).start();
Mockito.doReturn(true).when(transactionMock).commit();
vnfTemplateNicDaoImplSpy.deleteByTemplateId(templateId);
Mockito.verify(transactionMock, Mockito.times(1)).start();
Mockito.verify(vnfTemplateNicDaoImplSpy, Mockito.times(1)).remove(searchCriteriaVnfTemplateNicVOMock);
Mockito.verify(transactionMock, Mockito.times(1)).commit();
}
}
}

View File

@ -1533,6 +1533,10 @@ public class ApiDBUtils {
return s_ipAddressDao.findByAssociatedVmId(vmId); return s_ipAddressDao.findByAssociatedVmId(vmId);
} }
public static IpAddress findIpByAssociatedVmIdAndNetworkId(long vmId, long networkId) {
return s_ipAddressDao.findByVmIdAndNetworkId(networkId, vmId);
}
public static String getHaTag() { public static String getHaTag() {
return s_haMgr.getHaTag(); return s_haMgr.getHaTag();
} }

View File

@ -99,6 +99,7 @@ import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd;
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd; import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd; import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
import org.apache.cloudstack.api.command.user.template.ListVnfTemplatesCmd;
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd; import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd; import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd;
@ -222,6 +223,7 @@ import com.cloud.ha.HighAvailabilityManager;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.RouterHealthCheckResult; import com.cloud.network.RouterHealthCheckResult;
import com.cloud.network.VNF;
import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.VpcVirtualNetworkApplianceService;
import com.cloud.network.dao.RouterHealthCheckResultDao; import com.cloud.network.dao.RouterHealthCheckResultDao;
import com.cloud.network.dao.RouterHealthCheckResultVO; import com.cloud.network.dao.RouterHealthCheckResultVO;
@ -1315,6 +1317,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
userVmSearchBuilder.join("autoScaleVmGroup", autoScaleMapSearch, autoScaleMapSearch.entity().getInstanceId(), userVmSearchBuilder.entity().getId(), JoinBuilder.JoinType.INNER); userVmSearchBuilder.join("autoScaleVmGroup", autoScaleMapSearch, autoScaleMapSearch.entity().getInstanceId(), userVmSearchBuilder.entity().getId(), JoinBuilder.JoinType.INNER);
} }
Boolean isVnf = cmd.getVnf();
if (isVnf != null) {
SearchBuilder<VMTemplateVO> templateSearch = _templateDao.createSearchBuilder();
templateSearch.and("templateTypeEQ", templateSearch.entity().getTemplateType(), Op.EQ);
templateSearch.and("templateTypeNEQ", templateSearch.entity().getTemplateType(), Op.NEQ);
userVmSearchBuilder.join("vmTemplate", templateSearch, templateSearch.entity().getId(), userVmSearchBuilder.entity().getTemplateId(), JoinBuilder.JoinType.INNER);
}
SearchCriteria<UserVmVO> userVmSearchCriteria = userVmSearchBuilder.create(); SearchCriteria<UserVmVO> userVmSearchCriteria = userVmSearchBuilder.create();
accountMgr.buildACLSearchCriteria(userVmSearchCriteria, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); accountMgr.buildACLSearchCriteria(userVmSearchCriteria, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
@ -1423,6 +1434,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
userVmSearchCriteria.setJoinParameters("autoScaleVmGroup", "autoScaleVmGroupId", autoScaleVmGroupId); userVmSearchCriteria.setJoinParameters("autoScaleVmGroup", "autoScaleVmGroupId", autoScaleVmGroupId);
} }
if (isVnf != null) {
if (isVnf) {
userVmSearchCriteria.setJoinParameters("vmTemplate", "templateTypeEQ", TemplateType.VNF);
} else {
userVmSearchCriteria.setJoinParameters("vmTemplate", "templateTypeNEQ", TemplateType.VNF);
}
}
if (isRootAdmin) { if (isRootAdmin) {
if (podId != null) { if (podId != null) {
userVmSearchCriteria.setParameters("podId", podId); userVmSearchCriteria.setParameters("podId", podId);
@ -3791,13 +3810,24 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured)); boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured));
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
String templateType = cmd.getTemplateType();
if (cmd instanceof ListVnfTemplatesCmd) {
if (templateType == null) {
templateType = TemplateType.VNF.name();
} else if (!TemplateType.VNF.name().equals(templateType)) {
throw new InvalidParameterValueException("Template type must be VNF when list VNF templates");
}
}
Boolean isVnf = cmd.getVnf();
return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType,
showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique()); showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(), templateType, isVnf);
} }
private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize, private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize,
Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller, Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller,
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags, boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, Boolean showUnique) { ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags, boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, Boolean showUnique, String templateType,
Boolean isVnf) {
// check if zone is configured, if not, just return empty list // check if zone is configured, if not, just return empty list
List<HypervisorType> hypers = null; List<HypervisorType> hypers = null;
@ -3964,7 +3994,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
applyPublicTemplateSharingRestrictions(sc, caller); applyPublicTemplateSharingRestrictions(sc, caller);
return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr, caller, return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr, caller,
showRemovedTmpl, parentTemplateId, showUnique, searchFilter, sc); showRemovedTmpl, parentTemplateId, showUnique, templateType, isVnf, searchFilter, sc);
} }
@ -4019,7 +4049,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<HypervisorType> hypers, Map<String, String> tags, String name, String keyword, private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<HypervisorType> hypers, Map<String, String> tags, String name, String keyword,
HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr, Account caller, HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr, Account caller,
boolean showRemovedTmpl, Long parentTemplateId, Boolean showUnique, boolean showRemovedTmpl, Long parentTemplateId, Boolean showUnique, String templateType, Boolean isVnf,
Filter searchFilter, SearchCriteria<TemplateJoinVO> sc) { Filter searchFilter, SearchCriteria<TemplateJoinVO> sc) {
if (!isIso) { if (!isIso) {
// add hypervisor criteria for template case // add hypervisor criteria for template case
@ -4101,6 +4131,18 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
sc.addAnd("parentTemplateId", SearchCriteria.Op.EQ, parentTemplateId); sc.addAnd("parentTemplateId", SearchCriteria.Op.EQ, parentTemplateId);
} }
if (templateType != null) {
sc.addAnd("templateType", SearchCriteria.Op.EQ, templateType);
}
if (isVnf != null) {
if (isVnf) {
sc.addAnd("templateType", SearchCriteria.Op.EQ, TemplateType.VNF);
} else {
sc.addAnd("templateType", SearchCriteria.Op.NEQ, TemplateType.VNF);
}
}
// don't return removed template, this should not be needed since we // don't return removed template, this should not be needed since we
// changed annotation for removed field in TemplateJoinVO. // changed annotation for removed field in TemplateJoinVO.
// sc.addAnd("removed", SearchCriteria.Op.NULL); // sc.addAnd("removed", SearchCriteria.Op.NULL);
@ -4192,7 +4234,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(),
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique()); hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null);
} }
@Override @Override
@ -4212,6 +4254,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
} }
fillVMOrTemplateDetailOptions(options, hypervisorType); fillVMOrTemplateDetailOptions(options, hypervisorType);
break; break;
case VnfTemplate:
fillVnfTemplateDetailOptions(options);
return new DetailOptionsResponse(options);
default: default:
throw new CloudRuntimeException("Resource type not supported."); throw new CloudRuntimeException("Resource type not supported.");
} }
@ -4235,6 +4280,19 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
return responses; return responses;
} }
private void fillVnfTemplateDetailOptions(final Map<String, List<String>> options) {
for (VNF.AccessDetail detail : VNF.AccessDetail.values()) {
if (VNF.AccessDetail.ACCESS_METHODS.equals(detail)) {
options.put(detail.name().toLowerCase(), Arrays.stream(VNF.AccessMethod.values()).map(method -> method.toString()).sorted().collect(Collectors.toList()));
} else {
options.put(detail.name().toLowerCase(), Collections.emptyList());
}
}
for (VNF.VnfDetail detail : VNF.VnfDetail.values()) {
options.put(detail.name().toLowerCase(), Collections.emptyList());
}
}
private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> options, final HypervisorType hypervisorType) { private void fillVMOrTemplateDetailOptions(final Map<String, List<String>> options, final HypervisorType hypervisorType) {
if (options == null) { if (options == null) {
throw new CloudRuntimeException("Invalid/null detail-options response object passed"); throw new CloudRuntimeException("Invalid/null detail-options response object passed");

View File

@ -17,6 +17,7 @@
package com.cloud.api.query.dao; package com.cloud.api.query.dao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -29,10 +30,16 @@ import javax.inject.Inject;
import com.cloud.deployasis.DeployAsIsConstants; import com.cloud.deployasis.DeployAsIsConstants;
import com.cloud.deployasis.TemplateDeployAsIsDetailVO; import com.cloud.deployasis.TemplateDeployAsIsDetailVO;
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao; import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.user.dao.UserDataDao; import com.cloud.user.dao.UserDataDao;
import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.response.VnfNicResponse;
import org.apache.cloudstack.api.response.VnfTemplateResponse;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -93,6 +100,10 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
private AnnotationDao annotationDao; private AnnotationDao annotationDao;
@Inject @Inject
private UserDataDao userDataDao; private UserDataDao userDataDao;
@Inject
VnfTemplateDetailsDao vnfTemplateDetailsDao;
@Inject
VnfTemplateNicDao vnfTemplateNicDao;
private final SearchBuilder<TemplateJoinVO> tmpltIdPairSearch; private final SearchBuilder<TemplateJoinVO> tmpltIdPairSearch;
@ -190,7 +201,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
} }
} }
TemplateResponse templateResponse = new TemplateResponse(); TemplateResponse templateResponse = initTemplateResponse(template);
templateResponse.setDownloadProgress(downloadProgressDetails); templateResponse.setDownloadProgress(downloadProgressDetails);
templateResponse.setId(template.getUuid()); templateResponse.setId(template.getUuid());
templateResponse.setName(template.getName()); templateResponse.setName(template.getName());
@ -305,10 +316,29 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
templateResponse.setUserDataParams(template.getUserDataParams()); templateResponse.setUserDataParams(template.getUserDataParams());
templateResponse.setUserDataPolicy(template.getUserDataPolicy()); templateResponse.setUserDataPolicy(template.getUserDataPolicy());
} }
templateResponse.setObjectName("template"); templateResponse.setObjectName("template");
return templateResponse; return templateResponse;
} }
private TemplateResponse initTemplateResponse(TemplateJoinVO template) {
TemplateResponse templateResponse = new TemplateResponse();
if (Storage.TemplateType.VNF.equals(template.getTemplateType())) {
VnfTemplateResponse vnfTemplateResponse = new VnfTemplateResponse();
List<VnfTemplateNicVO> nics = vnfTemplateNicDao.listByTemplateId(template.getId());
for (VnfTemplateNicVO nic : nics) {
vnfTemplateResponse.addVnfNic(new VnfNicResponse(nic.getDeviceId(), nic.getDeviceName(), nic.isRequired(), nic.isManagement(), nic.getDescription()));
}
List<VnfTemplateDetailVO> details = vnfTemplateDetailsDao.listDetails(template.getId());
Collections.sort(details, (v1, v2) -> v1.getName().compareToIgnoreCase(v2.getName()));
for (VnfTemplateDetailVO detail : details) {
vnfTemplateResponse.addVnfDetail(detail.getName(), detail.getValue());
}
templateResponse = vnfTemplateResponse;
}
return templateResponse;
}
private void setDeployAsIsDetails(TemplateJoinVO template, TemplateResponse templateResponse) { private void setDeployAsIsDetails(TemplateJoinVO template, TemplateResponse templateResponse) {
if (template.isDeployAsIs()) { if (template.isDeployAsIs()) {
List<TemplateDeployAsIsDetailVO> deployAsIsDetails = templateDeployAsIsDetailsDao.listDetails(template.getId()); List<TemplateDeployAsIsDetailVO> deployAsIsDetails = templateDeployAsIsDetailsDao.listDetails(template.getId());
@ -326,7 +356,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
// compared to listTemplates and listIsos. // compared to listTemplates and listIsos.
@Override @Override
public TemplateResponse newUpdateResponse(TemplateJoinVO result) { public TemplateResponse newUpdateResponse(TemplateJoinVO result) {
TemplateResponse response = new TemplateResponse(); TemplateResponse response = initTemplateResponse(result);
response.setId(result.getUuid()); response.setId(result.getUuid());
response.setName(result.getName()); response.setName(result.getName());
response.setDisplayText(result.getDisplayText()); response.setDisplayText(result.getDisplayText());

View File

@ -18,6 +18,7 @@ package com.cloud.api.query.dao;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
@ -39,6 +40,7 @@ import org.apache.cloudstack.api.response.NicResponse;
import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VnfNicResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.query.QueryService;
@ -51,11 +53,17 @@ import com.cloud.api.ApiResponseHelper;
import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.gpu.GPU; import com.cloud.gpu.GPU;
import com.cloud.host.ControlState; import com.cloud.host.ControlState;
import com.cloud.network.IpAddress;
import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOS;
import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.user.User; import com.cloud.user.User;
@ -95,6 +103,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
private VpcDao vpcDao; private VpcDao vpcDao;
@Inject @Inject
UserStatisticsDao userStatsDao; UserStatisticsDao userStatsDao;
@Inject
VnfTemplateDetailsDao vnfTemplateDetailsDao;
@Inject
VnfTemplateNicDao vnfTemplateNicDao;
private final SearchBuilder<UserVmJoinVO> VmDetailSearch; private final SearchBuilder<UserVmJoinVO> VmDetailSearch;
private final SearchBuilder<UserVmJoinVO> activeVmByIsoSearch; private final SearchBuilder<UserVmJoinVO> activeVmByIsoSearch;
@ -183,6 +195,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
userVmResponse.setTemplateName(userVm.getTemplateName()); userVmResponse.setTemplateName(userVm.getTemplateName());
userVmResponse.setTemplateDisplayText(userVm.getTemplateDisplayText()); userVmResponse.setTemplateDisplayText(userVm.getTemplateDisplayText());
userVmResponse.setPasswordEnabled(userVm.isPasswordEnabled()); userVmResponse.setPasswordEnabled(userVm.isPasswordEnabled());
userVmResponse.setTemplateType(userVm.getTemplateType().toString());
} }
if (details.contains(VMDetails.all) || details.contains(VMDetails.iso)) { if (details.contains(VMDetails.all) || details.contains(VMDetails.iso)) {
userVmResponse.setIsoId(userVm.getIsoUuid()); userVmResponse.setIsoId(userVm.getIsoUuid());
@ -320,6 +333,12 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
} }
nicResponse.setSecondaryIps(ipList); nicResponse.setSecondaryIps(ipList);
} }
IpAddress publicIp = ApiDBUtils.findIpByAssociatedVmIdAndNetworkId(userVm.getId(), userVm.getNetworkId());
if (publicIp != null) {
nicResponse.setPublicIpId(publicIp.getUuid());
nicResponse.setPublicIp(publicIp.getAddress().toString());
}
nicResponse.setObjectName("nic"); nicResponse.setObjectName("nic");
List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id).stream() List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id).stream()
@ -419,9 +438,25 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
addVmRxTxDataToResponse(userVm, userVmResponse); addVmRxTxDataToResponse(userVm, userVmResponse);
if (TemplateType.VNF.equals(userVm.getTemplateType()) && (details.contains(VMDetails.all) || details.contains(VMDetails.vnfnics))) {
addVnfInfoToserVmResponse(userVm, userVmResponse);
}
return userVmResponse; return userVmResponse;
} }
private void addVnfInfoToserVmResponse(UserVmJoinVO userVm, UserVmResponse userVmResponse) {
List<VnfTemplateNicVO> vnfNics = vnfTemplateNicDao.listByTemplateId(userVm.getTemplateId());
for (VnfTemplateNicVO nic : vnfNics) {
userVmResponse.addVnfNic(new VnfNicResponse(nic.getDeviceId(), nic.getDeviceName(), nic.isRequired(), nic.isManagement(), nic.getDescription()));
}
List<VnfTemplateDetailVO> vnfDetails = vnfTemplateDetailsDao.listDetails(userVm.getTemplateId());
Collections.sort(vnfDetails, (v1, v2) -> v1.getName().compareToIgnoreCase(v2.getName()));
for (VnfTemplateDetailVO detail : vnfDetails) {
userVmResponse.addVnfDetail(detail.getName(), detail.getValue());
}
}
private void addVmRxTxDataToResponse(UserVmJoinVO userVm, UserVmResponse userVmResponse) { private void addVmRxTxDataToResponse(UserVmJoinVO userVm, UserVmResponse userVmResponse) {
Long bytesReceived = 0L; Long bytesReceived = 0L;
Long bytesSent = 0L; Long bytesSent = 0L;
@ -519,6 +554,11 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
} }
nicResponse.setSecondaryIps(ipList); nicResponse.setSecondaryIps(ipList);
} }
IpAddress publicIp = ApiDBUtils.findIpByAssociatedVmIdAndNetworkId(uvo.getId(), uvo.getNetworkId());
if (publicIp != null) {
nicResponse.setPublicIpId(publicIp.getUuid());
nicResponse.setPublicIp(publicIp.getAddress().toString());
}
/* 18: extra dhcp options */ /* 18: extra dhcp options */
nicResponse.setObjectName("nic"); nicResponse.setObjectName("nic");

View File

@ -34,6 +34,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network.GuestType; import com.cloud.network.Network.GuestType;
import com.cloud.network.Networks.TrafficType; import com.cloud.network.Networks.TrafficType;
import com.cloud.resource.ResourceState; import com.cloud.resource.ResourceState;
import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.user.Account; import com.cloud.user.Account;
@ -191,6 +192,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
@Column(name = "template_name") @Column(name = "template_name")
private String templateName; private String templateName;
@Column(name = "template_type")
private TemplateType templateType;
@Column(name = "template_display_text", length = 4096) @Column(name = "template_display_text", length = 4096)
private String templateDisplayText; private String templateDisplayText;
@ -632,6 +636,10 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
return templateName; return templateName;
} }
public TemplateType getTemplateType() {
return templateType;
}
public String getTemplateDisplayText() { public String getTemplateDisplayText() {
return templateDisplayText; return templateDisplayText;
} }

View File

@ -275,7 +275,8 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId()); Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId());
_accountMgr.checkAccess(caller, null, true, owner); _accountMgr.checkAccess(caller, null, true, owner);
boolean isRouting = (cmd.isRoutingType() == null) ? false : cmd.isRoutingType(); TemplateType templateType = templateMgr.validateTemplateType(cmd, _accountMgr.isAdmin(caller.getAccountId()),
CollectionUtils.isEmpty(cmd.getZoneIds()));
List<Long> zoneId = cmd.getZoneIds(); List<Long> zoneId = cmd.getZoneIds();
// ignore passed zoneId if we are using region wide image store // ignore passed zoneId if we are using region wide image store
@ -305,7 +306,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
} }
return prepare(false, CallContext.current().getCallingUserId(), cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), cmd.isPasswordEnabled(), cmd.getRequiresHvm(), return prepare(false, CallContext.current().getCallingUserId(), cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), cmd.isPasswordEnabled(), cmd.getRequiresHvm(),
cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), cmd.isExtractable(), cmd.getFormat(), cmd.getOsTypeId(), zoneId, hypervisorType, cmd.getChecksum(), true, cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), cmd.isExtractable(), cmd.getFormat(), cmd.getOsTypeId(), zoneId, hypervisorType, cmd.getChecksum(), true,
cmd.getTemplateTag(), owner, details, cmd.isSshKeyEnabled(), null, cmd.isDynamicallyScalable(), isRouting ? TemplateType.ROUTING : TemplateType.USER, cmd.getTemplateTag(), owner, details, cmd.isSshKeyEnabled(), null, cmd.isDynamicallyScalable(), templateType,
cmd.isDirectDownload(), cmd.isDeployAsIs()); cmd.isDirectDownload(), cmd.isDeployAsIs());
} }

View File

@ -36,6 +36,7 @@ import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseListTemplateOrIsoPermissionsCmd; import org.apache.cloudstack.api.BaseListTemplateOrIsoPermissionsCmd;
import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoCmd; import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoCmd;
import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoPermissionsCmd; import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoPermissionsCmd;
@ -53,8 +54,10 @@ import org.apache.cloudstack.api.command.user.template.ExtractTemplateCmd;
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd; import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
import org.apache.cloudstack.api.command.user.template.ListTemplatePermissionsCmd; import org.apache.cloudstack.api.command.user.template.ListTemplatePermissionsCmd;
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissionsCmd; import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissionsCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd; import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd;
import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.api.response.GetUploadParamsResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
@ -96,6 +99,8 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.storage.template.VnfTemplateUtils;
import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
@ -303,6 +308,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
@Inject @Inject
protected SnapshotHelper snapshotHelper; protected SnapshotHelper snapshotHelper;
@Inject
VnfTemplateManager vnfTemplateManager;
private TemplateAdapter getAdapter(HypervisorType type) { private TemplateAdapter getAdapter(HypervisorType type) {
TemplateAdapter adapter = null; TemplateAdapter adapter = null;
@ -360,6 +367,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
if (template != null) { if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
if (cmd instanceof RegisterVnfTemplateCmd) {
vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd);
}
return template; return template;
} else { } else {
throw new CloudRuntimeException("Failed to create a template"); throw new CloudRuntimeException("Failed to create a template");
@ -1329,6 +1339,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
throw new InvalidParameterValueException("Please specify a valid template."); throw new InvalidParameterValueException("Please specify a valid template.");
} }
VnfTemplateUtils.validateApiCommandParams(cmd, template);
TemplateAdapter adapter = getAdapter(template.getHypervisorType()); TemplateAdapter adapter = getAdapter(template.getHypervisorType());
TemplateProfile profile = adapter.prepareDelete(cmd); TemplateProfile profile = adapter.prepareDelete(cmd);
return adapter.delete(profile); return adapter.delete(profile);
@ -2113,22 +2125,11 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
// update template type // update template type
TemplateType templateType = null; TemplateType templateType = null;
if (cmd instanceof UpdateTemplateCmd) { if (cmd instanceof UpdateTemplateCmd) {
String newType = ((UpdateTemplateCmd)cmd).getTemplateType(); boolean isAdmin = _accountMgr.isAdmin(account.getId());
if (newType != null) { templateType = validateTemplateType(cmd, isAdmin, template.isCrossZones());
if (!_accountService.isRootAdmin(account.getId())) { if (cmd instanceof UpdateVnfTemplateCmd) {
throw new PermissionDeniedException("Parameter templatetype can only be specified by a Root Admin, permission denied"); VnfTemplateUtils.validateApiCommandParams(cmd, template);
} vnfTemplateManager.updateVnfTemplate(template.getId(), (UpdateVnfTemplateCmd) cmd);
try {
templateType = TemplateType.valueOf(newType.toUpperCase());
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException("Please specify a valid templatetype: ROUTING / SYSTEM / USER / BUILTIN / PERHOST");
}
}
if (templateType != null && cmd.isRoutingType() != null && (TemplateType.ROUTING.equals(templateType) != cmd.isRoutingType())) {
throw new InvalidParameterValueException("Please specify a valid templatetype (consistent with isrouting parameter).");
}
if (templateType != null && (templateType == TemplateType.SYSTEM || templateType == TemplateType.BUILTIN) && !template.isCrossZones()) {
throw new InvalidParameterValueException("System and Builtin templates must be cross zone");
} }
} }
@ -2248,6 +2249,51 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
return _tmpltDao.findById(id); return _tmpltDao.findById(id);
} }
@Override
public TemplateType validateTemplateType(BaseCmd cmd, boolean isAdmin, boolean isCrossZones) {
if (!(cmd instanceof UpdateTemplateCmd) && !(cmd instanceof RegisterTemplateCmd)) {
return null;
}
TemplateType templateType = null;
String newType = null;
Boolean isRoutingType = null;
if (cmd instanceof UpdateTemplateCmd) {
newType = ((UpdateTemplateCmd)cmd).getTemplateType();
isRoutingType = ((UpdateTemplateCmd)cmd).isRoutingType();
} else if (cmd instanceof RegisterTemplateCmd) {
newType = ((RegisterTemplateCmd)cmd).getTemplateType();
isRoutingType = ((RegisterTemplateCmd)cmd).isRoutingType();
}
if (newType != null) {
try {
templateType = TemplateType.valueOf(newType.toUpperCase());
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException(String.format("Please specify a valid templatetype: %s",
org.apache.commons.lang3.StringUtils.join(",", TemplateType.values())));
}
}
if (templateType != null) {
if (isRoutingType != null && (TemplateType.ROUTING.equals(templateType) != isRoutingType)) {
throw new InvalidParameterValueException("Please specify a valid templatetype (consistent with isrouting parameter).");
} else if ((templateType == TemplateType.SYSTEM || templateType == TemplateType.BUILTIN) && !isCrossZones) {
throw new InvalidParameterValueException("System and Builtin templates must be cross zone.");
} else if ((cmd instanceof RegisterVnfTemplateCmd || cmd instanceof UpdateVnfTemplateCmd) && !TemplateType.VNF.equals(templateType)) {
throw new InvalidParameterValueException("The template type must be VNF for VNF templates, but the actual type is " + templateType);
}
} else if (cmd instanceof RegisterTemplateCmd) {
boolean isRouting = Boolean.TRUE.equals(isRoutingType);
templateType = (cmd instanceof RegisterVnfTemplateCmd) ? TemplateType.VNF : (isRouting ? TemplateType.ROUTING : TemplateType.USER);
}
if (templateType != null && !isAdmin && !Arrays.asList(TemplateType.USER, TemplateType.VNF).contains(templateType)) {
if (cmd instanceof RegisterTemplateCmd) {
throw new InvalidParameterValueException(String.format("Users can not register template with template type %s.", templateType));
} else if (cmd instanceof UpdateTemplateCmd) {
throw new InvalidParameterValueException(String.format("Users can not update template to template type %s.", templateType));
}
}
return templateType;
}
void validateDetails(VMTemplateVO template, Map<String, String> details) { void validateDetails(VMTemplateVO template, Map<String, String> details) {
if (MapUtils.isEmpty(details)) { if (MapUtils.isEmpty(details)) {
return; return;
@ -2270,7 +2316,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
String msg = String.format("Invalid %s: %s specified. Valid values are: %s", String msg = String.format("Invalid %s: %s specified. Valid values are: %s",
ApiConstants.BOOT_MODE, bootMode, Arrays.toString(ApiConstants.BootMode.values())); ApiConstants.BOOT_MODE, bootMode, Arrays.toString(ApiConstants.BootMode.values()));
s_logger.error(msg); s_logger.error(msg);
throw new InvalidParameterValueException(msg); } throw new InvalidParameterValueException(msg);
}
} }
void verifyTemplateId(Long id) { void verifyTemplateId(Long id) {

View File

@ -69,6 +69,7 @@ import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin;
import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd; import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
@ -123,6 +124,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.userdata.UserDataManager;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
import org.apache.cloudstack.utils.security.ParserUtils; import org.apache.cloudstack.utils.security.ParserUtils;
@ -620,6 +622,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Inject @Inject
private UserDataManager userDataManager; private UserDataManager userDataManager;
@Inject
VnfTemplateManager vnfTemplateManager;
private static final ConfigKey<Integer> VmIpFetchWaitInterval = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180", private static final ConfigKey<Integer> VmIpFetchWaitInterval = new ConfigKey<Integer>("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180",
"Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true); "Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true);
@ -5901,6 +5906,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (template == null) { if (template == null) {
throw new InvalidParameterValueException("Unable to use template " + templateId); throw new InvalidParameterValueException("Unable to use template " + templateId);
} }
if (TemplateType.VNF.equals(template.getTemplateType())) {
vnfTemplateManager.validateVnfApplianceNics(template, cmd.getNetworkIds());
} else if (cmd instanceof DeployVnfApplianceCmd) {
throw new InvalidParameterValueException("Can't deploy VNF appliance from a non-VNF template");
}
ServiceOfferingJoinVO svcOffering = serviceOfferingJoinDao.findById(serviceOfferingId); ServiceOfferingJoinVO svcOffering = serviceOfferingJoinDao.findById(serviceOfferingId);
@ -5982,14 +5992,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (networkIds != null) { if (networkIds != null) {
throw new InvalidParameterValueException("Can't specify network Ids in Basic zone"); throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
} else { } else {
vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId, vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd, zone, template, owner), owner, name, displayName, diskOfferingId,
size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(), size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(),
cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId); dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId);
} }
} else { } else {
if (zone.isSecurityGroupEnabled()) { if (zone.isSecurityGroupEnabled()) {
vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd), owner, name, vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd, zone, template, owner), owner, name,
displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard,
cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, null); dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, null);
@ -6001,6 +6011,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, group, vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, group,
cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getHypervisor(), cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(),
cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId); cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId);
if (cmd instanceof DeployVnfApplianceCmd) {
vnfTemplateManager.createIsolatedNetworkRulesForVnfAppliance(zone, template, owner, vm, (DeployVnfApplianceCmd) cmd);
}
} }
} }
@ -6267,6 +6280,20 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} }
} }
protected List<Long> getSecurityGroupIdList(SecurityGroupAction cmd, DataCenter zone, VirtualMachineTemplate template, Account owner) {
List<Long> securityGroupIdList = getSecurityGroupIdList(cmd);
if (cmd instanceof DeployVnfApplianceCmd) {
SecurityGroup securityGroup = vnfTemplateManager.createSecurityGroupForVnfAppliance(zone, template, owner, (DeployVnfApplianceCmd) cmd);
if (securityGroup != null) {
if (securityGroupIdList == null) {
securityGroupIdList = new ArrayList<>();
}
securityGroupIdList.add(securityGroup.getId());
}
}
return securityGroupIdList;
}
// this is an opportunity to verify that parameters that came in via the Details Map are OK // this is an opportunity to verify that parameters that came in via the Details Map are OK
// for example, minIops and maxIops should either both be specified or neither be specified and, // for example, minIops and maxIops should either both be specified or neither be specified and,
// if specified, minIops should be <= maxIops // if specified, minIops should be <= maxIops

View File

@ -0,0 +1,372 @@
// 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.storage.template;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.dc.DataCenter;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.IpAddress;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkService;
import com.cloud.network.VNF;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.firewall.FirewallService;
import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.rules.RulesService;
import com.cloud.network.security.SecurityGroup;
import com.cloud.network.security.SecurityGroupManager;
import com.cloud.network.security.SecurityGroupService;
import com.cloud.network.security.SecurityGroupVO;
import com.cloud.network.security.SecurityRule;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.NicVO;
import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.api.command.admin.template.ListVnfTemplatesCmdByAdmin;
import org.apache.cloudstack.api.command.admin.template.RegisterVnfTemplateCmdByAdmin;
import org.apache.cloudstack.api.command.admin.template.UpdateVnfTemplateCmdByAdmin;
import org.apache.cloudstack.api.command.admin.vm.DeployVnfApplianceCmdByAdmin;
import org.apache.cloudstack.api.command.user.template.DeleteVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.ListVnfTemplatesCmd;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
public class VnfTemplateManagerImpl extends ManagerBase implements VnfTemplateManager, PluggableService, Configurable {
static final Logger LOGGER = Logger.getLogger(VnfTemplateManagerImpl.class);
public static final String VNF_SECURITY_GROUP_NAME = "VNF_SecurityGroup_";
public static final String ACCESS_METHOD_SEPARATOR = ",";
public static final Integer ACCESS_DEFAULT_SSH_PORT = 22;
public static final Integer ACCESS_DEFAULT_HTTP_PORT = 80;
public static final Integer ACCESS_DEFAULT_HTTPS_PORT = 443;
@Inject
VnfTemplateDetailsDao vnfTemplateDetailsDao;
@Inject
VnfTemplateNicDao vnfTemplateNicDao;
@Inject
SecurityGroupManager securityGroupManager;
@Inject
SecurityGroupService securityGroupService;
@Inject
NetworkModel networkModel;
@Inject
IpAddressManager ipAddressManager;
@Inject
NicDao nicDao;
@Inject
NetworkDao networkDao;
@Inject
NetworkService networkService;
@Inject
RulesService rulesService;
@Inject
FirewallRulesDao firewallRulesDao;
@Inject
FirewallService firewallService;
@Override
public List<Class<?>> getCommands() {
final List<Class<?>> cmdList = new ArrayList<>();
if (!VnfTemplateAndApplianceEnabled.value()) {
return cmdList;
}
cmdList.add(RegisterVnfTemplateCmd.class);
cmdList.add(RegisterVnfTemplateCmdByAdmin.class);
cmdList.add(ListVnfTemplatesCmd.class);
cmdList.add(ListVnfTemplatesCmdByAdmin.class);
cmdList.add(UpdateVnfTemplateCmd.class);
cmdList.add(UpdateVnfTemplateCmdByAdmin.class);
cmdList.add(DeleteVnfTemplateCmd.class);
cmdList.add(DeployVnfApplianceCmd.class);
cmdList.add(DeployVnfApplianceCmdByAdmin.class);
return cmdList;
}
@Override
public void persistVnfTemplate(long templateId, RegisterVnfTemplateCmd cmd) {
persistVnfTemplateNics(templateId, cmd.getVnfNics());
persistVnfTemplateDetails(templateId, cmd);
}
private void persistVnfTemplateNics(long templateId, List<VNF.VnfNic> nics) {
for (VNF.VnfNic nic : nics) {
VnfTemplateNicVO vnfTemplateNicVO = new VnfTemplateNicVO(templateId, nic.getDeviceId(), nic.getName(), nic.isRequired(), nic.isManagement(), nic.getDescription());
vnfTemplateNicDao.persist(vnfTemplateNicVO);
}
}
private void persistVnfTemplateDetails(long templateId, RegisterVnfTemplateCmd cmd) {
persistVnfTemplateDetails(templateId, cmd.getVnfDetails());
}
private void persistVnfTemplateDetails(long templateId, Map<String, String> vnfDetails) {
for (Map.Entry<String, String> entry: vnfDetails.entrySet()) {
String value = entry.getValue();
if (VNF.AccessDetail.ACCESS_METHODS.name().equalsIgnoreCase(entry.getKey())) {
value = Arrays.stream(value.split(ACCESS_METHOD_SEPARATOR)).sorted().collect(Collectors.joining(ACCESS_METHOD_SEPARATOR));
}
vnfTemplateDetailsDao.addDetail(templateId, entry.getKey().toLowerCase(), value, true);
}
}
@Override
public void updateVnfTemplate(long templateId, UpdateVnfTemplateCmd cmd) {
updateVnfTemplateDetails(templateId, cmd);
updateVnfTemplateNics(templateId, cmd);
}
private void updateVnfTemplateDetails(long templateId, UpdateVnfTemplateCmd cmd) {
boolean cleanupVnfDetails = cmd.isCleanupVnfDetails();
if (cleanupVnfDetails) {
vnfTemplateDetailsDao.removeDetails(templateId);
} else if (MapUtils.isNotEmpty(cmd.getVnfDetails())) {
vnfTemplateDetailsDao.removeDetails(templateId);
persistVnfTemplateDetails(templateId, cmd.getVnfDetails());
}
}
private void updateVnfTemplateNics(long templateId, UpdateVnfTemplateCmd cmd) {
boolean cleanupVnfNics = cmd.isCleanupVnfNics();
if (cleanupVnfNics) {
vnfTemplateNicDao.deleteByTemplateId(templateId);
} else if (CollectionUtils.isNotEmpty(cmd.getVnfNics())) {
vnfTemplateNicDao.deleteByTemplateId(templateId);
persistVnfTemplateNics(templateId, cmd.getVnfNics());
}
}
@Override
public String getConfigComponentName() {
return VnfTemplateManager.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey[] { VnfTemplateAndApplianceEnabled };
}
@Override
public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds) {
if (CollectionUtils.isEmpty(networkIds)) {
throw new InvalidParameterValueException("VNF nics list is empty");
}
List<VnfTemplateNicVO> vnfNics = vnfTemplateNicDao.listByTemplateId(template.getId());
for (VnfTemplateNicVO vnfNic : vnfNics) {
if (vnfNic.isRequired() && networkIds.size() <= vnfNic.getDeviceId()) {
throw new InvalidParameterValueException("VNF nic is required but not found: " + vnfNic);
}
}
}
protected Set<Integer> getOpenPortsForVnfAppliance(VirtualMachineTemplate template) {
Set<Integer> ports = new HashSet<>();
VnfTemplateDetailVO accessMethodsDetail = vnfTemplateDetailsDao.findDetail(template.getId(), VNF.AccessDetail.ACCESS_METHODS.name().toLowerCase());
if (accessMethodsDetail == null || accessMethodsDetail.getValue() == null) {
return ports;
}
String[] accessMethods = accessMethodsDetail.getValue().split(ACCESS_METHOD_SEPARATOR);
for (String accessMethod : accessMethods) {
if (VNF.AccessMethod.SSH_WITH_KEY.toString().equalsIgnoreCase(accessMethod)
|| VNF.AccessMethod.SSH_WITH_PASSWORD.toString().equalsIgnoreCase(accessMethod)) {
VnfTemplateDetailVO accessDetail = vnfTemplateDetailsDao.findDetail(template.getId(), VNF.AccessDetail.SSH_PORT.name().toLowerCase());
if (accessDetail == null) {
ports.add(ACCESS_DEFAULT_SSH_PORT);
} else {
ports.add(NumbersUtil.parseInt(accessDetail.getValue(), ACCESS_DEFAULT_SSH_PORT));
}
} else if (VNF.AccessMethod.HTTP.toString().equalsIgnoreCase(accessMethod)) {
VnfTemplateDetailVO accessDetail = vnfTemplateDetailsDao.findDetail(template.getId(), VNF.AccessDetail.HTTP_PORT.name().toLowerCase());
if (accessDetail == null) {
ports.add(ACCESS_DEFAULT_HTTP_PORT);
} else {
ports.add(NumbersUtil.parseInt(accessDetail.getValue(), ACCESS_DEFAULT_HTTP_PORT));
}
} else if (VNF.AccessMethod.HTTPS.toString().equalsIgnoreCase(accessMethod)) {
VnfTemplateDetailVO accessDetail = vnfTemplateDetailsDao.findDetail(template.getId(), VNF.AccessDetail.HTTPS_PORT.name().toLowerCase());
if (accessDetail == null) {
ports.add(ACCESS_DEFAULT_HTTPS_PORT);
} else {
ports.add(NumbersUtil.parseInt(accessDetail.getValue(), ACCESS_DEFAULT_HTTPS_PORT));
}
}
}
return ports;
}
private Set<Long> getDeviceIdsOfVnfManagementNics(VirtualMachineTemplate template) {
Set<Long> deviceIds = new HashSet<>();
for (VnfTemplateNicVO nic : vnfTemplateNicDao.listByTemplateId(template.getId())) {
if (nic.isManagement()) {
deviceIds.add(nic.getDeviceId());
}
}
return deviceIds;
}
protected Map<Network, String> getManagementNetworkAndIp(VirtualMachineTemplate template, UserVm vm) {
Map<Network, String> networkAndIpMap = new HashMap<>();
Set<Long> managementDeviceIds = getDeviceIdsOfVnfManagementNics(template);
for (NicVO nic : nicDao.listByVmId(vm.getId())) {
if (managementDeviceIds.contains((long) nic.getDeviceId()) && nic.getIPv4Address() != null) {
Network network = networkDao.findById(nic.getNetworkId());
if (network == null || !Network.GuestType.Isolated.equals(network.getGuestType())) {
continue;
}
if (!networkModel.areServicesSupportedInNetwork(network.getId(), Network.Service.StaticNat)) {
LOGGER.info(String.format("Network ID: %s does not support static nat, " +
"skipping this network configuration for VNF appliance", network.getUuid()));
continue;
}
if (network.getVpcId() != null) {
LOGGER.info(String.format("Network ID: %s is a VPC tier, " +
"skipping this network configuration for VNF appliance", network.getUuid()));
continue;
}
if (!networkModel.areServicesSupportedInNetwork(network.getId(), Network.Service.Firewall)) {
LOGGER.info(String.format("Network ID: %s does not support firewall, " +
"skipping this network configuration for VNF appliance", network.getUuid()));
continue;
}
networkAndIpMap.put(network, nic.getIPv4Address());
}
}
return networkAndIpMap;
}
@Override
public SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner,
DeployVnfApplianceCmd cmd) {
if (zone == null || !zone.isSecurityGroupEnabled()) {
return null;
}
if (!cmd.getVnfConfigureManagement()) {
return null;
}
LOGGER.debug("Creating security group and rules for VNF appliance");
Set<Integer> ports = getOpenPortsForVnfAppliance(template);
if (ports.size() == 0) {
LOGGER.debug("No need to create security group and rules for VNF appliance as there is no ports to be open");
return null;
}
String securityGroupName = VNF_SECURITY_GROUP_NAME.concat(Long.toHexString(System.currentTimeMillis()));
SecurityGroupVO securityGroupVO = securityGroupManager.createSecurityGroup(securityGroupName,
"Security group for VNF appliance", owner.getDomainId(), owner.getId(), owner.getAccountName());
if (securityGroupVO == null) {
throw new CloudRuntimeException(String.format("Failed to create security group: %s", securityGroupName));
}
List<String> cidrList = cmd.getVnfCidrlist();
for (Integer port : ports) {
securityGroupService.authorizeSecurityGroupRule(securityGroupVO.getId(), NetUtils.TCP_PROTO, port, port,
null, null, cidrList, null, SecurityRule.SecurityRuleType.IngressRule);
}
return securityGroupVO;
}
@Override
public void createIsolatedNetworkRulesForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner,
UserVm vm, DeployVnfApplianceCmd cmd)
throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException {
Map<Network, String> networkAndIpMap = getManagementNetworkAndIp(template, vm);
Set<Integer> ports = getOpenPortsForVnfAppliance(template);
for (Map.Entry<Network, String> entry : networkAndIpMap.entrySet()) {
Network network = entry.getKey();
LOGGER.debug("Creating network rules for VNF appliance on isolated network " + network.getUuid());
String ip = entry.getValue();
IpAddress publicIp = networkService.allocateIP(owner, zone.getId(), network.getId(), null, null);
if (publicIp == null) {
continue;
}
publicIp = ipAddressManager.associateIPToGuestNetwork(publicIp.getId(), network.getId(), false);
if (publicIp.isSourceNat()) {
// If isolated network is not implemented, the first acquired Public IP will be Source NAT IP
publicIp = networkService.allocateIP(owner, zone.getId(), network.getId(), null, null);
if (publicIp == null) {
continue;
}
publicIp = ipAddressManager.associateIPToGuestNetwork(publicIp.getId(), network.getId(), false);
}
final IpAddress publicIpFinal = publicIp;
final List<String> cidrList = cmd.getVnfCidrlist();
try {
boolean result = rulesService.enableStaticNat(publicIp.getId(), vm.getId(), network.getId(), ip);
if (!result) {
throw new CloudRuntimeException(String.format("Failed to create static nat for vm: %s", vm.getUuid()));
}
} catch (NetworkRuleConflictException e) {
throw new CloudRuntimeException(String.format("Failed to create static nat for vm %s due to %s", vm.getUuid(), e.getMessage()));
}
if (network.getVpcId() == null) {
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<>() {
@Override
public void doInTransactionWithoutResult(final TransactionStatus status) throws CloudRuntimeException {
for (Integer port : ports) {
FirewallRuleVO newFirewallRule = new FirewallRuleVO(null, publicIpFinal.getId(), port, port, NetUtils.TCP_PROTO,
network.getId(), owner.getAccountId(), owner.getDomainId(), FirewallRule.Purpose.Firewall,
cidrList, null, null, null, FirewallRule.TrafficType.Ingress);
newFirewallRule.setDisplay(true);
newFirewallRule.setState(FirewallRule.State.Add);
firewallRulesDao.persist(newFirewallRule);
}
}
});
firewallService.applyIngressFwRules(publicIp.getId(), owner);
}
LOGGER.debug("Created network rules for VNF appliance on isolated network " + network.getUuid());
}
}
}

View File

@ -356,4 +356,5 @@
<property name="asyncJobDispatcher" ref="ApiAsyncJobDispatcher" /> <property name="asyncJobDispatcher" ref="ApiAsyncJobDispatcher" />
</bean> </bean>
<bean id="vnfTemplateManager" class="org.apache.cloudstack.storage.template.VnfTemplateManagerImpl" />
</beans> </beans>

View File

@ -24,7 +24,9 @@ import com.cloud.event.dao.EventJoinDao;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.PermissionDeniedException;
import com.cloud.network.Network; import com.cloud.network.Network;
import com.cloud.network.VNF;
import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.NetworkVO;
import com.cloud.server.ResourceTag;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
@ -39,6 +41,8 @@ import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
import org.apache.cloudstack.api.response.DetailOptionsResponse;
import org.apache.cloudstack.api.response.EventResponse; import org.apache.cloudstack.api.response.EventResponse;
import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
@ -54,10 +58,13 @@ import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -197,6 +204,27 @@ public class QueryManagerImplTest {
queryManager.searchForEvents(cmd); queryManager.searchForEvents(cmd);
} }
@Test
public void listVnfDetailOptionsCmd() {
ListDetailOptionsCmd cmd = Mockito.mock(ListDetailOptionsCmd.class);
when(cmd.getResourceType()).thenReturn(ResourceTag.ResourceObjectType.VnfTemplate);
DetailOptionsResponse response = queryManager.listDetailOptions(cmd);
Map<String, List<String>> options = response.getDetails();
int expectedLength = VNF.AccessDetail.values().length + VNF.VnfDetail.values().length;
Assert.assertEquals(expectedLength, options.size());
Set<String> keys = options.keySet();
for (VNF.AccessDetail detail : VNF.AccessDetail.values()) {
Assert.assertTrue(keys.contains(detail.name().toLowerCase()));
}
for (VNF.VnfDetail detail : VNF.VnfDetail.values()) {
Assert.assertTrue(keys.contains(detail.name().toLowerCase()));
}
List<String> expectedAccessMethods = Arrays.stream(VNF.AccessMethod.values()).map(method -> method.toString()).sorted().collect(Collectors.toList());
Assert.assertEquals(expectedAccessMethods, options.get(VNF.AccessDetail.ACCESS_METHODS.name().toLowerCase()));
}
@Test @Test
public void applyPublicTemplateRestrictionsTestDoesNotApplyRestrictionsWhenCallerIsRootAdmin() { public void applyPublicTemplateRestrictionsTestDoesNotApplyRestrictionsWhenCallerIsRootAdmin() {
Mockito.when(accountMock.getType()).thenReturn(Account.Type.ADMIN); Mockito.when(accountMock.getType()).thenReturn(Account.Type.ADMIN);

View File

@ -19,17 +19,25 @@ package com.cloud.api.query.dao;
import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.template.TemplateManager; import com.cloud.template.TemplateManager;
import com.cloud.user.Account; import com.cloud.user.Account;
import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.VnfTemplateResponse;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -39,6 +47,12 @@ public class TemplateJoinDaoImplTest extends GenericDaoBaseWithTagInformationBas
@InjectMocks @InjectMocks
private TemplateJoinDaoImpl _templateJoinDaoImpl; private TemplateJoinDaoImpl _templateJoinDaoImpl;
@Mock
private VnfTemplateNicDao vnfTemplateNicDao;
@Mock
private VnfTemplateDetailsDao vnfTemplateDetailsDao;
private TemplateJoinVO template = new TemplateJoinVO(); private TemplateJoinVO template = new TemplateJoinVO();
private TemplateResponse templateResponse = new TemplateResponse(); private TemplateResponse templateResponse = new TemplateResponse();
@ -60,6 +74,8 @@ public class TemplateJoinDaoImplTest extends GenericDaoBaseWithTagInformationBas
private String domainName = "ROOT"; private String domainName = "ROOT";
private String detailName = "detail_name1"; private String detailName = "detail_name1";
private String detailValue = "detail_val"; private String detailValue = "detail_val";
private Storage.TemplateType templateType = Storage.TemplateType.VNF;
private Long templateId = 101L;
@Before @Before
public void setup() { public void setup() {
@ -68,7 +84,7 @@ public class TemplateJoinDaoImplTest extends GenericDaoBaseWithTagInformationBas
} }
@Test @Test
public void testUpdateTemplateTagInfo(){ public void testUpdateTemplateTagInfo() {
testUpdateTagInformation(_templateJoinDaoImpl, template, templateResponse); testUpdateTagInformation(_templateJoinDaoImpl, template, templateResponse);
} }
@ -89,8 +105,8 @@ public class TemplateJoinDaoImplTest extends GenericDaoBaseWithTagInformationBas
Assert.assertEquals(accountName, ReflectionTestUtils.getField(response, "account")); Assert.assertEquals(accountName, ReflectionTestUtils.getField(response, "account"));
Assert.assertEquals(domainUuid, ReflectionTestUtils.getField(response, "domainId")); Assert.assertEquals(domainUuid, ReflectionTestUtils.getField(response, "domainId"));
Assert.assertEquals(domainName, ReflectionTestUtils.getField(response, "domainName")); Assert.assertEquals(domainName, ReflectionTestUtils.getField(response, "domainName"));
Assert.assertTrue(((Map)ReflectionTestUtils.getField(response, "details")).containsKey(detailName)); Assert.assertTrue(((Map) ReflectionTestUtils.getField(response, "details")).containsKey(detailName));
Assert.assertEquals(detailValue, ((Map)ReflectionTestUtils.getField(response, "details")).get(detailName)); Assert.assertEquals(detailValue, ((Map) ReflectionTestUtils.getField(response, "details")).get(detailName));
} }
private void populateTemplateJoinVO() { private void populateTemplateJoinVO() {
@ -111,5 +127,27 @@ public class TemplateJoinDaoImplTest extends GenericDaoBaseWithTagInformationBas
ReflectionTestUtils.setField(template, "domainName", domainName); ReflectionTestUtils.setField(template, "domainName", domainName);
ReflectionTestUtils.setField(template, "detailName", detailName); ReflectionTestUtils.setField(template, "detailName", detailName);
ReflectionTestUtils.setField(template, "detailValue", detailValue); ReflectionTestUtils.setField(template, "detailValue", detailValue);
ReflectionTestUtils.setField(template, "templateType", templateType);
}
@Test
public void testNewUpdateResponseForVnf() {
ReflectionTestUtils.setField(template, "id", templateId);
ReflectionTestUtils.setField(template, "templateType", templateType);
VnfTemplateNicVO vnfNic1 = new VnfTemplateNicVO(templateId, 0L, "eth0", true, true, "first");
VnfTemplateNicVO vnfNic2 = new VnfTemplateNicVO(templateId, 1L, "eth1", true, true, "second");
Mockito.doReturn(Arrays.asList(vnfNic1, vnfNic2)).when(vnfTemplateNicDao).listByTemplateId(templateId);
VnfTemplateDetailVO detail1 = new VnfTemplateDetailVO(templateId, "name1", "value1", true);
VnfTemplateDetailVO detail2 = new VnfTemplateDetailVO(templateId, "name2", "value2", true);
VnfTemplateDetailVO detail3 = new VnfTemplateDetailVO(templateId, "name3", "value3", true);
Mockito.doReturn(Arrays.asList(detail1, detail2, detail3)).when(vnfTemplateDetailsDao).listDetails(templateId);
final TemplateResponse response = _templateJoinDaoImpl.newUpdateResponse(template);
Assert.assertTrue(response instanceof VnfTemplateResponse);
Assert.assertEquals(2, ((VnfTemplateResponse)response).getVnfNics().size());
Assert.assertEquals(3, ((VnfTemplateResponse)response).getVnfDetails().size());
} }
} }

View File

@ -17,23 +17,78 @@
package com.cloud.api.query.dao; package com.cloud.api.query.dao;
import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.storage.Storage;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.dao.UserDao;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.vm.dao.UserVmDetailsDao;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.UserVmResponse;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.EnumSet;
import static org.mockito.ArgumentMatchers.nullable;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class UserVmJoinDaoImplTest extends GenericDaoBaseWithTagInformationBaseTest<UserVmJoinVO, UserVmResponse> { public class UserVmJoinDaoImplTest extends GenericDaoBaseWithTagInformationBaseTest<UserVmJoinVO, UserVmResponse> {
@InjectMocks @InjectMocks
private UserVmJoinDaoImpl _userVmJoinDaoImpl; private UserVmJoinDaoImpl _userVmJoinDaoImpl;
@Mock
private UserDao userDao;
@Mock
private AnnotationDao annotationDao;
@Mock
private AccountManager accountMgr;
@Mock
private UserVmDetailsDao _userVmDetailsDao;
@Mock
private UserStatisticsDao userStatsDao;
@Mock
private VnfTemplateNicDao vnfTemplateNicDao;
@Mock
private VnfTemplateDetailsDao vnfTemplateDetailsDao;
private UserVmJoinVO userVm = new UserVmJoinVO(); private UserVmJoinVO userVm = new UserVmJoinVO();
private UserVmResponse userVmResponse = new UserVmResponse(); private UserVmResponse userVmResponse = new UserVmResponse();
@Mock
Account caller;
@Mock
UserVmJoinVO userVmMock;
private Long vmId = 100L;
private Long templateId = 101L;
@Before @Before
public void setup() { public void setup() {
prepareSetup(); prepareSetup();
@ -50,4 +105,51 @@ public class UserVmJoinDaoImplTest extends GenericDaoBaseWithTagInformationBaseT
testUpdateTagInformation(_userVmJoinDaoImpl, userVm, userVmResponse); testUpdateTagInformation(_userVmJoinDaoImpl, userVm, userVmResponse);
} }
private void prepareNewUserVmResponseForVnfAppliance() {
Mockito.when(userVmMock.getId()).thenReturn(vmId);
Mockito.when(userVmMock.getTemplateId()).thenReturn(templateId);
Mockito.when(userVmMock.getTemplateType()).thenReturn(Storage.TemplateType.VNF);
Mockito.when(caller.getId()).thenReturn(2L);
Mockito.when(accountMgr.isRootAdmin(nullable(Long.class))).thenReturn(true);
SearchBuilder<UserStatisticsVO> searchBuilderMock = Mockito.mock(SearchBuilder.class);
Mockito.doReturn(searchBuilderMock).when(userStatsDao).createSearchBuilder();
UserStatisticsVO userStatisticsVOMock = Mockito.mock(UserStatisticsVO.class);
Mockito.when(searchBuilderMock.entity()).thenReturn(userStatisticsVOMock);
SearchCriteria<UserStatisticsVO> searchCriteriaMock = Mockito.mock(SearchCriteria.class);
Mockito.doReturn(searchCriteriaMock).when(searchBuilderMock).create();
Mockito.doReturn(Arrays.asList()).when(userStatsDao).search(searchCriteriaMock, null);
VnfTemplateNicVO vnfNic1 = new VnfTemplateNicVO(templateId, 0L, "eth0", true, true, "first");
VnfTemplateNicVO vnfNic2 = new VnfTemplateNicVO(templateId, 1L, "eth1", true, true, "second");
Mockito.doReturn(Arrays.asList(vnfNic1, vnfNic2)).when(vnfTemplateNicDao).listByTemplateId(templateId);
VnfTemplateDetailVO detail1 = new VnfTemplateDetailVO(templateId, "name1", "value1", true);
VnfTemplateDetailVO detail2 = new VnfTemplateDetailVO(templateId, "name2", "value2", true);
VnfTemplateDetailVO detail3 = new VnfTemplateDetailVO(templateId, "name3", "value3", true);
Mockito.doReturn(Arrays.asList(detail1, detail2, detail3)).when(vnfTemplateDetailsDao).listDetails(templateId);
}
@Test
public void testNewUserVmResponseForVnfAppliance() {
prepareNewUserVmResponseForVnfAppliance();
UserVmResponse response = _userVmJoinDaoImpl.newUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock,
EnumSet.of(ApiConstants.VMDetails.all), null, null, caller);
Assert.assertEquals(2, response.getVnfNics().size());
Assert.assertEquals(3, response.getVnfDetails().size());
}
@Test
public void testNewUserVmResponseForVnfApplianceVnfNics() {
prepareNewUserVmResponseForVnfAppliance();
UserVmResponse response = _userVmJoinDaoImpl.newUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock,
EnumSet.of(ApiConstants.VMDetails.vnfnics), null, null, caller);
Assert.assertEquals(2, response.getVnfNics().size());
Assert.assertEquals(3, response.getVnfDetails().size());
}
} }

View File

@ -45,6 +45,10 @@ import javax.inject.Inject;
import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd;
import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd; import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
@ -68,6 +72,7 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.test.utils.SpringUtils; import org.apache.cloudstack.test.utils.SpringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
@ -193,6 +198,8 @@ public class TemplateManagerImplTest {
@Inject @Inject
AccountManager _accountMgr; AccountManager _accountMgr;
@Inject
VnfTemplateManager vnfTemplateManager;
public class CustomThreadPoolExecutor extends ThreadPoolExecutor { public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
AtomicInteger ai = new AtomicInteger(0); AtomicInteger ai = new AtomicInteger(0);
@ -589,6 +596,109 @@ public class TemplateManagerImplTest {
Assert.assertEquals(template, resultTemplate); Assert.assertEquals(template, resultTemplate);
} }
@Test
public void testRegisterTemplateWithTemplateType() {
RegisterTemplateCmd cmd = Mockito.mock(RegisterTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
Assert.assertEquals(Storage.TemplateType.SYSTEM, type);
}
@Test
public void testRegisterTemplateWithoutTemplateType() {
RegisterTemplateCmd cmd = Mockito.mock(RegisterTemplateCmd.class);
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
Assert.assertEquals(Storage.TemplateType.USER, type);
}
@Test(expected = InvalidParameterValueException.class)
public void testRegisterTemplateWithSystemTemplateTypeByUser() {
RegisterVnfTemplateCmd cmd = Mockito.mock(RegisterVnfTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
}
@Test(expected = InvalidParameterValueException.class)
public void testRegisterVnfTemplateWithTemplateType() {
RegisterVnfTemplateCmd cmd = Mockito.mock(RegisterVnfTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
Assert.assertEquals(Storage.TemplateType.VNF, type);
}
@Test
public void testRegisterVnfTemplateWithoutTemplateType() {
RegisterVnfTemplateCmd cmd = Mockito.mock(RegisterVnfTemplateCmd.class);
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
Assert.assertEquals(Storage.TemplateType.VNF, type);
}
@Test
public void testUpdateTemplateWithTemplateType() {
UpdateTemplateCmd cmd = Mockito.mock(UpdateTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
Assert.assertEquals(Storage.TemplateType.SYSTEM, type);
}
@Test
public void testUpdateTemplateWithoutTemplateType() {
UpdateTemplateCmd cmd = Mockito.mock(UpdateTemplateCmd.class);
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
Assert.assertNull(type);
}
@Test(expected = InvalidParameterValueException.class)
public void testUpdateTemplateWithInvalidTemplateType() {
UpdateTemplateCmd cmd = Mockito.mock(UpdateTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn("invalidtype");
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
}
@Test(expected = InvalidParameterValueException.class)
public void testUpdateTemplateWithInvalidTemplateTypeForRouting() {
UpdateTemplateCmd cmd = Mockito.mock(UpdateTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.USER.toString());
when(cmd.isRoutingType()).thenReturn(true);
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
}
@Test(expected = InvalidParameterValueException.class)
public void testUpdateTemplateWithInvalidCrossZonesForSystem() {
UpdateTemplateCmd cmd = Mockito.mock(UpdateTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, false);
}
@Test(expected = InvalidParameterValueException.class)
public void testUpdateTemplateWithSystemTemplateTypeByUser() {
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
}
@Test(expected = InvalidParameterValueException.class)
public void testUpdateVnfTemplateWithTemplateType() {
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
when(cmd.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM.toString());
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
Assert.assertEquals(Storage.TemplateType.VNF, type);
}
@Test
public void testUpdateVnfTemplateWithoutTemplateType() {
UpdateVnfTemplateCmd cmd = Mockito.mock(UpdateVnfTemplateCmd.class);
Storage.TemplateType type = templateManager.validateTemplateType(cmd, false, true);
Assert.assertNull(type);
}
@Test
public void testDeleteTemplateWithTemplateType() {
DeleteTemplateCmd cmd = new DeleteTemplateCmd();
Storage.TemplateType type = templateManager.validateTemplateType(cmd, true, true);
Assert.assertNull(type);
}
@Configuration @Configuration
@ComponentScan(basePackageClasses = {TemplateManagerImpl.class}, @ComponentScan(basePackageClasses = {TemplateManagerImpl.class},
includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
@ -790,6 +900,11 @@ public class TemplateManagerImplTest {
return Mockito.mock(HypervisorGuruManager.class); return Mockito.mock(HypervisorGuruManager.class);
} }
@Bean
public VnfTemplateManager vnfTemplateManager() {
return Mockito.mock(VnfTemplateManager.class);
}
@Bean @Bean
public SnapshotHelper snapshotHelper() { public SnapshotHelper snapshotHelper() {
return Mockito.mock(SnapshotHelper.class); return Mockito.mock(SnapshotHelper.class);

View File

@ -16,6 +16,8 @@
// under the License. // under the License.
package com.cloud.vm; package com.cloud.vm;
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
import com.cloud.configuration.Resource; import com.cloud.configuration.Resource;
import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVO;
@ -37,6 +39,7 @@ import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.NetworkModel; import com.cloud.network.NetworkModel;
import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.NetworkVO;
import com.cloud.network.security.SecurityGroupVO;
import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering;
import com.cloud.server.ManagementService; import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
@ -44,6 +47,7 @@ import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.GuestOSVO; import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ScopeType; import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume; import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiService; import com.cloud.storage.VolumeApiService;
@ -72,6 +76,7 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.UserVmDetailsDao;
import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd; import org.apache.cloudstack.api.command.user.vm.ResetVMUserDataCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
@ -79,6 +84,7 @@ import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.template.VnfTemplateManager;
import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.userdata.UserDataManager;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
@ -229,10 +235,20 @@ public class UserVmManagerImplTest {
@Mock @Mock
VirtualMachineProfile virtualMachineProfile; VirtualMachineProfile virtualMachineProfile;
@Mock
VirtualMachineTemplate templateMock;
@Mock
VnfTemplateManager vnfTemplateManager;
@Mock
ServiceOfferingJoinDao serviceOfferingJoinDao;
private static final long vmId = 1l; private static final long vmId = 1l;
private static final long zoneId = 2L; private static final long zoneId = 2L;
private static final long accountId = 3L; private static final long accountId = 3L;
private static final long serviceOfferingId = 10L; private static final long serviceOfferingId = 10L;
private static final long templateId = 11L;
private static final long GiB_TO_BYTES = 1024 * 1024 * 1024; private static final long GiB_TO_BYTES = 1024 * 1024 * 1024;
@ -941,6 +957,43 @@ public class UserVmManagerImplTest {
userVmManagerImpl.createVirtualMachine(deployVMCmd); userVmManagerImpl.createVirtualMachine(deployVMCmd);
} }
@Test
public void createVirtualMachine() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
DeployVMCmd deployVMCmd = new DeployVMCmd();
ReflectionTestUtils.setField(deployVMCmd, "zoneId", zoneId);
ReflectionTestUtils.setField(deployVMCmd, "templateId", templateId);
ReflectionTestUtils.setField(deployVMCmd, "serviceOfferingId", serviceOfferingId);
deployVMCmd._accountService = accountService;
when(accountService.finalyzeAccountId(nullable(String.class), nullable(Long.class), nullable(Long.class), eq(true))).thenReturn(accountId);
when(accountService.getActiveAccountById(accountId)).thenReturn(account);
when(entityManager.findById(DataCenter.class, zoneId)).thenReturn(_dcMock);
when(entityManager.findById(ServiceOffering.class, serviceOfferingId)).thenReturn(serviceOffering);
when(serviceOffering.getState()).thenReturn(ServiceOffering.State.Active);
when(entityManager.findById(VirtualMachineTemplate.class, templateId)).thenReturn(templateMock);
when(templateMock.getTemplateType()).thenReturn(Storage.TemplateType.VNF);
when(templateMock.isDeployAsIs()).thenReturn(false);
when(templateMock.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
when(templateMock.getUserDataId()).thenReturn(null);
Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class));
ServiceOfferingJoinVO svcOfferingMock = Mockito.mock(ServiceOfferingJoinVO.class);
when(serviceOfferingJoinDao.findById(anyLong())).thenReturn(svcOfferingMock);
when(_dcMock.isLocalStorageEnabled()).thenReturn(true);
when(_dcMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
any(), any(), any(), any(), eq(true), any());
UserVm result = userVmManagerImpl.createVirtualMachine(deployVMCmd);
assertEquals(userVmVoMock, result);
Mockito.verify(vnfTemplateManager).validateVnfApplianceNics(templateMock, null);
Mockito.verify(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
any(), any(), any(), any(), eq(true), any());
}
private List<VolumeVO> mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(int localVolumes, int nonLocalVolumes) { private List<VolumeVO> mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(int localVolumes, int nonLocalVolumes) {
List<VolumeVO> volumes = new ArrayList<>(); List<VolumeVO> volumes = new ArrayList<>();
for (int i=0; i< localVolumes + nonLocalVolumes; ++i) { for (int i=0; i< localVolumes + nonLocalVolumes; ++i) {
@ -1076,6 +1129,24 @@ public class UserVmManagerImplTest {
Mockito.verify(userVmDao).update(vmId, userVmVoMock); Mockito.verify(userVmDao).update(vmId, userVmVoMock);
} }
@Test
public void testGetSecurityGroupIdList() {
DeployVnfApplianceCmd cmd = Mockito.mock(DeployVnfApplianceCmd.class);
Mockito.doReturn(new ArrayList<Long>()).when(userVmManagerImpl).getSecurityGroupIdList(cmd);
SecurityGroupVO securityGroupVO = Mockito.mock(SecurityGroupVO.class);
long securityGroupId = 100L;
when(securityGroupVO.getId()).thenReturn(securityGroupId);
Mockito.doReturn(securityGroupVO).when(vnfTemplateManager).createSecurityGroupForVnfAppliance(any(), any(), any(), any(DeployVnfApplianceCmd.class));
List<Long> securityGroupIds = userVmManagerImpl.getSecurityGroupIdList(cmd, null, null, null);
Assert.assertEquals(1, securityGroupIds.size());
Assert.assertEquals(securityGroupId, securityGroupIds.get(0).longValue());
Mockito.verify(userVmManagerImpl).getSecurityGroupIdList(cmd);
Mockito.verify(vnfTemplateManager).createSecurityGroupForVnfAppliance(any(), any(), any(), any(DeployVnfApplianceCmd.class));
}
@Test @Test
public void getCurrentVmPasswordOrDefineNewPasswordTestTemplateIsNotPasswordEnabledReturnPreDefinedString() { public void getCurrentVmPasswordOrDefineNewPasswordTestTemplateIsNotPasswordEnabledReturnPreDefinedString() {
String expected = "saved_password"; String expected = "saved_password";

View File

@ -0,0 +1,389 @@
// 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.storage.template;
import com.cloud.dc.DataCenter;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkService;
import com.cloud.network.VNF;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.firewall.FirewallService;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.rules.RulesService;
import com.cloud.network.security.SecurityGroup;
import com.cloud.network.security.SecurityGroupManager;
import com.cloud.network.security.SecurityGroupRuleVO;
import com.cloud.network.security.SecurityGroupService;
import com.cloud.network.security.SecurityGroupVO;
import com.cloud.storage.VnfTemplateDetailVO;
import com.cloud.storage.VnfTemplateNicVO;
import com.cloud.storage.dao.VnfTemplateDetailsDao;
import com.cloud.storage.dao.VnfTemplateNicDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.vm.NicVO;
import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.api.command.user.template.RegisterVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class VnfTemplateManagerImplTest {
@Spy
@InjectMocks
VnfTemplateManagerImpl vnfTemplateManagerImpl;
@Mock
VnfTemplateDetailsDao vnfTemplateDetailsDao;
@Mock
VnfTemplateNicDao vnfTemplateNicDao;
@Mock
VirtualMachineTemplate template;
@Mock
NicDao nicDao;
@Mock
NetworkDao networkDao;
@Mock
NetworkModel networkModel;
@Mock
SecurityGroupManager securityGroupManager;
@Mock
SecurityGroupService securityGroupService;
@Mock
NetworkService networkService;
@Mock
IpAddressManager ipAddressManager;
@Mock
RulesService rulesService;
@Mock
FirewallRulesDao firewallRulesDao;
@Mock
FirewallService firewallService;
final static long templateId = 100L;
final static long vmId = 101L;
final static long networkId = 101L;
final static long securityGroupId = 102L;
final static long zoneId = 103L;
final static long publicIpId = 104L;
final static String ipAddress = "10.10.10.10";
final static Integer sshPort = 2222;
final static Integer httpPort = 8080;
final static Integer httpsPort = 8443;
final Map<String, Object> vnfNics = new HashMap<>();
final Map<String, Object> vnfDetails = new HashMap<>();
@Before
public void setUp() {
vnfNics.put("0", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "1"),
Map.entry("name", "eth1"),
Map.entry("required", "true"),
Map.entry("description", "The second NIC of VNF appliance")
)));
vnfNics.put("1", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "2"),
Map.entry("name", "eth2"),
Map.entry("required", "false"),
Map.entry("description", "The third NIC of VNF appliance")
)));
vnfNics.put("2", new HashMap<>(Map.ofEntries(
Map.entry("deviceid", "0"),
Map.entry("name", "eth0"),
Map.entry("description", "The first NIC of VNF appliance")
)));
vnfDetails.put("0", new HashMap<>(Map.ofEntries(
Map.entry("accessMethods", "console,http,https"),
Map.entry("username", "admin"),
Map.entry("password", "password"),
Map.entry("version", "4.19.0"),
Map.entry("vendor", "cloudstack")
)));
VnfTemplateNicVO vnfNic1 = new VnfTemplateNicVO(templateId, 0L, "eth0", true, true, "first");
VnfTemplateNicVO vnfNic2 = new VnfTemplateNicVO(templateId, 1L, "eth1", true, true, "second");
VnfTemplateNicVO vnfNic3 = new VnfTemplateNicVO(templateId, 2L, "eth2", false, true, "third");
Mockito.doReturn(Arrays.asList(vnfNic1, vnfNic2, vnfNic3)).when(vnfTemplateNicDao).listByTemplateId(templateId);
when(template.getId()).thenReturn(templateId);
}
@Test
public void testPersistVnfTemplateRegister() {
RegisterVnfTemplateCmd cmd = new RegisterVnfTemplateCmd();
ReflectionTestUtils.setField(cmd,"vnfNics", vnfNics);
ReflectionTestUtils.setField(cmd,"vnfDetails", vnfDetails);
vnfTemplateManagerImpl.persistVnfTemplate(templateId, cmd);
Mockito.verify(vnfTemplateNicDao, Mockito.times(vnfNics.size())).persist(any(VnfTemplateNicVO.class));
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(0)).removeDetails(templateId);
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(5)).addDetail(eq(templateId), anyString(), anyString(), eq(true));
}
@Test
public void testPersistVnfTemplateUpdate() {
UpdateVnfTemplateCmd cmd = new UpdateVnfTemplateCmd();
ReflectionTestUtils.setField(cmd,"vnfNics", vnfNics);
ReflectionTestUtils.setField(cmd,"vnfDetails", vnfDetails);
vnfTemplateManagerImpl.updateVnfTemplate(templateId, cmd);
Mockito.verify(vnfTemplateNicDao, Mockito.times(vnfNics.size())).persist(any(VnfTemplateNicVO.class));
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(1)).removeDetails(templateId);
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(5)).addDetail(eq(templateId), anyString(), anyString(), eq(true));
}
@Test
public void testPersistVnfTemplateUpdateWithoutNics() {
UpdateVnfTemplateCmd cmd = new UpdateVnfTemplateCmd();
ReflectionTestUtils.setField(cmd,"vnfDetails", vnfDetails);
ReflectionTestUtils.setField(cmd,"cleanupVnfNics", true);
vnfTemplateManagerImpl.updateVnfTemplate(templateId, cmd);
Mockito.verify(vnfTemplateNicDao, Mockito.times(1)).deleteByTemplateId(templateId);
Mockito.verify(vnfTemplateNicDao, Mockito.times(0)).persist(any(VnfTemplateNicVO.class));
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(1)).removeDetails(templateId);
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(5)).addDetail(eq(templateId), anyString(), anyString(), eq(true));
}
@Test
public void testPersistVnfTemplateUpdateWithoutDetails() {
UpdateVnfTemplateCmd cmd = new UpdateVnfTemplateCmd();
ReflectionTestUtils.setField(cmd,"vnfNics", vnfNics);
ReflectionTestUtils.setField(cmd,"cleanupVnfDetails", true);
vnfTemplateManagerImpl.updateVnfTemplate(templateId, cmd);
Mockito.verify(vnfTemplateNicDao, Mockito.times(vnfNics.size())).persist(any(VnfTemplateNicVO.class));
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(1)).removeDetails(templateId);
Mockito.verify(vnfTemplateDetailsDao, Mockito.times(0)).addDetail(eq(templateId), anyString(), anyString(), eq(true));
}
@Test
public void testValidateVnfApplianceNicsWithRequiredNics() {
List<Long> networkIds = Arrays.asList(200L, 201L);
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
}
@Test
public void testValidateVnfApplianceNicsWithAllNics() {
List<Long> networkIds = Arrays.asList(200L, 201L, 202L);
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfApplianceNicsWithEmptyList() {
List<Long> networkIds = new ArrayList<>();
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateVnfApplianceNicsWithMissingNetworkId() {
List<Long> networkIds = Arrays.asList(200L);
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
}
@Test
public void testGetManagementNetworkAndIp() {
when(template.getId()).thenReturn(templateId);
VnfTemplateNicVO vnfNic1 = new VnfTemplateNicVO(templateId, 0L, "eth0", true, true, "first");
VnfTemplateNicVO vnfNic2 = new VnfTemplateNicVO(templateId, 1L, "eth1", true, false, "second");
VnfTemplateNicVO vnfNic3 = new VnfTemplateNicVO(templateId, 2L, "eth2", false, false, "third");
Mockito.doReturn(Arrays.asList(vnfNic1, vnfNic2, vnfNic3)).when(vnfTemplateNicDao).listByTemplateId(templateId);
UserVm vm = Mockito.mock(UserVm.class);
when(vm.getId()).thenReturn(vmId);
NicVO nic1 = Mockito.mock(NicVO.class);
NicVO nic2 = Mockito.mock(NicVO.class);
NicVO nic3 = Mockito.mock(NicVO.class);
when(nic1.getDeviceId()).thenReturn(0);
when(nic1.getIPv4Address()).thenReturn(ipAddress);
when(nic1.getNetworkId()).thenReturn(networkId);
when(nic2.getDeviceId()).thenReturn(1);
when(nic3.getDeviceId()).thenReturn(2);
Mockito.doReturn(Arrays.asList(nic1, nic2, nic3)).when(nicDao).listByVmId(vmId);
NetworkVO network = Mockito.mock(NetworkVO.class);
when(network.getId()).thenReturn(networkId);
when(network.getGuestType()).thenReturn(Network.GuestType.Isolated);
when(network.getVpcId()).thenReturn(null);
Mockito.doReturn(network).when(networkDao).findById(networkId);
when(networkModel.areServicesSupportedInNetwork(networkId, Network.Service.StaticNat)).thenReturn(true);
when(networkModel.areServicesSupportedInNetwork(networkId, Network.Service.Firewall)).thenReturn(true);
Map<Network, String> networkAndIpMap = vnfTemplateManagerImpl.getManagementNetworkAndIp(template, vm);
Assert.assertEquals(1, networkAndIpMap.size());
Assert.assertTrue(networkAndIpMap.containsKey(network));
Assert.assertTrue(networkAndIpMap.containsValue(ipAddress));
}
@Test
public void testGetOpenPortsForVnfAppliance() {
when(template.getId()).thenReturn(templateId);
VnfTemplateDetailVO accessMethodsDetail = Mockito.mock(VnfTemplateDetailVO.class);
when(accessMethodsDetail.getValue()).thenReturn("console,ssh-password,http,https");
when(vnfTemplateDetailsDao.findDetail(templateId, VNF.AccessDetail.ACCESS_METHODS.name().toLowerCase())).thenReturn(accessMethodsDetail);
VnfTemplateDetailVO sshPortDetail = Mockito.mock(VnfTemplateDetailVO.class);
when(sshPortDetail.getValue()).thenReturn(String.valueOf(sshPort));
when(vnfTemplateDetailsDao.findDetail(templateId, VNF.AccessDetail.SSH_PORT.name().toLowerCase())).thenReturn(sshPortDetail);
VnfTemplateDetailVO httpPortDetail = Mockito.mock(VnfTemplateDetailVO.class);
when(httpPortDetail.getValue()).thenReturn(String.valueOf(httpPort));
when(vnfTemplateDetailsDao.findDetail(templateId, VNF.AccessDetail.HTTP_PORT.name().toLowerCase())).thenReturn(httpPortDetail);
VnfTemplateDetailVO httpsPortDetail = Mockito.mock(VnfTemplateDetailVO.class);
when(httpsPortDetail.getValue()).thenReturn(String.valueOf(httpsPort));
when(vnfTemplateDetailsDao.findDetail(templateId, VNF.AccessDetail.HTTPS_PORT.name().toLowerCase())).thenReturn(httpsPortDetail);
Set<Integer> ports = vnfTemplateManagerImpl.getOpenPortsForVnfAppliance(template);
Assert.assertEquals(3, ports.size());
Assert.assertTrue(ports.contains(sshPort));
Assert.assertTrue(ports.contains(httpPort));
Assert.assertTrue(ports.contains(httpsPort));
}
@Test
public void testCreateSecurityGroupForVnfAppliance() {
DataCenter zone = Mockito.mock(DataCenter.class);
when(zone.isSecurityGroupEnabled()).thenReturn(true);
DeployVnfApplianceCmd cmd = Mockito.mock(DeployVnfApplianceCmd.class);
when(cmd.getVnfConfigureManagement()).thenReturn(true);
when(cmd.getVnfCidrlist()).thenReturn(Arrays.asList("0.0.0.0/0"));
Set<Integer> ports = new HashSet<>();
ports.add(sshPort);
ports.add(httpPort);
ports.add(httpsPort);
Mockito.doReturn(ports).when(vnfTemplateManagerImpl).getOpenPortsForVnfAppliance(template);
Account owner = Mockito.mock(Account.class);
when(owner.getDomainId()).thenReturn(1L);
when(owner.getAccountName()).thenReturn("admin");
SecurityGroupVO securityGroupVO = Mockito.mock(SecurityGroupVO.class);
when(securityGroupVO.getId()).thenReturn(securityGroupId);
Mockito.doReturn(securityGroupVO).when(securityGroupManager).createSecurityGroup(anyString(), anyString(), anyLong(), anyLong(), anyString());
SecurityGroupRuleVO securityGroupRuleVO = Mockito.mock(SecurityGroupRuleVO.class);
Mockito.doReturn(Arrays.asList(securityGroupRuleVO)).when(securityGroupService).authorizeSecurityGroupRule(anyLong(), anyString(), anyInt(), anyInt(),
any(), any(), any(), any(), any());
SecurityGroup result = vnfTemplateManagerImpl.createSecurityGroupForVnfAppliance(zone, template, owner, cmd);
Assert.assertEquals(result, securityGroupVO);
Mockito.verify(securityGroupService, Mockito.times(3)).authorizeSecurityGroupRule(anyLong(), anyString(), anyInt(), anyInt(),
any(), any(), any(), any(), any());
}
@Test
public void testCreateIsolatedNetworkRulesForVnfAppliance() throws InsufficientAddressCapacityException, ResourceUnavailableException,
ResourceAllocationException, NetworkRuleConflictException {
DataCenter zone = Mockito.mock(DataCenter.class);
when(zone.getId()).thenReturn(zoneId);
Account owner = Mockito.mock(Account.class);
UserVm vm = Mockito.mock(UserVm.class);
when(vm.getId()).thenReturn(vmId);
DeployVnfApplianceCmd cmd = Mockito.mock(DeployVnfApplianceCmd.class);
Map<Network, String> networkAndIpMap = new HashMap<>();
NetworkVO network = Mockito.mock(NetworkVO.class);
when(network.getId()).thenReturn(networkId);
when(network.getVpcId()).thenReturn(null);
networkAndIpMap.put(network, ipAddress);
Mockito.doReturn(networkAndIpMap).when(vnfTemplateManagerImpl).getManagementNetworkAndIp(template, vm);
Set<Integer> ports = new HashSet<>();
ports.add(sshPort);
ports.add(httpPort);
ports.add(httpsPort);
Mockito.doReturn(ports).when(vnfTemplateManagerImpl).getOpenPortsForVnfAppliance(template);
FirewallRuleVO firewallRuleVO = Mockito.mock(FirewallRuleVO.class);
IPAddressVO publicIp = Mockito.mock(IPAddressVO.class);
when(publicIp.getId()).thenReturn(publicIpId);
when(publicIp.isSourceNat()).thenReturn(true).thenReturn(false);
Mockito.doReturn(publicIp).when(networkService).allocateIP(owner, zoneId, networkId, null, null);
Mockito.doReturn(publicIp).when(ipAddressManager).associateIPToGuestNetwork(publicIpId, networkId, false);
Mockito.doReturn(true).when(rulesService).enableStaticNat(publicIpId, vmId, networkId, ipAddress);
when(firewallRulesDao.persist(any())).thenReturn(firewallRuleVO);
Mockito.doReturn(true).when(firewallService).applyIngressFwRules(publicIpId, owner);
vnfTemplateManagerImpl.createIsolatedNetworkRulesForVnfAppliance(zone, template, owner, vm, cmd);
Mockito.verify(networkService, Mockito.times(2)).allocateIP(owner, zoneId, networkId, null, null);
Mockito.verify(ipAddressManager, Mockito.times(2)).associateIPToGuestNetwork(publicIpId, networkId, false);
Mockito.verify(rulesService, Mockito.times(1)).enableStaticNat(publicIpId, vmId, networkId, ipAddress);
Mockito.verify(firewallRulesDao, Mockito.times(3)).persist(any());
Mockito.verify(firewallService, Mockito.times(1)).applyIngressFwRules(publicIpId, owner);
}
}

View File

@ -0,0 +1,341 @@
# 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.
""" Smoke tests for VNF templates/appliances
"""
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import (Account,
Domain,
Configurations,
ServiceOffering,
VirtualMachine,
Network,
NetworkOffering,
VnfAppliance,
VnfTemplate,
Zone)
from marvin.lib.common import get_zone, get_template
from nose.plugins.attrib import attr
import time
VNF_NICS = [{"deviceid": "0", "name": "WAN", "required": "true", "description": "Public WAN"},
{"deviceid": "1", "name": "LAN-1", "required": "true", "description": "Private LAN-1"}]
NEW_VNF_NICS = [{"deviceid": "0", "name": "WAN", "required": "true", "description": "Public WAN"},
{"deviceid": "1", "name": "LAN-1", "required": "true", "description": "Private LAN-1"},
{"deviceid": "2", "name": "LAN-2", "required": "false", "description": "Private LAN-2"}]
VNF_DETAILS = [{"access_methods": "console,https,http", "username": "root"}]
NEW_VNF_DETAILS = [{"access_methods": "console,https,http", "username": "root", "password": "cloudstack"}]
class TestVnfTemplates(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestVnfTemplates, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls._cleanup = []
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.hypervisor = cls.testClient.getHypervisorInfo()
zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
cls.zone = Zone(zone.__dict__)
cls.template = get_template(cls.apiclient, cls.zone.id)
cls.domain = Domain.create(
cls.apiclient,
cls.services["domain"]
)
cls._cleanup.append(cls.domain)
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
admin=True,
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.user = cls.account.user[0]
cls.user_apiclient = cls.testClient.getUserApiClient(
cls.user.username, cls.domain.name
)
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["big"]
)
cls._cleanup.append(cls.service_offering)
cls.vnf_template_config = {
"name": "pfsense",
"displaytext": "pfsense",
"format": cls.template.format,
"url": cls.template.url,
"requireshvm": "True",
"ispublic": "True",
"isextractable": "True",
"hypervisor": cls.hypervisor,
"zoneid": cls.zone.id,
"ostype": "FreeBSD 12 (64-bit)",
"directdownload": False
}
cls.initial_setting = Configurations.list(
cls.apiclient,
name="vnf.template.appliance.enabled")[0].value
Configurations.update(cls.apiclient, "vnf.template.appliance.enabled", "true")
cls.vnf_templates = []
@classmethod
def tearDownClass(cls):
Configurations.update(cls.apiclient, "vnf.template.appliance.enabled", cls.initial_setting)
if len(cls.vnf_templates) > 0:
for vnf_template in cls.vnf_templates:
vnf_template.delete(cls.user_apiclient)
super(TestVnfTemplates, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
def tearDown(self):
super(TestVnfTemplates, self).tearDown()
def ensureVnfTemplateExists(self):
if len(self.vnf_templates) == 0:
self.vnf_template = VnfTemplate.register(self.user_apiclient,
self.vnf_template_config,
zoneid=self.zone.id,
hypervisor=self.hypervisor,
vnfnics=VNF_NICS,
vnfdetails=VNF_DETAILS)
self.vnf_templates.append(self.vnf_template)
else:
self.vnf_template = self.vnf_templates[0]
def ensureVnfTemplateDownloaded(self):
"""Check if template download will finish in 5 minutes"""
retries = 30
interval = 10
while retries > -1:
time.sleep(interval)
templates_response = VnfTemplate.list(
self.user_apiclient,
id=self.vnf_template.id,
zoneid=self.zone.id,
templatefilter='self'
)
template = templates_response[0]
if not hasattr(template, 'status') or not template or not template.status:
retries = retries - 1
continue
if 'Failed' in template.status:
raise Exception(
"Failed to download template: status - %s" %
template.status)
elif template.status == 'Download Complete' and template.isready:
return
elif 'Downloaded' in template.status:
retries = retries - 1
continue
elif 'Installing' not in template.status:
if retries >= 0:
retries = retries - 1
continue
raise Exception(
"Error in downloading template: status - %s" %
template.status)
else:
retries = retries - 1
raise Exception("Template download failed exception.")
@attr(tags=["advanced"], required_hardware="false")
def test_01_register_vnf_template(self):
"""Test register VNF template
"""
self.ensureVnfTemplateExists()
@attr(tags=["advanced"], required_hardware="false")
def test_02_list_vnf_template(self):
"""Test list VNF template
"""
self.ensureVnfTemplateExists()
templates_response = VnfTemplate.list(
self.user_apiclient,
id=self.vnf_template.id,
zoneid=self.zone.id,
templatefilter='self'
)
if isinstance(templates_response, list) and len(templates_response) > 0:
template = templates_response[0]
self.assertEqual("VNF", template.templatetype,
"The template type of VNF template should be VNF but actually it is %s" % template.templatetype)
self.assertTrue(isinstance(template.vnfnics, list), "The template vnfnics must be a list")
self.assertEqual(2, len(template.vnfnics), "The VNF template should have 2 VNF nics")
self.assertEqual(2, len(template.vnfdetails.__dict__), "The VNF template should have 2 VNF details")
else:
self.fail("Failed to get VNF templates by listVnfTemplates API")
@attr(tags=["advanced"], required_hardware="false")
def test_03_edit_vnf_template(self):
"""Test edit VNF template
"""
self.ensureVnfTemplateExists()
self.vnf_template.update(
self.user_apiclient,
id=self.vnf_template.id,
vnfnics=NEW_VNF_NICS,
vnfdetails=NEW_VNF_DETAILS
)
templates_response = VnfTemplate.list(
self.user_apiclient,
id=self.vnf_template.id,
zoneid=self.zone.id,
templatefilter='self'
)
if isinstance(templates_response, list) and len(templates_response) > 0:
template = templates_response[0]
self.assertEqual("VNF", template.templatetype,
"The template type of VNF template should be VNF but actually it is %s" % template.templatetype)
self.assertEqual(3, len(template.vnfnics), "The VNF template should have 2 VNF nics")
self.assertEqual(3, len(template.vnfdetails.__dict__), "The VNF template should have 3 VNF details")
else:
self.fail("Failed to get VNF templates by listVnfTemplates API")
@attr(tags=["advanced"], required_hardware="false")
def test_04_deploy_vnf_appliance(self):
"""Test deploy VNF appliance
"""
self.ensureVnfTemplateExists()
self.ensureVnfTemplateDownloaded()
templates_response = VnfTemplate.list(
self.user_apiclient,
id=self.vnf_template.id,
zoneid=self.zone.id,
templatefilter='self'
)
if isinstance(templates_response, list) and len(templates_response) > 0:
template = templates_response[0]
if not template.isready:
self.fail("VNF template is not Ready")
else:
self.fail("Failed to find VNF template")
# Create network offerings
self.isolated_network_offering = NetworkOffering.create(
self.apiclient,
self.services["isolated_network_offering"])
self.cleanup.append(self.isolated_network_offering)
self.isolated_network_offering.update(
self.apiclient,
state='Enabled')
self.l2_network_offering = NetworkOffering.create(
self.apiclient,
self.services["l2-network_offering"])
self.cleanup.append(self.l2_network_offering)
self.l2_network_offering.update(
self.apiclient,
state='Enabled')
# Create networks
isolated_network = Network.create(
self.user_apiclient,
self.services["network"],
networkofferingid=self.isolated_network_offering.id,
zoneid=self.zone.id
)
self.cleanup.append(isolated_network)
l2_network_1 = Network.create(
self.user_apiclient,
self.services["l2-network"],
networkofferingid=self.l2_network_offering.id,
zoneid=self.zone.id
)
self.cleanup.append(l2_network_1)
l2_network_2 = Network.create(
self.user_apiclient,
self.services["l2-network"],
networkofferingid=self.l2_network_offering.id,
zoneid=self.zone.id
)
self.cleanup.append(l2_network_2)
# failed deployment
try:
self.virtual_machine = VirtualMachine.create(
self.user_apiclient,
self.services["virtual_machine"],
zoneid=self.zone.id,
templateid=self.vnf_template.id,
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
networkids=[isolated_network.id]
)
self.cleanup.append(self.virtual_machine)
self.fail("The deployment should fail")
except Exception as e:
pass
# success deployment
self.vnf_appliance = VnfAppliance.create(
self.user_apiclient,
self.services["virtual_machine"],
zoneid=self.zone.id,
templateid=self.vnf_template.id,
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
networkids=[isolated_network.id, l2_network_1.id, l2_network_2.id],
vnfconfiguremanagement='true'
)
self.cleanup.append(self.vnf_appliance)
@attr(tags=["advanced"], required_hardware="false")
def test_05_delete_vnf_template(self):
"""Test delete VNF template
"""
self.ensureVnfTemplateExists()
self.vnf_template.delete(self.user_apiclient)
templates_response = VnfTemplate.list(
self.user_apiclient,
id=self.vnf_template.id,
zoneid=self.zone.id,
templatefilter='self'
)
self.assertIsNone(templates_response, "The VNF template should be removed")
self.vnf_templates.remove(self.vnf_template)

View File

@ -50,6 +50,7 @@ known_categories = {
'SystemVm': 'System VM', 'SystemVm': 'System VM',
'VirtualMachine': 'Virtual Machine', 'VirtualMachine': 'Virtual Machine',
'VM': 'Virtual Machine', 'VM': 'Virtual Machine',
'Vnf': 'Virtual Network Functions',
'Domain': 'Domain', 'Domain': 'Domain',
'Template': 'Template', 'Template': 'Template',
'Iso': 'ISO', 'Iso': 'ISO',

View File

@ -6731,3 +6731,315 @@ class VMSchedule:
cmd.id = self.id cmd.id = self.id
cmd.virtualmachineid = self.virtualmachineid cmd.virtualmachineid = self.virtualmachineid
return (apiclient.deleteVMSchedule(cmd)) return (apiclient.deleteVMSchedule(cmd))
class VnfTemplate:
"""Manage VNF template life cycle"""
def __init__(self, items):
self.__dict__.update(items)
@classmethod
def register(cls, apiclient, services, zoneid=None,
account=None, domainid=None, hypervisor=None,
projectid=None, details=None, randomize_name=True,
vnfnics=None, vnfdetails=None):
"""Create VNF template from URL"""
# Create template from Virtual machine and Volume ID
cmd = registerVnfTemplate.registerVnfTemplateCmd()
cmd.displaytext = services["displaytext"]
if randomize_name:
cmd.name = "-".join([services["name"], random_gen()])
else:
cmd.name = services["name"]
cmd.format = services["format"]
if hypervisor:
cmd.hypervisor = hypervisor
elif "hypervisor" in services:
cmd.hypervisor = services["hypervisor"]
if "ostypeid" in services:
cmd.ostypeid = services["ostypeid"]
elif "ostype" in services:
# Find OSTypeId from Os type
sub_cmd = listOsTypes.listOsTypesCmd()
sub_cmd.description = services["ostype"]
ostypes = apiclient.listOsTypes(sub_cmd)
if not isinstance(ostypes, list):
raise Exception(
"Unable to find Ostype id with desc: %s" %
services["ostype"])
cmd.ostypeid = ostypes[0].id
else:
raise Exception(
"Unable to find Ostype is required for registering template")
cmd.url = services["url"]
if zoneid:
cmd.zoneid = zoneid
else:
cmd.zoneid = services["zoneid"]
cmd.isfeatured = services[
"isfeatured"] if "isfeatured" in services else False
cmd.ispublic = services[
"ispublic"] if "ispublic" in services else False
cmd.isextractable = services[
"isextractable"] if "isextractable" in services else False
cmd.isdynamicallyscalable = services["isdynamicallyscalable"] if "isdynamicallyscalable" in services else False
cmd.passwordenabled = services[
"passwordenabled"] if "passwordenabled" in services else False
cmd.deployasis = services["deployasis"] if "deployasis" in services else False
if account:
cmd.account = account
if domainid:
cmd.domainid = domainid
if projectid:
cmd.projectid = projectid
elif "projectid" in services:
cmd.projectid = services["projectid"]
if details:
cmd.details = details
if "directdownload" in services:
cmd.directdownload = services["directdownload"]
if vnfnics:
cmd.vnfnics = vnfnics
if vnfdetails:
cmd.vnfdetails = vnfdetails
# Register Template
template = apiclient.registerVnfTemplate(cmd)
if isinstance(template, list):
return VnfTemplate(template[0].__dict__)
def delete(self, apiclient, zoneid=None):
"""Delete VNF Template"""
cmd = deleteVnfTemplate.deleteVnfTemplateCmd()
cmd.id = self.id
if zoneid:
cmd.zoneid = zoneid
apiclient.deleteVnfTemplate(cmd)
def update(self, apiclient, **kwargs):
"""Updates the template details"""
cmd = updateVnfTemplate.updateVnfTemplateCmd()
cmd.id = self.id
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
return (apiclient.updateVnfTemplate(cmd))
@classmethod
def list(cls, apiclient, **kwargs):
"""List all templates matching criteria"""
cmd = listVnfTemplates.listVnfTemplatesCmd()
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
cmd.listall = True
return (apiclient.listVnfTemplates(cmd))
class VnfAppliance:
"""Manage VNF Appliance life cycle"""
def __init__(self, items):
self.__dict__.update(items)
@classmethod
def create(cls, apiclient, services, templateid=None, accountid=None,
domainid=None, zoneid=None, networkids=None,
serviceofferingid=None, securitygroupids=None,
projectid=None, startvm=None, diskofferingid=None,
affinitygroupnames=None, affinitygroupids=None, group=None,
hostid=None, clusterid=None, keypair=None, ipaddress=None, mode='default',
method='GET', hypervisor=None, customcpunumber=None,
customcpuspeed=None, custommemory=None, rootdisksize=None,
rootdiskcontroller=None, vpcid=None, macaddress=None, datadisktemplate_diskoffering_list={},
properties=None, nicnetworklist=None, bootmode=None, boottype=None, dynamicscalingenabled=None,
userdataid=None, userdatadetails=None, extraconfig=None,
vnfconfiguremanagement=None, vnfcidrlist=None):
"""Create the VNF appliance"""
cmd = deployVnfAppliance.deployVnfApplianceCmd()
if serviceofferingid:
cmd.serviceofferingid = serviceofferingid
elif "serviceoffering" in services:
cmd.serviceofferingid = services["serviceoffering"]
if zoneid:
cmd.zoneid = zoneid
elif "zoneid" in services:
cmd.zoneid = services["zoneid"]
if hypervisor:
cmd.hypervisor = hypervisor
if "displayname" in services:
cmd.displayname = services["displayname"]
if "name" in services:
cmd.name = services["name"]
if accountid:
cmd.account = accountid
elif "account" in services:
cmd.account = services["account"]
if domainid:
cmd.domainid = domainid
elif "domainid" in services:
cmd.domainid = services["domainid"]
if networkids:
cmd.networkids = networkids
allow_egress = False
elif "networkids" in services:
cmd.networkids = services["networkids"]
allow_egress = False
else:
# When no networkids are passed, network
# is created using the "defaultOfferingWithSourceNAT"
# which has an egress policy of DENY. But guests in tests
# need access to test network connectivity
allow_egress = True
if templateid:
cmd.templateid = templateid
elif "template" in services:
cmd.templateid = services["template"]
if diskofferingid:
cmd.diskofferingid = diskofferingid
elif "diskoffering" in services:
cmd.diskofferingid = services["diskoffering"]
if keypair:
cmd.keypair = keypair
elif "keypair" in services:
cmd.keypair = services["keypair"]
if ipaddress:
cmd.ipaddress = ipaddress
elif "ipaddress" in services:
cmd.ipaddress = services["ipaddress"]
if securitygroupids:
cmd.securitygroupids = [str(sg_id) for sg_id in securitygroupids]
if "affinitygroupnames" in services:
cmd.affinitygroupnames = services["affinitygroupnames"]
elif affinitygroupnames:
cmd.affinitygroupnames = affinitygroupnames
if affinitygroupids:
cmd.affinitygroupids = affinitygroupids
if projectid:
cmd.projectid = projectid
if startvm is not None:
cmd.startvm = startvm
if hostid:
cmd.hostid = hostid
if clusterid:
cmd.clusterid = clusterid
if "userdata" in services:
cmd.userdata = base64.urlsafe_b64encode(services["userdata"].encode()).decode()
if userdataid is not None:
cmd.userdataid = userdataid
if userdatadetails is not None:
cmd.userdatadetails = userdatadetails
if "dhcpoptionsnetworklist" in services:
cmd.dhcpoptionsnetworklist = services["dhcpoptionsnetworklist"]
if dynamicscalingenabled is not None:
cmd.dynamicscalingenabled = dynamicscalingenabled
cmd.details = [{}]
if customcpunumber:
cmd.details[0]["cpuNumber"] = customcpunumber
if customcpuspeed:
cmd.details[0]["cpuSpeed"] = customcpuspeed
if custommemory:
cmd.details[0]["memory"] = custommemory
if not rootdisksize is None and rootdisksize >= 0:
cmd.details[0]["rootdisksize"] = rootdisksize
if rootdiskcontroller:
cmd.details[0]["rootDiskController"] = rootdiskcontroller
if "size" in services:
cmd.size = services["size"]
if group:
cmd.group = group
cmd.datadisktemplatetodiskofferinglist = []
for datadisktemplate, diskoffering in list(datadisktemplate_diskoffering_list.items()):
cmd.datadisktemplatetodiskofferinglist.append({
'datadisktemplateid': datadisktemplate,
'diskofferingid': diskoffering
})
# program default access to ssh
if mode.lower() == 'basic':
cls.ssh_access_group(apiclient, cmd)
if macaddress:
cmd.macaddress = macaddress
elif macaddress in services:
cmd.macaddress = services["macaddress"]
if properties:
cmd.properties = properties
if nicnetworklist:
cmd.nicnetworklist = nicnetworklist
if bootmode:
cmd.bootmode = bootmode
if boottype:
cmd.boottype = boottype
if extraconfig:
cmd.extraconfig = extraconfig
if vnfconfiguremanagement:
cmd.vnfconfiguremanagement = vnfconfiguremanagement
if vnfcidrlist:
cmd.vnfcidrlist = vnfcidrlist
vnf_app = apiclient.deployVnfAppliance(cmd, method=method)
return VnfAppliance(vnf_app.__dict__)
def delete(self, apiclient, expunge=True, **kwargs):
"""Destroy an VNF appliance"""
cmd = destroyVirtualMachine.destroyVirtualMachineCmd()
cmd.id = self.id
cmd.expunge = expunge
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
apiclient.destroyVirtualMachine(cmd)

View File

@ -372,6 +372,7 @@
"label.back": "Back", "label.back": "Back",
"label.backup": "Backups", "label.backup": "Backups",
"label.backup.attach.restore": "Restore and attach backup volume", "label.backup.attach.restore": "Restore and attach backup volume",
"label.backup.configure.schedule": "Configure Backup Schedule",
"label.backup.offering.assign": "Assign VM to backup offering", "label.backup.offering.assign": "Assign VM to backup offering",
"label.backup.offering.remove": "Remove VM from backup offering", "label.backup.offering.remove": "Remove VM from backup offering",
"label.backup.offerings": "Backup offerings", "label.backup.offerings": "Backup offerings",
@ -1151,6 +1152,8 @@
"label.launch": "Launch", "label.launch": "Launch",
"label.launch.vm": "Launch instance", "label.launch.vm": "Launch instance",
"label.launch.vm.and.stay": "Launch instance & stay on this page", "label.launch.vm.and.stay": "Launch instance & stay on this page",
"label.launch.vnf.appliance": "Launch VNF appliance",
"label.launch.vnf.appliance.and.stay": "Launch VNF appliance & stay on this page",
"label.launch.zone": "Launch zone", "label.launch.zone": "Launch zone",
"label.lb.algorithm.leastconn": "Least connections", "label.lb.algorithm.leastconn": "Least connections",
"label.lb.algorithm.roundrobin": "Round-robin", "label.lb.algorithm.roundrobin": "Round-robin",
@ -1460,6 +1463,7 @@
"label.parentname": "Parent", "label.parentname": "Parent",
"label.passive": "Passive", "label.passive": "Passive",
"label.password": "Password", "label.password": "Password",
"label.password.default": "Default Password",
"label.password.reset.confirm": "Password has been reset to ", "label.password.reset.confirm": "Password has been reset to ",
"label.passwordenabled": "Password enabled", "label.passwordenabled": "Password enabled",
"label.path": "Path", "label.path": "Path",
@ -2191,6 +2195,42 @@
"label.vmwaredcname": "VMware datacenter name", "label.vmwaredcname": "VMware datacenter name",
"label.vmwaredcvcenter": "VMware datacenter vCenter", "label.vmwaredcvcenter": "VMware datacenter vCenter",
"label.vmwarenetworklabel": "VMware traffic label", "label.vmwarenetworklabel": "VMware traffic label",
"label.vnf.appliance": "VNF Appliance",
"label.vnf.appliances": "VNF appliances",
"label.vnf.appliance.add": "Add VNF Appliance",
"label.vnf.appliance.access.methods": "Management access information of this VNF appliance",
"label.vnf.app.action.destroy": "Destroy VNF appliance",
"label.vnf.app.action.edit": "Edit VNF appliance",
"label.vnf.app.action.expunge": "Expunge VNF appliance",
"label.vnf.app.action.migrate.to.host": "Migrate VNF appliance to another host",
"label.vnf.app.action.migrate.to.ps": "Migrate VNF appliance to another primary storage",
"label.vnf.app.action.recover": "Recover VNF appliance",
"label.vnf.app.action.scale": "Scale VNF appliance",
"label.vnf.app.action.start": "Start VNF appliance",
"label.vnf.app.action.stop": "Stop VNF appliance",
"label.vnf.app.action.reboot": "Reboot VNF appliance",
"label.vnf.app.action.reinstall": "Reinstall VNF appliance",
"label.vnf.cidr.list": "Source cidr list of rules",
"label.vnf.cidr.list.tooltip": "the CIDR list to forward traffic from to the VNF management interface. Multiple entries must be separated by a single comma character (,). The default value is 0.0.0.0/0.",
"label.vnf.configure.management": "Configure rules for VNF management interfaces",
"label.vnf.configure.management.tooltip": "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise.",
"label.vnf.detail.add": "Add VNF detail",
"label.vnf.detail.remove": "Remove VNF detail",
"label.vnf.details": "VNF details",
"label.vnf.nic.add": "Add VNF nic",
"label.vnf.nic.delete": "Delete VNF nic",
"label.vnf.nic.description": "Description of VNF nic",
"label.vnf.nic.deviceid": "Device ID of VNF nic. It starts with 0",
"label.vnf.nic.edit": "Edit VNF nic",
"label.vnf.nic.management": "Management NIC",
"label.vnf.nic.management.description": "True if the VNF nic is a management interface. False otherwise",
"label.vnf.nic.name": "Name of VNF nic",
"label.vnf.nic.remove": "Remove VNF nic",
"label.vnf.nic.required": "True if VNF nic is required. Otherwise optional",
"label.vnf.nics": "VNF nics",
"label.vnf.settings": "VNF settings",
"label.vnf.templates": "VNF templates",
"label.vnf.template.register": "Register VNF template",
"label.vnmc": "VNMC", "label.vnmc": "VNMC",
"label.volgroup": "Volume group", "label.volgroup": "Volume group",
"label.volume": "Volume", "label.volume": "Volume",
@ -3145,6 +3185,17 @@
"message.vm.state.stopped": "VM is stopped.", "message.vm.state.stopped": "VM is stopped.",
"message.vm.state.stopping": "VM is being stopped.", "message.vm.state.stopping": "VM is being stopped.",
"message.vm.state.unknown": "VM state is unknown.", "message.vm.state.unknown": "VM state is unknown.",
"message.vnf.appliance.networks": "Please select networks for the new VNF appliance.",
"message.vnf.credentials.change": "Please change the password(s) of the VNF appliance immediately.",
"message.vnf.credentials.default": "The default credentials(s) of the VNF appliance",
"message.vnf.credentials.in.template.vnf.details": "Please find the default credentials for this VNF in the details of the VNF template.",
"message.vnf.error.deviceid.should.be.continuous": "The deviceid of selected VNF nics should be continuous.",
"message.vnf.error.network.is.already.used": "Network has been used by multiple nics of the new VNF appliance.",
"message.vnf.error.no.networks": "Please select networks for nics of the new VNF appliance.",
"message.vnf.error.no.network.for.required.deviceid": "Please select a network for required nic of the new VNF appliance.",
"message.vnf.nic.move.up.fail": "Failed to move up this NIC",
"message.vnf.nic.move.down.fail": "Failed to move down this NIC",
"message.vnf.select.networks": "Please select a network for each VNF nic. ",
"message.volume.state.allocated": "The volume is allocated but has not been created yet.", "message.volume.state.allocated": "The volume is allocated but has not been created yet.",
"message.volume.state.attaching": "The volume is attaching to a volume from Ready state.", "message.volume.state.attaching": "The volume is attaching to a volume from Ready state.",
"message.volume.state.copying": "The volume is being copied from the image store to primary storage, in case it's an uploaded volume.", "message.volume.state.copying": "The volume is being copied from the image store to primary storage, in case it's an uploaded volume.",

View File

@ -28,6 +28,11 @@
<p v-html="ip6routes" /> <p v-html="ip6routes" />
</template> </template>
</a-alert> </a-alert>
<a-alert v-if="vnfAccessMethods" type="info" :showIcon="true" :message="$t('label.vnf.appliance.access.methods')">
<template #description>
<p v-html="vnfAccessMethods" />
</template>
</a-alert>
<a-list <a-list
size="small" size="small"
:dataSource="fetchDetails()"> :dataSource="fetchDetails()">
@ -159,11 +164,99 @@ export default {
customDisplayItems () { customDisplayItems () {
return ['ip6routes', 'privatemtu', 'publicmtu'] return ['ip6routes', 'privatemtu', 'publicmtu']
}, },
vnfAccessMethods () {
if (this.resource.templatetype === 'VNF' && ['vm', 'vnfapp'].includes(this.$route.meta.name)) {
const accessMethodsDescription = []
const accessMethods = this.resource.vnfdetails?.access_methods || null
const username = this.resource.vnfdetails?.username || null
const password = this.resource.vnfdetails?.password || null
const sshPort = this.resource.vnfdetails?.ssh_port || 22
const sshUsername = this.resource.vnfdetails?.ssh_user || null
const sshPassword = this.resource.vnfdetails?.ssh_password || null
let httpPath = this.resource.vnfdetails?.http_path || ''
if (!httpPath.startsWith('/')) {
httpPath = '/' + httpPath
}
const httpPort = this.resource.vnfdetails?.http_port || null
let httpsPath = this.resource.vnfdetails?.https_path || ''
if (!httpsPath.startsWith('/')) {
httpsPath = '/' + httpsPath
}
const httpsPort = this.resource.vnfdetails?.https_port || null
const webUsername = this.resource.vnfdetails?.web_user || null
const webPassword = this.resource.vnfdetails?.web_password || null
const credentials = []
if (username) {
credentials.push(this.$t('label.username') + ' : ' + username)
}
if (password) {
credentials.push(this.$t('label.password.default') + ' : ' + password)
}
if (webUsername) {
credentials.push('Web ' + this.$t('label.username') + ' : ' + webUsername)
}
if (webPassword) {
credentials.push('Web ' + this.$t('label.password.default') + ' : ' + webPassword)
}
if (sshUsername) {
credentials.push('SSH ' + this.$t('label.username') + ' : ' + sshUsername)
}
if (sshPassword) {
credentials.push('SSH ' + this.$t('label.password.default') + ' : ' + sshPassword)
}
const managementDeviceIds = []
for (const vnfnic of this.resource.vnfnics) {
if (vnfnic.management) {
managementDeviceIds.push(vnfnic.deviceid)
}
}
const managementIps = []
for (const nic of this.resource.nic) {
if (managementDeviceIds.includes(parseInt(nic.deviceid)) && nic.ipaddress) {
managementIps.push(nic.ipaddress)
if (nic.publicip) {
managementIps.push(nic.publicip)
}
}
}
if (accessMethods) {
const accessMethodsArray = accessMethods.split(',')
for (const accessMethod of accessMethodsArray) {
if (accessMethod === 'console') {
accessMethodsDescription.push('- VM Console.')
} else if (accessMethod === 'ssh-password') {
accessMethodsDescription.push('- SSH with password' + (sshPort ? ' (SSH port is ' + sshPort + ').' : '.'))
} else if (accessMethod === 'ssh-key') {
accessMethodsDescription.push('- SSH with key' + (sshPort ? ' (SSH port is ' + sshPort + ').' : '.'))
} else if (accessMethod === 'http') {
for (const managementIp of managementIps) {
const url = 'http://' + managementIp + (httpPort ? ':' + httpPort : '') + httpPath
accessMethodsDescription.push('- Webpage: <a href="' + url + '" target="_blank>">' + url + '</a>')
}
} else if (accessMethod === 'https') {
for (const managementIp of managementIps) {
const url = 'https://' + managementIp + (httpsPort ? ':' + httpsPort : '') + httpsPath
accessMethodsDescription.push('- Webpage: <a href="' + url + '" target="_blank">' + url + '</a>')
}
}
}
} else {
accessMethodsDescription.push('- VM Console.')
}
if (credentials) {
accessMethodsDescription.push('<br>' + this.$t('message.vnf.credentials.in.template.vnf.details'))
}
return accessMethodsDescription.join('<br>')
}
return null
},
ipV6Address () { ipV6Address () {
if (this.dataResource.nic && this.dataResource.nic.length > 0) { if (this.dataResource.nic && this.dataResource.nic.length > 0) {
return this.dataResource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') return this.dataResource.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ')
} }
return null return null
}, },
ip6routes () { ip6routes () {

View File

@ -39,7 +39,7 @@
</template> </template>
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'name'"> <template v-if="column.key === 'name'">
<span v-if="['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px"> <span v-if="['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px">
<span v-if="record.icon && record.icon.base64image"> <span v-if="record.icon && record.icon.base64image">
<resource-icon :image="record.icon.base64image" size="2x"/> <resource-icon :image="record.icon.base64image" size="2x"/>
</span> </span>
@ -55,7 +55,7 @@
<span v-if="$route.path.startsWith('/project')" style="margin-right: 5px"> <span v-if="$route.path.startsWith('/project')" style="margin-right: 5px">
<tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" /> <tooltip-button type="dashed" size="small" icon="LoginOutlined" @onClick="changeProject(record)" />
</span> </span>
<span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px"> <span v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px">
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/> <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="2x" /> <os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="2x" />
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/> <render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
@ -96,7 +96,7 @@
<span>{{ text <= 0 ? 'N/A' : text }}</span> <span>{{ text <= 0 ? 'N/A' : text }}</span>
</template> </template>
<template v-if="column.key === 'templatetype'"> <template v-if="column.key === 'templatetype'">
<router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link> <span>{{ text }}</span>
</template> </template>
<template v-if="column.key === 'type'"> <template v-if="column.key === 'type'">
<span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span> <span v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)">{{ $t(text.toLowerCase()) }}</span>
@ -118,7 +118,7 @@
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</template> </template>
<template v-if="column.key === 'username'"> <template v-if="column.key === 'username'">
<span v-if="$showIcon() && !['vm'].includes($route.path.split('/')[1])" style="margin-right: 5px"> <span v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px">
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/> <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
<user-outlined v-else style="font-size: 16px;" /> <user-outlined v-else style="font-size: 16px;" />
</span> </span>
@ -355,7 +355,7 @@
<template v-if="['created', 'sent'].includes(column.key)"> <template v-if="['created', 'sent'].includes(column.key)">
{{ $toLocaleDate(text) }} {{ $toLocaleDate(text) }}
</template> </template>
<template v-if="['startdate', 'enddate'].includes(column.key) && ['vm'].includes($route.path.split('/')[1])"> <template v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp'].includes($route.path.split('/')[1])">
{{ getDateAtTimeZone(text, record.timezone) }} {{ getDateAtTimeZone(text, record.timezone) }}
</template> </template>
<template v-if="column.key === 'order'"> <template v-if="column.key === 'order'">
@ -581,7 +581,7 @@ export default {
quickViewEnabled () { quickViewEnabled () {
return new RegExp(['/vm', '/kubernetes', '/ssh', '/userdata', '/vmgroup', '/affinitygroup', '/autoscalevmgroup', return new RegExp(['/vm', '/kubernetes', '/ssh', '/userdata', '/vmgroup', '/affinitygroup', '/autoscalevmgroup',
'/volume', '/snapshot', '/vmsnapshot', '/backup', '/volume', '/snapshot', '/vmsnapshot', '/backup',
'/guestnetwork', '/vpc', '/vpncustomergateway', '/guestnetwork', '/vpc', '/vpncustomergateway', '/vnfapp',
'/template', '/iso', '/template', '/iso',
'/project', '/account', '/project', '/account',
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation', '/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
@ -591,7 +591,7 @@ export default {
}, },
enableGroupAction () { enableGroupAction () {
return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot', return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot',
'vmsnapshot', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vmsnapshot', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering', 'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment' 'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment'
].includes(this.$route.name) ].includes(this.$route.name)

View File

@ -17,7 +17,7 @@
<template> <template>
<a <a
v-if="['vm', 'systemvm', 'router', 'ilbvm'].includes($route.meta.name) && 'listVirtualMachines' in $store.getters.apis && 'createConsoleEndpoint' in $store.getters.apis" v-if="['vm', 'systemvm', 'router', 'ilbvm', 'vnfapp'].includes($route.meta.name) && 'listVirtualMachines' in $store.getters.apis && 'createConsoleEndpoint' in $store.getters.apis"
@click="consoleUrl"> @click="consoleUrl">
<a-button style="margin-left: 5px" shape="circle" type="dashed" :size="size" :disabled="['Stopped', 'Error', 'Destroyed'].includes(resource.state) || resource.hostcontrolstate === 'Offline'" > <a-button style="margin-left: 5px" shape="circle" type="dashed" :size="size" :disabled="['Stopped', 'Error', 'Destroyed'].includes(resource.state) || resource.hostcontrolstate === 'Offline'" >
<code-outlined v-if="!copyUrlToClipboard"/> <code-outlined v-if="!copyUrlToClipboard"/>

View File

@ -35,6 +35,7 @@ export default {
if (store.getters.metrics) { if (store.getters.metrics) {
params = { details: 'servoff,tmpl,nics,stats' } params = { details: 'servoff,tmpl,nics,stats' }
} }
params.isvnf = false
return params return params
}, },
filters: () => { filters: () => {
@ -255,7 +256,7 @@ export default {
{ {
api: 'createBackupSchedule', api: 'createBackupSchedule',
icon: 'schedule-outlined', icon: 'schedule-outlined',
label: 'Configure Backup Schedule', label: 'label.backup.configure.schedule',
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups', docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
dataView: true, dataView: true,
popup: true, popup: true,

View File

@ -48,16 +48,17 @@ export default {
fields.push('account') fields.push('account')
} }
if (['Admin'].includes(store.getters.userInfo.roletype)) { if (['Admin'].includes(store.getters.userInfo.roletype)) {
fields.push('templatetype')
fields.push('order') fields.push('order')
} }
return fields return fields
}, },
details: () => { details: () => {
var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled', var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled',
'crossZones', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type', 'crossZones', 'templatetype', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type',
'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy'] 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy']
if (['Admin'].includes(store.getters.userInfo.roletype)) { if (['Admin'].includes(store.getters.userInfo.roletype)) {
fields.push('templatetype', 'url') fields.push('url')
} }
return fields return fields
}, },
@ -76,6 +77,10 @@ export default {
}, { }, {
name: 'settings', name: 'settings',
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailSettings'))) component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailSettings')))
}, {
name: 'vnf.settings',
component: shallowRef(defineAsyncComponent(() => import('@/views/image/TemplateVnfSettings.vue'))),
show: (record) => { return record.templatetype === 'VNF' }
}, },
{ {
name: 'events', name: 'events',

View File

@ -73,6 +73,10 @@ export default {
name: 'virtual.routers', name: 'virtual.routers',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))),
show: (record) => { return (record.type === 'Isolated' || record.type === 'Shared') && 'listRouters' in store.getters.apis && isAdmin() } show: (record) => { return (record.type === 'Isolated' || record.type === 'Shared') && 'listRouters' in store.getters.apis && isAdmin() }
}, {
name: 'vnf.appliances',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/VnfAppliancesTab.vue'))),
show: () => { return 'deployVnfAppliance' in store.getters.apis }
}, { }, {
name: 'guest.ip.range', name: 'guest.ip.range',
component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestIpRanges.vue'))), component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestIpRanges.vue'))),
@ -316,6 +320,408 @@ export default {
} }
] ]
}, },
{
name: 'vnfapp',
title: 'label.vnf.appliances',
icon: 'gateway-outlined',
permission: ['listVirtualMachinesMetrics'],
params: () => {
return { details: 'servoff,tmpl,nics', isvnf: true }
},
columns: () => {
const fields = ['name', 'state', 'ipaddress']
if (store.getters.userInfo.roletype === 'Admin') {
fields.splice(2, 0, 'instancename')
fields.push('account')
fields.push('domain')
fields.push('hostname')
} else if (store.getters.userInfo.roletype === 'DomainAdmin') {
fields.push('account')
} else {
fields.push('serviceofferingname')
}
fields.push('zonename')
return fields
},
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'groupid', 'tags'],
details: () => {
var fields = ['name', 'displayname', 'id', 'state', 'ipaddress', 'ip6address', 'templatename', 'ostypename',
'serviceofferingname', 'isdynamicallyscalable', 'haenable', 'hypervisor', 'boottype', 'bootmode', 'account',
'domain', 'zonename', 'userdataid', 'userdataname', 'userdataparams', 'userdatadetails', 'userdatapolicy', 'hostcontrolstate']
const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true)
if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) {
return fields
}
fields.push('securitygroup')
return fields
},
tabs: [{
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/InstanceTab.vue')))
}],
actions: [
{
api: 'deployVnfAppliance',
icon: 'plus-outlined',
label: 'label.vnf.appliance.add',
docHelp: 'adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances',
listView: true,
component: () => import('@/views/compute/DeployVnfAppliance.vue')
},
{
api: 'updateVirtualMachine',
icon: 'edit-outlined',
label: 'label.vnf.app.action.edit',
docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group',
dataView: true,
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/EditVM.vue')))
},
{
api: 'startVirtualMachine',
icon: 'caret-right-outlined',
label: 'label.vnf.app.action.start',
message: 'message.action.start.instance',
docHelp: 'adminguide/virtual_machines.html#stopping-and-starting-vms',
dataView: true,
groupAction: true,
popup: true,
groupMap: (selection, values) => { return selection.map(x => { return { id: x, considerlasthost: values.considerlasthost } }) },
args: ['considerlasthost'],
show: (record) => { return ['Stopped'].includes(record.state) },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/StartVirtualMachine.vue')))
},
{
api: 'stopVirtualMachine',
icon: 'poweroff-outlined',
label: 'label.vnf.app.action.stop',
message: 'message.action.stop.instance',
docHelp: 'adminguide/virtual_machines.html#stopping-and-starting-vms',
dataView: true,
groupAction: true,
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) },
args: ['forced'],
show: (record) => { return ['Running'].includes(record.state) }
},
{
api: 'rebootVirtualMachine',
icon: 'reload-outlined',
label: 'label.vnf.app.action.reboot',
message: 'message.action.reboot.instance',
docHelp: 'adminguide/virtual_machines.html#stopping-and-starting-vms',
dataView: true,
show: (record) => { return ['Running'].includes(record.state) },
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
args: (record, store) => {
var fields = []
fields.push('forced')
if (record.hypervisor === 'VMware') {
if (store.apis.rebootVirtualMachine.params.filter(x => x.name === 'bootintosetup').length > 0) {
fields.push('bootintosetup')
}
}
return fields
},
groupAction: true,
popup: true,
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) }
},
{
api: 'restoreVirtualMachine',
icon: 'sync-outlined',
label: 'label.vnf.app.action.reinstall',
message: 'message.reinstall.vm',
dataView: true,
args: ['virtualmachineid', 'templateid'],
filters: (record) => {
var filters = {}
var filterParams = {}
filterParams.hypervisortype = record.hypervisor
filterParams.zoneid = record.zoneid
filters.templateid = filterParams
return filters
},
show: (record) => { return ['Running', 'Stopped'].includes(record.state) },
mapping: {
virtualmachineid: {
value: (record) => { return record.id }
}
},
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
successMethod: (obj, result) => {
const vm = result.jobresult.virtualmachine || {}
if (result.jobstatus === 1 && vm.password) {
const name = vm.displayname || vm.name || vm.id
obj.$notification.success({
message: `${obj.$t('label.reinstall.vm')}: ` + name,
description: `${obj.$t('label.password.reset.confirm')}: ` + vm.password,
duration: 0
})
}
}
},
{
api: 'createVMSnapshot',
icon: 'camera-outlined',
label: 'label.action.vmsnapshot.create',
docHelp: 'adminguide/virtual_machines.html#virtual-machine-snapshots',
dataView: true,
args: ['virtualmachineid', 'name', 'description', 'snapshotmemory', 'quiescevm'],
show: (record) => {
return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') ||
(['Stopped'].includes(record.state) && ((record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') ||
(record.hypervisor === 'KVM' && record.pooltype === 'PowerFlex'))))
},
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
}
}
},
{
api: 'createSnapshot',
icon: ['fas', 'camera-retro'],
label: 'label.action.vmstoragesnapshot.create',
docHelp: 'adminguide/virtual_machines.html#virtual-machine-snapshots',
dataView: true,
popup: true,
show: (record) => {
return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') ||
(['Stopped'].includes(record.state) && !['KVM', 'LXC'].includes(record.hypervisor)))
},
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateSnapshotWizard.vue')))
},
{
api: 'assignVirtualMachineToBackupOffering',
icon: 'folder-add-outlined',
label: 'label.backup.offering.assign',
message: 'label.backup.offering.assign',
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
dataView: true,
args: ['virtualmachineid', 'backupofferingid'],
show: (record) => { return !record.backupofferingid },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
}
}
},
{
api: 'createBackup',
icon: 'cloud-upload-outlined',
label: 'label.create.backup',
message: 'message.backup.create',
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
dataView: true,
args: ['virtualmachineid'],
show: (record) => { return record.backupofferingid },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
}
}
},
{
api: 'createBackupSchedule',
icon: 'schedule-outlined',
label: 'label.backup.configure.schedule',
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
dataView: true,
popup: true,
show: (record) => { return record.backupofferingid },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/BackupScheduleWizard.vue'))),
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
},
intervaltype: {
options: ['HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY']
}
}
},
{
api: 'removeVirtualMachineFromBackupOffering',
icon: 'scissor-outlined',
label: 'label.backup.offering.remove',
message: 'label.backup.offering.remove',
docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
dataView: true,
args: ['virtualmachineid', 'forced'],
show: (record) => { return record.backupofferingid },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
}
}
},
{
api: 'attachIso',
icon: 'paper-clip-outlined',
label: 'label.action.attach.iso',
docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm',
dataView: true,
popup: true,
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid },
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue')))
},
{
api: 'detachIso',
icon: 'link-outlined',
label: 'label.action.detach.iso',
message: 'message.detach.iso.confirm',
dataView: true,
args: (record, store) => {
var args = ['virtualmachineid']
if (record && record.hypervisor && record.hypervisor === 'VMware') {
args.push('forced')
}
return args
},
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid },
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
}
}
},
{
api: 'updateVMAffinityGroup',
icon: 'swap-outlined',
label: 'label.change.affinity',
docHelp: 'adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm',
dataView: true,
args: ['affinitygroupids'],
show: (record) => { return ['Stopped'].includes(record.state) },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeAffinity'))),
popup: true
},
{
api: 'scaleVirtualMachine',
icon: 'arrows-alt-outlined',
label: 'label.vnf.app.action.scale',
docHelp: 'adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram',
dataView: true,
show: (record) => { return ['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'LXC') },
disabled: (record) => { return record.state === 'Running' && !record.isdynamicallyscalable },
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleVM.vue')))
},
{
api: 'migrateVirtualMachine',
icon: 'drag-outlined',
label: 'label.vnf.app.action.migrate.to.host',
docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration',
dataView: true,
show: (record, store) => { return ['Running'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) },
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateWizard.vue')))
},
{
api: 'migrateVirtualMachine',
icon: 'drag-outlined',
label: 'label.vnf.app.action.migrate.to.ps',
message: 'message.migrate.instance.to.ps',
docHelp: 'adminguide/virtual_machines.html#moving-vms-between-hosts-manual-live-migration',
dataView: true,
show: (record, store) => { return ['Stopped'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) },
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/MigrateVMStorage'))),
popup: true
},
{
api: 'resetPasswordForVirtualMachine',
icon: 'key-outlined',
label: 'label.action.reset.password',
message: 'message.action.instance.reset.password',
dataView: true,
show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled },
response: (result) => {
return {
message: result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null,
copybuttontext: result.virtualmachine.password ? 'label.copy.password' : null,
copytext: result.virtualmachine.password ? result.virtualmachine.password : null
}
}
},
{
api: 'resetSSHKeyForVirtualMachine',
icon: 'lock-outlined',
label: 'label.reset.ssh.key.pair',
message: 'message.desc.reset.ssh.key.pair',
docHelp: 'adminguide/virtual_machines.html#resetting-ssh-keys',
dataView: true,
show: (record) => { return ['Stopped'].includes(record.state) },
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetSshKeyPair')))
},
{
api: 'resetUserDataForVirtualMachine',
icon: 'solution-outlined',
label: 'label.reset.userdata.on.vm',
message: 'message.desc.reset.userdata',
docHelp: 'adminguide/virtual_machines.html#resetting-userdata',
dataView: true,
show: (record) => { return ['Stopped'].includes(record.state) },
popup: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetUserData')))
},
{
api: 'assignVirtualMachine',
icon: 'user-add-outlined',
label: 'label.assign.instance.another',
dataView: true,
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AssignInstance'))),
popup: true,
show: (record) => { return ['Stopped'].includes(record.state) }
},
{
api: 'recoverVirtualMachine',
icon: 'medicine-box-outlined',
label: 'label.vnf.app.action.recover',
message: 'message.recover.vm',
dataView: true,
show: (record, store) => { return ['Destroyed'].includes(record.state) && store.features.allowuserexpungerecovervm }
},
{
api: 'unmanageVirtualMachine',
icon: 'disconnect-outlined',
label: 'label.action.unmanage.virtualmachine',
message: 'message.action.unmanage.virtualmachine',
dataView: true,
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && record.hypervisor === 'VMware' }
},
{
api: 'expungeVirtualMachine',
icon: 'delete-outlined',
label: 'label.vnf.app.action.expunge',
message: (record) => { return record.backupofferingid ? 'message.action.expunge.instance.with.backups' : 'message.action.expunge.instance' },
docHelp: 'adminguide/virtual_machines.html#deleting-vms',
dataView: true,
show: (record, store) => { return ['Destroyed', 'Expunging'].includes(record.state) && store.features.allowuserexpungerecovervm }
},
{
api: 'destroyVirtualMachine',
icon: 'delete-outlined',
label: 'label.vnf.app.action.destroy',
message: 'message.action.destroy.instance',
docHelp: 'adminguide/virtual_machines.html#deleting-vms',
dataView: true,
groupAction: true,
args: (record, store, group) => {
return (['Admin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervm)
? ['expunge'] : []
},
popup: true,
groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge } }) },
show: (record) => { return ['Running', 'Stopped', 'Error'].includes(record.state) },
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/DestroyVM.vue')))
}
]
},
{ {
name: 'publicip', name: 'publicip',
title: 'label.public.ip.addresses', title: 'label.public.ip.addresses',

View File

@ -762,7 +762,7 @@ export default {
} }
} }
if (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) && if (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) &&
'templatefilter' in params && this.routeName === 'template') { 'templatefilter' in params && (['template'].includes(this.routeName))) {
params.templatefilter = 'all' params.templatefilter = 'all'
} }
if (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) && if (['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) &&
@ -789,7 +789,9 @@ export default {
} }
this.projectView = Boolean(store.getters.project && store.getters.project.id) this.projectView = Boolean(store.getters.project && store.getters.project.id)
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'volume', 'snapshot', 'vmsnapshot', 'guestnetwork', 'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes', 'autoscalevmgroup'].includes(this.$route.name) this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'volume', 'snapshot', 'vmsnapshot', 'guestnetwork',
'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes',
'autoscalevmgroup', 'vnfapp'].includes(this.$route.name)
if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) { if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) {
this.dataView = true this.dataView = true
@ -891,6 +893,7 @@ export default {
if (['listVirtualMachinesMetrics'].includes(this.apiName) && this.dataView) { if (['listVirtualMachinesMetrics'].includes(this.apiName) && this.dataView) {
delete params.details delete params.details
delete params.isvnf
} }
this.loading = true this.loading = true
@ -1101,7 +1104,7 @@ export default {
this.rules = reactive({}) this.rules = reactive({})
if (action.component && action.api && !action.popup) { if (action.component && action.api && !action.popup) {
const query = {} const query = {}
if (this.$route.path.startsWith('/vm')) { if (this.$route.path.startsWith('/vm') || this.$route.path.startsWith('/vnfapp')) {
switch (true) { switch (true) {
case ('templateid' in this.$route.query): case ('templateid' in this.$route.query):
query.templateid = this.$route.query.templateid query.templateid = this.$route.query.templateid

View File

@ -522,7 +522,7 @@
{{ $t('label.isadvanced') }} {{ $t('label.isadvanced') }}
<a-switch v-model:checked="showDetails" style="margin-left: 10px"/> <a-switch v-model:checked="showDetails" style="margin-left: 10px"/>
</span> </span>
<div style="margin-top: 15px" v-show="showDetails"> <div style="margin-top: 15px" v-if="showDetails">
<div <div
v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && !template.deployasis"> v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && !template.deployasis">
<a-form-item :label="$t('label.boottype')" name="boottype" ref="boottype"> <a-form-item :label="$t('label.boottype')" name="boottype" ref="boottype">
@ -2292,6 +2292,7 @@ export default {
args.details = 'all' args.details = 'all'
args.showicon = 'true' args.showicon = 'true'
args.id = this.templateId args.id = this.templateId
args.isvnf = false
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
api('listTemplates', args).then((response) => { api('listTemplates', args).then((response) => {

File diff suppressed because it is too large Load Diff

View File

@ -91,6 +91,10 @@ export default {
type: Boolean, type: Boolean,
default: () => false default: () => false
}, },
vnf: {
type: Boolean,
default: () => false
},
preFillContent: { preFillContent: {
type: Object, type: Object,
default: () => {} default: () => {}
@ -138,6 +142,9 @@ export default {
}, },
computed: { computed: {
rowSelection () { rowSelection () {
if (this.vnf) {
return null
}
return { return {
type: 'radio', type: 'radio',
selectedRowKeys: this.selectedRowKeys, selectedRowKeys: this.selectedRowKeys,

View File

@ -22,7 +22,7 @@
:placeholder="$t('label.search')" :placeholder="$t('label.search')"
v-model:value="filter" v-model:value="filter"
@search="handleSearch" /> @search="handleSearch" />
<a-button type="primary" @click="onCreateNetworkClick" style="float: right; margin-right: 5px; z-index: 8" v-if="showCreateButton"> <a-button type="primary" @click="onCreateNetworkClick" style="float: right; margin-right: 5px; z-index: 8" v-if="showCreateButton && !this.vnf">
{{ $t('label.create.network') }} {{ $t('label.create.network') }}
</a-button> </a-button>
<a-table <a-table
@ -138,6 +138,10 @@ export default {
type: Boolean, type: Boolean,
default: () => false default: () => false
}, },
vnf: {
type: Boolean,
default: () => false
},
preFillContent: { preFillContent: {
type: Object, type: Object,
default: () => {} default: () => {}

View File

@ -0,0 +1,166 @@
// 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.
<template>
<div style="margin-top: 10px;">
<label>{{ $t('message.vnf.select.networks') }}</label>
</div>
<a-form
:ref="formRef"
:model="form"
:rules="rules">
<a-table
:columns="columns"
:dataSource="items"
:pagination="false"
:rowKey="record => record.deviceid"
size="middle"
:scroll="{ y: 225 }">
<template #deviceid="{ text }">
<div>{{ text }}</div>
</template>
<template #name="{ text }">
<div>{{ text }}</div>
</template>
<template #required="{ record }">
<span v-if="record.required">{{ $t('label.yes') }}</span>
<span v-else>{{ $t('label.no') }}</span>
</template>
<template #management="{ record }">
<span v-if="record.management">{{ $t('label.yes') }}</span>
<span v-else>{{ $t('label.no') }}</span>
</template>
<template #description="{record}">
<span> {{ record.description }} </span>
</template>
<template #network="{ record }">
<a-form-item style="display: block" :name="'nic-' + record.deviceid">
<a-select
@change="updateNicNetworkValue($event, record.deviceid)"
optionFilterProp="label"
:filterOption="(input, option) => {
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option key="" >{{ }}</a-select-option>
<a-select-option v-for="network in networks" :key="network.id">
{{ network.name }}
</a-select-option>
</a-select>
</a-form-item>
</template>
</a-table>
</a-form>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
name: 'VnfNicsSelection',
props: {
items: {
type: Array,
default: () => []
},
networks: {
type: Array,
default: () => []
},
preFillContent: {
type: Object,
default: () => {}
}
},
data () {
return {
values: {},
columns: [
{
dataIndex: 'deviceid',
title: this.$t('label.deviceid'),
width: '10%',
slots: { customRender: 'deviceid' }
},
{
dataIndex: 'name',
title: this.$t('label.name'),
width: '15%',
slots: { customRender: 'name' }
},
{
dataIndex: 'required',
title: this.$t('label.required'),
width: '10%',
slots: { customRender: 'required' }
},
{
dataIndex: 'management',
title: this.$t('label.vnf.nic.management'),
width: '15%',
slots: { customRender: 'management' }
},
{
dataIndex: 'description',
title: this.$t('label.description'),
width: '35%',
slots: { customRender: 'description' }
},
{
dataIndex: 'network',
title: this.$t('label.network'),
width: '25%',
slots: { customRender: 'network' }
}
]
}
},
created () {
this.initForm()
},
methods: {
initForm () {
this.formRef = ref()
this.form = reactive({})
this.rules = reactive({})
const form = {}
const rules = {}
this.form = reactive(form)
this.rules = reactive(rules)
},
updateNicNetworkValue (value, deviceid) {
this.values[deviceid] = this.networks.filter(network => network.id === value)?.[0] || null
this.$emit('update-vnf-nic-networks', this.values)
}
}
}
</script>
<style lang="less" scoped>
.ant-table-wrapper {
margin: 2rem 0;
}
:deep(.ant-table-tbody) > tr > td {
cursor: pointer;
}
.ant-form .ant-form-item {
margin-bottom: 0;
padding-bottom: 0;
}
</style>

View File

@ -324,6 +324,25 @@
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item
name="templatetype"
ref="templatetype">
<template #label>
<tooltip-label :title="$t('label.templatetype')" :tooltip="apiParams.templatetype.description"/>
</template>
<a-select
showSearch
optionFilterProp="label"
:filterOption="(input, option) => {
return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
v-model:value="form.templatetype"
:placeholder="apiParams.templatetype.description">
<a-select-option v-for="opt in templateTypes.opts" :key="opt.id">
{{ opt.name || opt.description }}
</a-select-option>
</a-select>
</a-form-item>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col :md="24" :lg="12"> <a-col :md="24" :lg="12">
<a-form-item <a-form-item
@ -405,11 +424,6 @@
{{ $t('label.ispublic') }} {{ $t('label.ispublic') }}
</a-checkbox> </a-checkbox>
</a-col> </a-col>
<a-col :span="12" v-if="isAdminRole">
<a-checkbox value="isrouting">
{{ $t('label.isrouting') }}
</a-checkbox>
</a-col>
</a-row> </a-row>
</a-checkbox-group> </a-checkbox-group>
</a-form-item> </a-form-item>
@ -465,6 +479,7 @@ export default {
format: {}, format: {},
osTypes: {}, osTypes: {},
defaultOsType: '', defaultOsType: '',
templateTypes: {},
userdata: {}, userdata: {},
userdataid: null, userdataid: null,
userdatapolicy: null, userdatapolicy: null,
@ -539,6 +554,7 @@ export default {
this.fetchCustomHypervisorName() this.fetchCustomHypervisorName()
this.fetchZone() this.fetchZone()
this.fetchOsTypes() this.fetchOsTypes()
this.fetchTemplateTypes()
this.fetchUserData() this.fetchUserData()
this.fetchUserdataPolicy() this.fetchUserdataPolicy()
if ('listDomains' in this.$store.getters.apis) { if ('listDomains' in this.$store.getters.apis) {
@ -681,6 +697,33 @@ export default {
this.osTypes.loading = false this.osTypes.loading = false
}) })
}, },
fetchTemplateTypes () {
this.templateTypes.opts = []
const templatetypes = []
templatetypes.push({
id: 'USER',
description: 'USER'
})
templatetypes.push({
id: 'VNF',
description: 'VNF'
})
if (this.isAdminRole) {
templatetypes.push({
id: 'SYSTEM',
description: 'SYSTEM'
})
templatetypes.push({
id: 'BUILTIN',
description: 'BUILTIN'
})
templatetypes.push({
id: 'ROUTING',
description: 'ROUTING'
})
}
this.templateTypes.opts = templatetypes
},
fetchUserData () { fetchUserData () {
const params = {} const params = {}
params.listAll = true params.listAll = true

View File

@ -0,0 +1,880 @@
// 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.
<template>
<div>
<a-divider/>
<div class="title">
<div class="form__label">
<tooltip-label :title="$t('label.vnf.nics')" :tooltip="apiParams.vnfnics.description"/>
</div>
</div>
<div>
<a-button
type="dashed"
style="width: 100%"
:disabled="!('updateVnfTemplate' in $store.getters.apis && isAdminOrOwner())"
@click="onShowAddVnfNic">
<template #icon><plus-outlined /></template>
{{ $t('label.vnf.nic.add') }}
</a-button>
</div>
<a-table
size="small"
style="overflow-y: auto"
:loading="loading"
:columns="columns"
:dataSource="vnfNics"
:pagination="false"
:rowKey="record => record.deviceid">
<template #deviceid="{record}">
<span> {{ record.deviceid }} </span>
</template>
<template #name="{record}">
<span> {{ record.name }} </span>
</template>
<template #required="{ record }">
<span v-if="record.required">{{ $t('label.yes') }}</span>
<span v-else>{{ $t('label.no') }}</span>
</template>
<template #management="{ record }">
<span v-if="record.management">{{ $t('label.yes') }}</span>
<span v-else>{{ $t('label.no') }}</span>
</template>
<template #description="{record}">
<span> {{ record.description }} </span>
</template>
<template #actions="{ record }">
<div class="shift-btns" v-if="'updateVnfTemplate' in $store.getters.apis && isAdminOrOwner()">
<a-tooltip placement="top">
<template #title>{{ $t('label.vnf.nic.edit') }}</template>
<a-button shape="round" @click="onShowEditVnfNic(record)" class="shift-btn">
<EditOutlined class="shift-btn" />
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<template #title>{{ $t('label.move.up.row') }}</template>
<a-button shape="round" @click="moveVnfNicUp(record)" class="shift-btn">
<CaretUpOutlined class="shift-btn" />
</a-button>
</a-tooltip>
<a-tooltip placement="top">
<template #title>{{ $t('label.move.down.row') }}</template>
<a-button shape="round" @click="moveVnfNicDown(record)" class="shift-btn">
<CaretDownOutlined class="shift-btn" />
</a-button>
</a-tooltip>
<a-popconfirm
:title="$t('label.vnf.nic.delete') + '?'"
@confirm="deleteVnfNic(record)"
:okText="$t('label.yes')"
:cancelText="$t('label.no')"
>
<template #title>{{ $t('label.vnf.nic.delete') }}</template>
<a-button shape="round" class="shift-btn" :danger="true" type="primary">
<DeleteOutlined class="shift-btn" />
</a-button>
</a-popconfirm>
</div>
</template>
</a-table>
<a-modal
:title="$t('label.vnf.nic.add')"
:visible="showAddVnfNic"
:afterClose="closeVnfNicModal"
:maskClosable="false"
:closable="true"
:footer="null"
@cancel="closeVnfNicModal">
<div class="new-vnf-nic" v-ctrl-enter="addVnfNic">
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.deviceid')" :tooltip="$t('label.vnf.nic.deviceid')"/>
</div>
<a-input v-model:value="newVnfNic.deviceid" type="number" v-focus="true"></a-input>
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.name')" :tooltip="$t('label.vnf.nic.name')"/>
</div>
<a-input v-model:value="newVnfNic.name"></a-input>
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.required')" :tooltip="$t('label.vnf.nic.required')"/>
</div>
<a-switch v-model:checked="newVnfNic.required" :checked="true" />
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.vnf.nic.management')" :tooltip="$t('label.vnf.nic.management.description')"/>
</div>
<a-switch v-model:checked="newVnfNic.management" :checked="false" />
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<tooltip-label :title="$t('label.description')" :tooltip="$t('label.vnf.nic.description')"/>
</div>
<a-input v-model:value="newVnfNic.description"></a-input>
</div>
</div>
<div :span="24" class="action-button">
<a-button @click="showAddVnfNic = false">{{ $t('label.cancel') }}</a-button>
<a-button ref="submit" type="primary" @click="addVnfNic">{{ $t('label.ok') }}</a-button>
</div>
</a-modal>
<a-modal
:title="$t('label.vnf.nic.add')"
:visible="showAddVnfNic"
:afterClose="closeVnfNicModal"
:maskClosable="false"
:closable="true"
:footer="null"
@cancel="closeVnfNicModal">
<div class="new-vnf-nic" v-ctrl-enter="addVnfNic">
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.deviceid')" :tooltip="$t('label.vnf.nic.deviceid')"/>
</div>
<a-input v-model:value="newVnfNic.deviceid" type="number" v-focus="true"></a-input>
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.name')" :tooltip="$t('label.vnf.nic.name')"/>
</div>
<a-input v-model:value="newVnfNic.name"></a-input>
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.required')" :tooltip="$t('label.vnf.nic.required')"/>
</div>
<a-switch v-model:checked="newVnfNic.required" :checked="true" />
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<span class="new-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.vnf.nic.management')" :tooltip="$t('label.vnf.nic.management.description')"/>
</div>
<a-switch v-model:checked="newVnfNic.management" :checked="false" />
</div>
<div class="new-vnf-nic__item">
<div class="new-vnf-nic__label">
<tooltip-label :title="$t('label.description')" :tooltip="$t('label.vnf.nic.description')"/>
</div>
<a-input v-model:value="newVnfNic.description"></a-input>
</div>
</div>
<div :span="24" class="action-button">
<a-button @click="showAddVnfNic = false">{{ $t('label.cancel') }}</a-button>
<a-button ref="submit" type="primary" @click="addVnfNic">{{ $t('label.ok') }}</a-button>
</div>
</a-modal>
<a-modal
:title="$t('label.vnf.nic.edit')"
:visible="showEditVnfNic"
:afterClose="closeVnfNicModal"
:maskClosable="false"
:closable="true"
:footer="null"
@cancel="closeVnfNicModal">
<div class="update-vnf-nic" v-ctrl-enter="editVnfNic">
<div class="update-vnf-nic__item">
<div class="update-vnf-nic__label">
<span class="update-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.deviceid')" :tooltip="$t('label.vnf.nic.deviceid')"/>
</div>
<span> {{ updateVnfNic.deviceid }} </span>
</div>
<div class="update-vnf-nic__item">
<div class="update-vnf-nic__label">
<span class="update-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.name')" :tooltip="$t('label.vnf.nic.name')"/>
</div>
<a-input v-model:value="updateVnfNic.name"></a-input>
</div>
<div class="update-vnf-nic__item">
<div class="update-vnf-nic__label">
<span class="update-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.required')" :tooltip="$t('label.vnf.nic.required')"/>
</div>
<a-switch v-model:checked="updateVnfNic.required" :checked="true" />
</div>
<div class="update-vnf-nic__item">
<div class="update-vnf-nic__label">
<span class="update-vnf-nic__required">*</span>
<tooltip-label :title="$t('label.vnf.nic.management')" :tooltip="$t('label.vnf.nic.management.description')"/>
</div>
<a-switch v-model:checked="updateVnfNic.management" :checked="false" />
</div>
<div class="update-vnf-nic__item">
<div class="update-vnf-nic__label">
<tooltip-label :title="$t('label.description')" :tooltip="$t('label.vnf.nic.description')"/>
</div>
<a-input v-model:value="updateVnfNic.description"></a-input>
</div>
</div>
<div :span="24" class="action-button">
<a-button @click="showEditVnfNic = false">{{ $t('label.cancel') }}</a-button>
<a-button ref="submit" type="primary" @click="editVnfNic">{{ $t('label.ok') }}</a-button>
</div>
</a-modal>
<a-divider/>
<div class="title">
<div class="form__label">
<tooltip-label :title="$t('label.vnf.details')" :tooltip="apiParams.vnfdetails.description"/>
</div>
</div>
<div v-show="!showAddVnfDetail">
<a-button
type="dashed"
style="width: 100%"
:disabled="!('updateVnfTemplate' in $store.getters.apis && isAdminOrOwner())"
@click="onShowAddVnfDetail">
<template #icon><plus-outlined /></template>
{{ $t('label.vnf.detail.add') }}
</a-button>
</div>
<div v-show="showAddVnfDetail">
<a-input-group
type="text"
compact>
<a-auto-complete
class="detail-input"
ref="keyElm"
:filterOption="filterOption"
v-model:value="newKey"
:options="detailKeys"
:placeholder="$t('label.name')" />
<a-input
class="tag-disabled-input"
style=" width: 30px; border-left: 0; pointer-events: none; text-align: center"
placeholder="="
disabled />
<a-select
v-if="newKey === 'access_methods'"
class="detail-input"
v-model:value="newValues"
mode="multiple"
:placeholder="$t('label.value')"
:filterOption="filterOption">
<a-select-option v-for="opt in detailValues" :key="opt.value" :label="opt.value">
<span>
{{ opt.value }}
</span>
</a-select-option>
</a-select>
<a-input
v-else
class="detail-input"
:filterOption="filterOption"
v-model:value="newValue"
:options="detailValues"
:placeholder="$t('label.value')" />
<tooltip-button :tooltip="$t('label.add.setting')" :shape="null" icon="check-outlined" @onClick="addVnfDetail" buttonClass="detail-button" />
<tooltip-button :tooltip="$t('label.cancel')" :shape="null" icon="close-outlined" @onClick="closeVnfDetail" buttonClass="detail-button" />
</a-input-group>
<p v-if="error" style="color: red"> {{ $t(error) }} </p>
</div>
<a-list size="large">
<a-list-item :key="index" v-for="(item, index) in vnfDetails">
<a-list-item-meta>
<template #title>
{{ item.name }}
</template>
<template #description>
<div v-if="item.edit" style="display: flex">
<a-select
v-if="item.name === 'access_methods'"
class="detail-input"
v-model:value="item.values"
mode="multiple"
:placeholder="$t('label.value')"
:filterOption="filterOption">
<a-select-option v-for="opt in getEditDetailOptions(vnfDetailOptions[item.name])" :key="opt.value" :label="opt.value">
<span>
{{ opt.value }}
</span>
</a-select-option>
</a-select>
<a-auto-complete
v-else
style="width: 100%"
v-model:value="item.displayvalue"
:options="getEditDetailOptions(vnfDetailOptions[item.name])"
@change="val => handleInputChange(val, index)"
@pressEnter="e => updateVnfDetail(index)" />
<tooltip-button
buttonClass="edit-button"
:tooltip="$t('label.cancel')"
@onClick="hideEditVnfDetail(index)"
v-if="item.edit"
iconType="close-circle-two-tone"
iconTwoToneColor="#f5222d" />
<tooltip-button
buttonClass="edit-button"
:tooltip="$t('label.ok')"
@onClick="updateVnfDetail(index)"
v-if="item.edit"
iconType="check-circle-two-tone"
iconTwoToneColor="#52c41a" />
</div>
<span v-else style="word-break: break-all">{{ item.displayvalue }}</span>
</template>
</a-list-item-meta>
<template #actions>
<div
v-if="'updateVnfTemplate' in $store.getters.apis && isAdminOrOwner()">
<tooltip-button
:tooltip="$t('label.edit')"
icon="edit-outlined"
@onClick="showEditVnfDetail(index)" />
</div>
<div
v-if="'updateVnfTemplate' in $store.getters.apis && isAdminOrOwner()">
<a-popconfirm
:title="`${$t('label.delete.setting')}?`"
@confirm="deleteVnfDetail(index)"
:okText="$t('label.yes')"
:cancelText="$t('label.no')"
placement="left"
>
<tooltip-button :tooltip="$t('label.delete')" type="primary" :danger="true" icon="delete-outlined" />
</a-popconfirm>
</div>
</template>
</a-list-item>
</a-list>
</div>
</template>
<script>
import { ref, reactive } from 'vue'
import { api } from '@/api'
import Status from '@/components/widgets/Status'
import TooltipButton from '@/components/widgets/TooltipButton'
import TooltipLabel from '@/components/widgets/TooltipLabel'
export default {
name: 'TemplateVnfSettings',
components: {
TooltipButton,
TooltipLabel,
Status
},
props: {
resource: {
type: Object,
required: true
},
loading: {
type: Boolean,
default: false
}
},
data () {
return {
resourceType: 'VnfTemplate',
vnfDetailOptions: {},
columns: [],
vnfNics: [],
previousVnfNics: [],
showAddVnfNic: false,
showEditVnfNic: false,
newVnfNic: {
deviceid: null,
name: null,
required: true,
management: false,
description: null
},
updateVnfNic: {
deviceid: null,
name: null,
required: true,
management: false,
description: null
},
vnfDetails: [],
previousVnfDetails: [],
showAddVnfDetail: false,
newKey: '',
newValue: '',
newValues: [],
error: false
}
},
beforeCreate () {
this.apiParams = this.$getApiParams('updateVnfTemplate')
},
computed: {
detailKeys () {
return Object.keys(this.vnfDetailOptions).map(key => {
return { value: key }
})
},
detailValues () {
if (!this.newKey) {
return []
}
if (!Array.isArray(this.vnfDetailOptions[this.newKey])) {
if (this.vnfDetailOptions[this.newKey]) {
return { value: this.vnfDetailOptions[this.newKey] }
} else {
return ''
}
}
return this.vnfDetailOptions[this.newKey].map(value => {
return { value: value }
})
}
},
created () {
this.columns = [
{
title: this.$t('label.deviceid'),
dataIndex: 'deviceid',
slots: { customRender: 'deviceid' }
},
{
title: this.$t('label.name'),
dataIndex: 'name'
},
{
title: this.$t('label.required'),
dataIndex: 'required',
slots: { customRender: 'required' }
},
{
title: this.$t('label.vnf.nic.management'),
dataIndex: 'management',
slots: { customRender: 'management' }
},
{
title: this.$t('label.description'),
dataIndex: 'description',
slots: { customRender: 'description' }
},
{
title: this.$t('label.action'),
slots: { customRender: 'actions' }
}
]
this.columns.push({
title: '',
dataIndex: 'action',
width: 100,
slots: { customRender: 'action' }
})
const userInfo = this.$store.getters.userInfo
if (!['Admin'].includes(userInfo.roletype) &&
(userInfo.account !== this.resource.account || userInfo.domain !== this.resource.domain)) {
this.columns = this.columns.filter(col => { return col.dataIndex !== 'status' })
}
this.initForm()
this.fetchData()
},
watch: {
loading (newData, oldData) {
if (!newData && !this.showGroupActionModal) {
this.fetchData()
}
}
},
methods: {
initForm () {
this.formRef = ref()
this.form = reactive({})
this.rules = reactive({
zoneid: [{ type: 'array', required: true, message: this.$t('message.error.select') }]
})
this.previousVnfNics = this.resource.vnfnics?.slice() || []
this.previousVnfDetails = []
if (this.resource.vnfdetails) {
this.previousVnfDetails = Object.keys(this.resource.vnfdetails).map(k => {
return {
name: k,
value: this.resource.vnfdetails[k],
displayvalue: this.getDisplayValue(k, this.resource.vnfdetails[k]),
values: k === 'access_methods' ? this.resource.vnfdetails[k].split(',') : null,
edit: false
}
})
}
api('listDetailOptions', { resourcetype: this.resourceType }).then(json => {
this.vnfDetailOptionsInApi = json.listdetailoptionsresponse.detailoptions.details
this.vnfDetailOptions = {}
Object.keys(this.vnfDetailOptionsInApi).sort().forEach(k => {
this.vnfDetailOptions[k] = this.vnfDetailOptionsInApi[k]
})
})
},
fetchData () {
this.vnfNics = this.previousVnfNics.slice() || []
this.vnfDetails = this.previousVnfDetails.slice() || []
},
isAdminOrOwner () {
return ['Admin'].includes(this.$store.getters.userInfo.roletype) ||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
this.resource.project && this.resource.projectid === this.$store.getters.project.id
},
onShowAddVnfNic () {
this.showAddVnfNic = true
},
filterOption (input, option) {
return (
option.value.toUpperCase().indexOf(input.toUpperCase()) >= 0
)
},
addVnfNic () {
if (!this.newVnfNic.deviceid || !this.newVnfNic.name) {
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.please.enter.valid.value')
})
return
}
if (this.newVnfNic && this.newVnfNic.deviceid && this.newVnfNic.name) {
this.vnfNics.push({
deviceid: this.newVnfNic.deviceid,
name: this.newVnfNic.name,
required: this.newVnfNic.required,
management: this.newVnfNic.management,
description: this.newVnfNic.description
})
}
this.updateVnfTemplateNics()
},
deleteVnfNic (record) {
for (var index = 0; index < this.vnfNics.length; index++) {
var nic = this.vnfNics[index]
if (nic.deviceid === record.deviceid) {
this.vnfNics.splice(index, 1)
break
}
}
this.updateVnfTemplateNics()
},
onShowEditVnfNic (record) {
this.updateVnfNic.deviceid = record.deviceid
this.updateVnfNic.name = record.name
this.updateVnfNic.required = record.required
this.updateVnfNic.management = record.management
this.updateVnfNic.description = record.description
this.showEditVnfNic = true
},
getEditDetailOptions (values) {
if (!values) {
return
}
var data = values.map(value => { return { value: value } })
return data
},
editVnfNic () {
if (!this.updateVnfNic.name) {
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.please.enter.valid.value')
})
return
}
if (this.updateVnfNic && this.updateVnfNic.name) {
for (var index = 0; index < this.vnfNics.length; index++) {
var nic = this.vnfNics[index]
if (nic.deviceid === this.updateVnfNic.deviceid) {
this.vnfNics[index] = this.updateVnfNic
}
}
}
this.updateVnfTemplateNics()
},
moveVnfNicUp (record) {
const deviceid = record.deviceid
let currentNic = null
let previousNic = null
for (var index = 0; index < this.vnfNics.length; index++) {
var nic = this.vnfNics[index]
if (nic.deviceid === record.deviceid) {
currentNic = JSON.parse(JSON.stringify(nic))
this.vnfNics[index] = currentNic
} else if (nic.deviceid === record.deviceid - 1) {
previousNic = JSON.parse(JSON.stringify(nic))
this.vnfNics[index] = previousNic
}
}
if (currentNic && previousNic) {
currentNic.deviceid = deviceid - 1
previousNic.deviceid = deviceid
const currentRequired = currentNic.required
currentNic.required = previousNic.required
previousNic.required = currentRequired
this.updateVnfTemplateNics()
} else {
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.vnf.nic.move.up.fail')
})
}
},
moveVnfNicDown (record) {
const deviceid = record.deviceid
let currentNic = null
let nextNic = null
for (var index = 0; index < this.vnfNics.length; index++) {
var nic = this.vnfNics[index]
if (nic.deviceid === record.deviceid) {
currentNic = JSON.parse(JSON.stringify(nic))
this.vnfNics[index] = currentNic
} else if (nic.deviceid === record.deviceid + 1) {
nextNic = JSON.parse(JSON.stringify(nic))
this.vnfNics[index] = nextNic
}
}
if (currentNic && nextNic) {
currentNic.deviceid = deviceid + 1
nextNic.deviceid = deviceid
const currentRequired = currentNic.required
currentNic.required = nextNic.required
nextNic.required = currentRequired
this.updateVnfTemplateNics()
} else {
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.vnf.nic.move.down.fail')
})
}
},
updateVnfTemplateDetails () {
this.updateVnfTemplate(true, false)
},
updateVnfTemplateNics () {
this.updateVnfTemplate(false, true)
},
updateVnfTemplate (areDetailsChanged, areNicsChanged) {
const apiName = 'updateVnfTemplate'
if (!(apiName in this.$store.getters.apis)) {
this.$notification.error({
message: this.$t('error.execute.api.failed') + ' ' + apiName,
description: this.$t('message.user.not.permitted.api')
})
return
}
const params = { id: this.resource.id }
if (areDetailsChanged) {
if (this.vnfDetails.length === 0) {
params.cleanupvnfdetails = true
} else {
this.vnfDetails.forEach(function (item, index) {
params['vnfdetails[0].' + item.name] = item.value
})
}
}
if (areNicsChanged) {
let i = 0
if (this.vnfNics.length === 0) {
params.cleanupvnfnics = true
}
for (var index = 0; index < this.vnfNics.length; index++) {
var nic = this.vnfNics[index]
params['vnfnics[' + i + '].deviceid'] = nic.deviceid
params['vnfnics[' + i + '].name'] = nic.name
params['vnfnics[' + i + '].required'] = nic.required
params['vnfnics[' + i + '].management'] = nic.management
params['vnfnics[' + i + '].description'] = nic.description
i++
}
}
api(apiName, params).then(json => {
this.vnfNics = json.updatetemplateresponse.template.vnfnics || []
const details = json.updatetemplateresponse.template.vnfdetails || []
this.vnfDetails = Object.keys(details).map(k => {
return {
name: k,
value: details[k],
displayvalue: this.getDisplayValue(k, details[k]),
values: k === 'access_methods' ? details[k].split(',') : null,
edit: false
}
})
this.previousVnfNics = this.vnfNics.slice()
this.previousVnfDetails = this.vnfDetails.slice()
this.closeVnfDetail()
this.closeVnfNicModal()
}).catch(error => {
this.$notifyError(error)
this.fetchData()
}).finally(f => {
})
},
getDisplayValue (name, value) {
return name.includes('password') ? '********' : value
},
showEditVnfDetail (index) {
this.vnfDetails[index].edit = true
this.vnfDetails[index].originalValue = this.vnfDetails[index].value
},
hideEditVnfDetail (index) {
this.vnfDetails[index].edit = false
this.vnfDetails[index].value = this.vnfDetails[index].originalValue
},
handleInputChange (val, index) {
this.vnfDetails[index].value = val
},
updateVnfDetail (index) {
if (Array.isArray(this.vnfDetails[index].values) && this.vnfDetails[index].values.length > 0) {
this.vnfDetails[index].value = this.vnfDetails[index].values.join(',')
}
this.vnfDetails[index].value = this.vnfDetails[index].displayvalue
this.vnfDetails[index].displayvalue = this.getDisplayValue(this.vnfDetails[index].name, this.vnfDetails[index].value)
this.updateVnfTemplateDetails()
},
onShowAddVnfDetail () {
this.showAddVnfDetail = true
setTimeout(() => {
this.$refs.keyElm.focus()
})
},
addVnfDetail () {
if (this.newKey === '' || (this.newValue === '' && this.newValues.length === 0)) {
this.error = this.$t('message.error.provide.setting')
return
}
this.error = false
this.newValueString = ''
if (this.newValues.length > 0) {
this.newValueString = this.newValues.join(',')
} else {
this.newValueString = this.newValue
}
this.vnfDetails.push({ name: this.newKey, value: this.newValueString })
this.updateVnfTemplateDetails()
},
deleteVnfDetail (index) {
this.vnfDetails.splice(index, 1)
this.updateVnfTemplateDetails()
},
closeVnfDetail () {
this.newKey = ''
this.newValue = ''
this.newValues = []
this.error = false
this.showAddVnfDetail = false
},
closeVnfNicModal () {
this.showAddVnfNic = false
this.showEditVnfNic = false
this.newVnfNic = {
deviceid: null,
name: null,
required: true,
management: false,
description: null
}
}
}
}
</script>
<style lang="less" scoped>
.row-element {
margin-top: 15px;
margin-bottom: 15px;
}
.detail-input {
width: calc(calc(100% / 2) - 45px);
}
.detail-button {
width: 30px;
}
.shift-btns {
display: flex;
}
.shift-btn {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
font-size: 12px;
&:not(:last-child) {
margin-right: 5px;
}
&--rotated {
font-size: 10px;
transform: rotate(90deg);
}
}
.new-vnf-nic {
&__item {
margin-bottom: 10px;
}
&__label {
margin-bottom: 5px;
font-weight: bold;
}
&__required {
margin-right: 5px;
color: red;
}
}
.update-vnf-nic {
&__item {
margin-bottom: 10px;
}
&__label {
margin-bottom: 5px;
font-weight: bold;
}
&__required {
margin-right: 5px;
color: red;
}
}
</style>

View File

@ -205,7 +205,7 @@ export default {
}, },
data () { data () {
return { return {
templatetypes: ['BUILTIN', 'USER', 'SYSTEM', 'ROUTING'], templatetypes: ['BUILTIN', 'USER', 'SYSTEM', 'ROUTING', 'VNF'],
rootDisk: {}, rootDisk: {},
nicAdapterType: {}, nicAdapterType: {},
keyboardType: {}, keyboardType: {},

View File

@ -0,0 +1,159 @@
// 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.
<template>
<a-table
size="small"
style="overflow-y: auto"
:columns="columns"
:dataSource="virtualmachines"
:rowKey="item => item.id"
:pagination="false"
:loading="fetchLoading"
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'name'">
<router-link :to="{ path: '/vnfapp/' + record.id }" >{{ text }}</router-link>
</template>
<template v-if="column.key === 'status'">
<status class="status" :text="record.state" displayText />
</template>
<template v-if="column.key === 'vmip'">
<status class="status" :text="record.vmip" displayText />
</template>
<template v-if="column.key === 'templatename'">
<router-link :to="{ path: '/template/' + record.templateid }" >{{ record.templatename || record.templateid }}</router-link>
</template>
<template v-if="column.key === 'osdisplayname'">
<status class="status" :text="record.osdisplayname" displayText />
</template>
<template v-if="column.key === 'hostname'">
<router-link :to="{ path: '/host/' + record.hostid }" >{{ record.hostname || record.hostid }}</router-link>
</template>
</template>
</a-table>
</template>
<script>
import { api } from '@/api'
import Status from '@/components/widgets/Status'
export default {
name: 'VnfAppliancesTab',
components: {
Status
},
props: {
resource: {
type: Object,
required: true
},
loading: {
type: Boolean,
default: false
}
},
data () {
return {
fetchLoading: false,
virtualmachines: [],
columns: [
{
key: 'name',
title: this.$t('label.name'),
dataIndex: 'name'
},
{
key: 'status',
title: this.$t('label.status'),
dataIndex: 'state'
},
{
title: this.$t('label.ipaddress'),
dataIndex: 'vmip'
},
{
key: 'templatename',
title: this.$t('label.templatename'),
dataIndex: 'templatename'
},
{
title: this.$t('label.osdisplayname'),
dataIndex: 'osdisplayname'
},
{
key: 'hostname',
title: this.$t('label.hostname'),
dataIndex: 'hostname'
}
]
}
},
created () {
this.fetchData()
},
watch: {
resource: {
deep: true,
handler (newItem) {
if (!newItem || !newItem.id) {
return
}
this.fetchData()
}
}
},
methods: {
fetchData () {
var params = {
details: 'servoff,tmpl,nics',
isVnf: true,
listAll: true
}
if (this.$route.fullPath.startsWith('/vpc')) {
params.vpcid = this.resource.id
} else {
params.networkid = this.resource.id
}
this.fetchLoading = true
api('listVirtualMachines', params).then(json => {
this.virtualmachines = json.listvirtualmachinesresponse.virtualmachine || []
for (const vm of this.virtualmachines) {
for (const vmnic of vm.nic) {
if (vmnic.networkid === this.resource.id) {
vm.vmip = vmnic.ipaddress
}
}
}
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
this.fetchLoading = false
})
}
}
}
</script>
<style lang="scss" scoped>
.status {
margin-top: -5px;
&--end {
margin-left: 5px;
}
}
</style>

View File

@ -360,6 +360,9 @@
<a-tab-pane :tab="$t('label.virtual.routers')" key="vr" v-if="$store.getters.userInfo.roletype === 'Admin'"> <a-tab-pane :tab="$t('label.virtual.routers')" key="vr" v-if="$store.getters.userInfo.roletype === 'Admin'">
<RoutersTab :resource="resource" :loading="loading" /> <RoutersTab :resource="resource" :loading="loading" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane :tab="$t('label.vnf.appliances')" key="vnf" v-if="'deployVnfAppliance' in $store.getters.apis">
<VnfAppliancesTab :resource="resource" :loading="loading" />
</a-tab-pane>
<a-tab-pane :tab="$t('label.events')" key="events" v-if="'listEvents' in $store.getters.apis"> <a-tab-pane :tab="$t('label.events')" key="events" v-if="'listEvents' in $store.getters.apis">
<events-tab :resource="resource" resourceType="Vpc" :loading="loading" /> <events-tab :resource="resource" resourceType="Vpc" :loading="loading" />
</a-tab-pane> </a-tab-pane>
@ -382,6 +385,7 @@ import Status from '@/components/widgets/Status'
import IpAddressesTab from './IpAddressesTab' import IpAddressesTab from './IpAddressesTab'
import RoutersTab from './RoutersTab' import RoutersTab from './RoutersTab'
import VpcTiersTab from './VpcTiersTab' import VpcTiersTab from './VpcTiersTab'
import VnfAppliancesTab from './VnfAppliancesTab'
import EventsTab from '@/components/view/EventsTab' import EventsTab from '@/components/view/EventsTab'
import AnnotationsTab from '@/components/view/AnnotationsTab' import AnnotationsTab from '@/components/view/AnnotationsTab'
import ResourceIcon from '@/components/view/ResourceIcon' import ResourceIcon from '@/components/view/ResourceIcon'
@ -394,6 +398,7 @@ export default {
IpAddressesTab, IpAddressesTab,
RoutersTab, RoutersTab,
VpcTiersTab, VpcTiersTab,
VnfAppliancesTab,
EventsTab, EventsTab,
AnnotationsTab, AnnotationsTab,
ResourceIcon ResourceIcon