summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/pom.xml5
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml1
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/pom.xml1
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/pom.xml99
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/VnfPackageRepository.java72
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VnfPackageRepositoryImpl.java223
-rw-r--r--openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/recipes/ON_5_setup_configuration.rb14
-rw-r--r--openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/vnfrepo-configuration.yaml.erb4
-rw-r--r--openecomp-be/dist/sdc-onboard-backend-docker/artifacts/startup.sh3
-rw-r--r--openecomp-ui/resources/scss/_components.scss1
-rw-r--r--openecomp-ui/resources/scss/components/_vnfBrowse.scss109
-rw-r--r--openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss53
-rw-r--r--openecomp-ui/src/nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx73
-rw-r--r--openecomp-ui/src/nfvo-utils/RestAPIUtil.js3
-rw-r--r--openecomp-ui/src/nfvo-utils/i18n/en.json9
-rw-r--r--openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js7
-rw-r--r--openecomp-ui/src/sdc-app/config/config.json3
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js54
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js7
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js8
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx69
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImport.js41
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportActionHelper.js180
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportConstants.js21
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportReducer.js31
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportView.jsx309
26 files changed, 1375 insertions, 25 deletions
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/pom.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/pom.xml
index c8b238389a..f2913ff522 100644
--- a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/pom.xml
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/pom.xml
@@ -35,6 +35,11 @@
<artifactId>openecomp-sdc-notification-core</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.onboarding</groupId>
+ <artifactId>vnf-repository-rest-services</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.openecomp.sdc.onboarding</groupId>
<artifactId>vendor-software-products-rest-services</artifactId>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml
index ef1e7247dc..6b812cc995 100644
--- a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml
@@ -70,6 +70,7 @@
<ref bean="deploymentFlavors"/>
<ref bean="images"/>
<ref bean="orchestrationTemplateCandidate"/>
+ <ref bean="vnfPackageRepository"/>
<ref bean="componentDependencies"/>
<ref bean="healthCheck"/>
<ref bean="itemPermissions"/>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/pom.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/pom.xml
index cef2e711b8..a88733e60a 100644
--- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/pom.xml
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/pom.xml
@@ -17,5 +17,6 @@
<modules>
<module>/vendor-software-products-rest-services</module>
<module>/vendor-software-products-rest-types</module>
+ <module>/vnf-repository-rest-services</module>
</modules>
</project>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/pom.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/pom.xml
new file mode 100644
index 0000000000..1829ceb542
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>vnf-repository-rest-services</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc.onboarding</groupId>
+ <artifactId>vendor-software-products-rest</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>${spring.framework.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.framework.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ <version>${spring.framework.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <version>${spring.framework.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.framework.version}</version>
+ </dependency>
+
+ <!-- CXF -->
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${http.client.version}</version>
+ </dependency>
+
+
+ <!-- Java Stuff -->
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <version>${javax.inject.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>openecomp-sdc-common-rest</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.common</groupId>
+ <artifactId>openecomp-configuration-management-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.common</groupId>
+ <artifactId>openecomp-configuration-management-core</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.onboarding</groupId>
+ <artifactId>vendor-software-products-rest-services</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/VnfPackageRepository.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/VnfPackageRepository.java
new file mode 100644
index 0000000000..1a16e1f0fb
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/VnfPackageRepository.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.openecomp.sdcrests.vsp.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.File;
+import static org.openecomp.sdcrests.common.RestConstants.USER_ID_HEADER_PARAM;
+import static org.openecomp.sdcrests.common.RestConstants.USER_MISSING_ERROR_MSG;
+
+@Path("/v1.0/vendor-software-products/{vspId}/versions/{versionId}/vnfrepository")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+@Api(value = "VNF Repository packages")
+@Validated
+public interface VnfPackageRepository extends VspEntities {
+
+ @GET
+ @Path("/vnfpackages")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Get VNF packages from VNF Repository", notes = "Call VNF Repostory to get VNF package details", response = File.class)
+ Response getVnfPackages(@PathParam("vspId") String vspId,
+ @ApiParam(value = "Version Id") @PathParam("versionId") String versionId,
+ @NotNull(message = USER_MISSING_ERROR_MSG) @HeaderParam(USER_ID_HEADER_PARAM) String user) throws Exception;
+
+ @GET
+ @Path("/vnfpackage/{csarId}/download")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download VNF package from VNF Repository", notes = "Download VNF package from VNF repository and send to client", response = File.class)
+ Response downloadVnfPackage(@PathParam("vspId") String vspId,
+ @ApiParam(value = "Version Id") @PathParam("versionId") String versionId,
+ @PathParam("csarId") String csarId,
+ @NotNull(message = USER_MISSING_ERROR_MSG) @HeaderParam(USER_ID_HEADER_PARAM) String user) throws Exception;
+
+ @POST
+ @Path("/vnfpackage/{csarId}/import")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Import VNF package from VNF Repository", notes = "Call VNF Repostory to download VNF package, validate it and send the response", response = UploadFileResponseDto.class)
+ Response importVnfPackage(@PathParam("vspId") String vspId,
+ @ApiParam(value = "Version Id") @PathParam("versionId") String versionId,
+ @PathParam("csarId") String csarId,
+ @NotNull(message = USER_MISSING_ERROR_MSG) @HeaderParam(USER_ID_HEADER_PARAM) String user) throws Exception;
+
+}
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VnfPackageRepositoryImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VnfPackageRepositoryImpl.java
new file mode 100644
index 0000000000..439fd1475f
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vnf-repository-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VnfPackageRepositoryImpl.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.openecomp.sdcrests.vsp.rest.services;
+
+import static javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION;
+import static org.openecomp.core.utilities.file.FileUtils.getFileExtension;
+import static org.openecomp.core.utilities.file.FileUtils.getNetworkPackageName;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.inject.Named;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.config.api.Configuration;
+import org.openecomp.config.api.ConfigurationManager;
+import org.openecomp.sdc.common.http.client.api.HttpRequest;
+import org.openecomp.sdc.common.http.client.api.HttpResponse;
+import org.openecomp.sdc.logging.api.Logger;
+import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
+import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
+import org.openecomp.sdc.versioning.VersioningManager;
+import org.openecomp.sdc.versioning.VersioningManagerFactory;
+import org.openecomp.sdc.versioning.dao.types.Version;
+import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
+import org.openecomp.sdcrests.vsp.rest.VnfPackageRepository;
+import org.openecomp.sdcrests.vsp.rest.mapping.MapUploadFileResponseToUploadFileResponseDto;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class implements the API interface with VNF Repository (VNFSDK) such as
+ * i) Get all the VNF Package Meta-data ii) Download the VNF Package iii) Import
+ * VNF package to SDC catalog (Download & validate)
+ *
+ * @version Amsterdam release (ONAP 1.0)
+ */
+@Named
+@Service("vnfPackageRepository")
+@Scope(value = "prototype")
+public class VnfPackageRepositoryImpl implements VnfPackageRepository {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VnfPackageRepositoryImpl.class);
+
+ private static boolean initFlag = false;
+
+ // Default VNF Repository configuration
+ private static final String CONFIG_NAMESPACE = "vnfrepo";
+
+ // Default address for VNF repository docker
+ private static final String DEF_DOCKER_COMPOSE_ADDR = "127.0.0.1";
+
+ private static String ipAddress = DEF_DOCKER_COMPOSE_ADDR;
+
+ // Default Download package URI and Get VNF package meta-data URI -
+ // configurable
+ private static String getVnfPkgUri = "/onapapi/vnfsdk-marketplace/v1/PackageResource/csars";
+
+ private static String downldPkgUri = "/onapapi/vnfsdk-marketplace/v1/PackageResource/csars/%s/files";
+
+ // Default port for VNF Repository
+ private static String port = "8702";
+
+ @Override
+ public Response getVnfPackages(String vspId, String versionId, String user) throws Exception {
+
+ LOGGER.debug("Get VNF Packages from Repository:{}", vspId);
+
+ // Step 1: Create REST client and configuration and prepare URI
+ init();
+
+ // Step 2: Build URI based on the IP address and port allocated
+ HttpResponse<String> rsp = HttpRequest.get(getVnfPkgUri);
+ if(HttpStatus.SC_OK != rsp.getStatusCode()) {
+ LOGGER.error("Failed to query VNF package metadata:uri={}, Response={}", getVnfPkgUri, rsp);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ // Step 3: Send the response to the client
+ LOGGER.debug("Response from VNF Repository: {}", rsp.getResponse());
+
+ return Response.ok(rsp.getResponse()).build();
+ }
+
+ @Override
+ public Response importVnfPackage(String vspId, String versionId, String csarId, String user) throws Exception {
+
+ LOGGER.debug("Import VNF Packages from Repository:{}", csarId);
+
+ // Step 1: Create REST client and configuration and prepare URI
+ init();
+
+ // Step 2: Build URI based on the IP address and port allocated
+ String uri = String.format(downldPkgUri, csarId);
+ HttpResponse<String> rsp = HttpRequest.get(uri);
+ if(HttpStatus.SC_OK != rsp.getStatusCode()) {
+ LOGGER.error("Failed to download package from VNF Repository:uri={}, Response={}", uri, rsp);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ LOGGER.debug("Response from VNF Repository for download package is success ");
+
+ // Step 3: Import the file to SDC and validate and send the response
+ try (InputStream fileStream = new BufferedInputStream(
+ new ByteArrayInputStream(rsp.getResponse().getBytes(StandardCharsets.ISO_8859_1)))) {
+
+ String filename = "temp_" + csarId + ".csar";
+ OrchestrationTemplateCandidateManager candidateManager =
+ OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface();
+ UploadFileResponse uploadFileResponse = candidateManager.upload(vspId, getVersion(vspId, versionId),
+ fileStream, getFileExtension(filename), getNetworkPackageName(filename));
+
+ UploadFileResponseDto uploadFileResponseDto = new MapUploadFileResponseToUploadFileResponseDto()
+ .applyMapping(uploadFileResponse, UploadFileResponseDto.class);
+
+ return Response.ok(uploadFileResponseDto).build();
+ } catch(Exception e) {
+ // Exception while uploading file
+
+ LOGGER.error("Exception while uploading VNF package received from VNF Repository:", e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+ }
+
+ @Override
+ public Response downloadVnfPackage(String vspId, String versionId, String csarId, String user) throws Exception {
+
+ LOGGER.debug("Download VNF Packages from Repository:csarId={}", csarId);
+
+ // Step 1: Create REST client and configuration and prepare URI
+ init();
+
+ // Step 2: Build URI based on the IP address and port allocated
+ String uri = String.format(downldPkgUri, csarId);
+ HttpResponse<String> rsp = HttpRequest.get(uri);
+ if(HttpStatus.SC_OK != rsp.getStatusCode()) {
+ LOGGER.error("Failed to download package from VNF Repository:uri={}, Response={}", uri, rsp);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ // Step 3:Send response to the client
+ String filename = "temp_" + csarId + ".csar";
+ Response.ResponseBuilder response = Response.ok(rsp.getResponse().getBytes(StandardCharsets.ISO_8859_1));
+ response.header(CONTENT_DISPOSITION, "attachment; filename=" + filename);
+
+ LOGGER.debug("Response from VNF Repository for download package is success ");
+
+ return response.build();
+ }
+
+ private Version getVersion(String vspId, String versionId) {
+ // Get list of Versions from the rest call
+ VersioningManager versioningManager = VersioningManagerFactory.getInstance().createInterface();
+
+ // Find the corresponding version from versionId
+ return versioningManager.list(vspId).stream().filter(ver -> ver.getId() != versionId).findAny()
+ .orElse(new Version(versionId));
+ }
+
+ private static void setVnfRepoConfig() {
+
+ try {
+ // Step 1: Fetch the on-boarding configuration
+ Configuration config = ConfigurationManager.lookup();
+
+ String vnfRepoHost = config.getAsString(CONFIG_NAMESPACE, "vnfRepoHost");
+ if(null != vnfRepoHost) {
+ ipAddress = vnfRepoHost;
+ }
+
+ String vnfRepoPort = config.getAsString(CONFIG_NAMESPACE, "vnfRepoPort");
+ if(null != vnfRepoPort) {
+ port = vnfRepoPort;
+ }
+
+ String getVnfUri = config.getAsString(CONFIG_NAMESPACE, "getVnfUri");
+ if(null != getVnfUri) {
+ getVnfPkgUri = getVnfUri;
+ }
+
+ String downloadVnfUri = config.getAsString(CONFIG_NAMESPACE, "downloadVnfUri");
+ if(null != downloadVnfUri) {
+ downldPkgUri = downloadVnfUri;
+ }
+
+ } catch(Exception e) {
+ LOGGER.error("Failed to load configuration, Exception caught, using default configuration", e);
+ }
+
+ getVnfPkgUri =
+ new StringBuilder("http://").append(ipAddress).append(":").append(port).append(getVnfPkgUri).toString();
+
+ downldPkgUri =
+ new StringBuilder("http://").append(ipAddress).append(":").append(port).append(downldPkgUri).toString();
+ }
+
+ private static synchronized void init() throws Exception {
+ if(!initFlag) {
+ // Step 1: Initialize configuration
+ setVnfRepoConfig();
+
+ initFlag = true;
+ }
+ }
+}
diff --git a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/recipes/ON_5_setup_configuration.rb b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/recipes/ON_5_setup_configuration.rb
index 62fe1265b7..747c735b84 100644
--- a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/recipes/ON_5_setup_configuration.rb
+++ b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/recipes/ON_5_setup_configuration.rb
@@ -17,4 +17,18 @@ template "onboard-be-config" do
:cassandra_truststore_password => node['cassandra'][:truststore_password],
:cassandra_ssl_enabled => "#{ENV['cassandra_ssl_enabled']}"
})
+end
+
+
+
+template "VnfrepoConfiguration" do
+ path "#{ENV['JETTY_BASE']}/config/onboarding-be/config-vnfrepo.yaml"
+ source "vnfrepo-configuration.yaml.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables({
+ :VNFREPO_IP => node['VnfRepo']['vnfRepoHost'],
+ :VNFREPO_PORT => node['VnfRepo']['vnfRepoPort']
+ })
end \ No newline at end of file
diff --git a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/vnfrepo-configuration.yaml.erb b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/vnfrepo-configuration.yaml.erb
new file mode 100644
index 0000000000..07e26629f0
--- /dev/null
+++ b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/chef-repo/cookbooks/sdc-onboard-backend/templates/default/vnfrepo-configuration.yaml.erb
@@ -0,0 +1,4 @@
+vnfRepoPort: <%= @VNFREPO_PORT %>
+vnfRepoHost: <%= @VNFREPO_IP %>
+getVnfUri: /onapapi/vnfsdk-marketplace/v1/PackageResource/csars
+downloadVnfUri: /onapapi/vnfsdk-marketplace/v1/PackageResource/csars/%s/files \ No newline at end of file
diff --git a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/startup.sh b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/startup.sh
index 1517f72202..413fdb317e 100644
--- a/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/startup.sh
+++ b/openecomp-be/dist/sdc-onboard-backend-docker/artifacts/startup.sh
@@ -14,7 +14,8 @@ JAVA_OPTIONS=" ${JAVA_OPTIONS} \
-Dconfig.home=${JETTY_BASE}/config \
-Dlog.home=${JETTY_BASE}/logs \
-Dlogback.configurationFile=${JETTY_BASE}/config/onboarding-be/logback.xml \
- -Dconfiguration.yaml=${JETTY_BASE}/config/onboarding-be/onboarding_configuration.yaml"
+ -Dconfiguration.yaml=${JETTY_BASE}/config/onboarding-be/onboarding_configuration.yaml \
+ -Dconfig.location=${JETTY_BASE}/config/onboarding-be/."
cd /var/lib/jetty
diff --git a/openecomp-ui/resources/scss/_components.scss b/openecomp-ui/resources/scss/_components.scss
index e18b2603e9..7bd90100e2 100644
--- a/openecomp-ui/resources/scss/_components.scss
+++ b/openecomp-ui/resources/scss/_components.scss
@@ -22,6 +22,7 @@
@import "components/userNotifications";
@import "components/overlay";
@import "components/vspDetailsVendorSelect";
+@import "components/vnfBrowse";
%noselect {
-webkit-touch-callout: none;
diff --git a/openecomp-ui/resources/scss/components/_vnfBrowse.scss b/openecomp-ui/resources/scss/components/_vnfBrowse.scss
new file mode 100644
index 0000000000..7e0085af8a
--- /dev/null
+++ b/openecomp-ui/resources/scss/components/_vnfBrowse.scss
@@ -0,0 +1,109 @@
+$message-info-icon-size: 16px;
+
+.vnf-creation-page {
+ .list-editor-view-header {
+ border-bottom: none;
+ }
+ .vnfBrowse-list-item {
+ display: flex;
+ height: 36px;
+ @extend .body-1;
+ &.header {
+ @extend .body-1-semibold;
+ background-color: $tlv-light-gray;
+ color: $text-black;
+ }
+ &.selectedRow {
+ background-color: $blue;
+ color: $white;
+ .svg-icon-wrapper {
+ &.__positive {
+ fill: $white;
+ color: $white;
+ }
+ }
+ }
+ .svg-icon-wrapper {
+ &.__positive {
+ fill: $dark-gray;
+ color: $dark-gray;
+ }
+ }
+ }
+
+ .activity-action {
+ .svg-icon-wrapper {
+ float: left;
+ }
+ }
+
+ .message-further-info-icon {
+ background-color: $gray;
+ }
+
+ .table-cell {
+ border-right: 1px solid $light-gray;
+ border-bottom: 1px solid $light-gray;
+ &:last-child {
+ border-right: none;
+ }
+ flex-basis: 22%;
+ display: flex;
+ padding: 0 20px;
+ justify-content: center;
+ flex-direction: column;
+
+ &.vnftable-action {
+ flex-basis: 12%;
+ span {
+ margin: auto;
+ }
+ }
+}
+
+ .vnf-table-header {
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ .header-sort-arrow {
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ margin-left: 9px;
+ &.up {
+ border-bottom: 5px solid $black;
+ }
+ &.down {
+ border-top: 5px solid $black;
+ }
+
+ }
+ }
+
+ .vnf-table-cell {
+ display: flex;
+ justify-content: space-between;
+ span {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+ .vnftable-name {
+ max-width: 22%;
+ }
+
+ .vnf-grid-section {
+ margin: 20px 20px 20px 50px;
+ }
+
+ .vnf-modal {
+ text-align: right;
+ margin-top: 22px;
+ }
+
+ .vnf-submit {
+ margin-right: 15px;
+ }
+
+} \ No newline at end of file
diff --git a/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss b/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss
index 99027d66ed..8d124c3b40 100644
--- a/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss
+++ b/openecomp-ui/resources/scss/modules/_softwareProductLandingPage.scss
@@ -160,12 +160,65 @@
color: $light-blue;
}
}
+ }
.software-product-landing-view-top-block-col-upl {
@extend .flex;
+ height: 215px;
+ text-align: center;
+ flex-direction: column;
+ justify-content: center;
+ border: 2px dashed $light-gray;
margin-bottom: 20px;
+ @extend .body-1;
+ align-items: center;
+ .upload-btn {
+ padding: 15px 55px;
}
+ .drag-text {
+ color: $blue;
+ @extend .body-1-semibold;
+ }
+ .or-text {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ color: $light-gray;
+ }
+ .upload {
+ width: 50%;
+ border : 0px !important;
+ }
+ .vnfRepo {
+ width: 50%;
+ cursor: pointer;
+ .searchRepo-text {
+ color: $blue;
+ @extend .body-1-semibold;
+ width: 72px;
+ line-height: 24px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ .svg-icon-wrapper {
+ .svg-icon.__search {
+ width: 34px;
+ height: 34px;
+ margin-top: 10px;
+ }
+ &.__positive {
+ fill: $blue;
+ color: $blue;
+ }
+ }
+ }
+ .verticalLine {
+ height: 90%;
+ border-left: 1px solid $light-gray;
+ }
+ }
+ .showVnf {
+ flex-direction: row;
}
}
}
diff --git a/openecomp-ui/src/nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx b/openecomp-ui/src/nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx
new file mode 100644
index 0000000000..ab8a18b4c6
--- /dev/null
+++ b/openecomp-ui/src/nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx
@@ -0,0 +1,73 @@
+/*!
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+import React, { Component } from 'react';
+import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx';
+import Configuration from 'sdc-app/config/Configuration.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
+
+function VNFBrowse({ onBrowseVNF, isReadOnlyMode }) {
+ if (!Configuration.get('showBrowseVNF')) {
+ return <div />;
+ } else {
+ return (
+ <div
+ className={`${'vnfRepo'}${isReadOnlyMode ? ' disabled' : ''}`}
+ onClick={onBrowseVNF}>
+ <div className={`${'searchRepo-text'}`}>
+ {i18n('Search in Repository')}
+ </div>
+ <SVGIcon
+ name="search"
+ color="positive"
+ iconClassName="searchIcon"
+ />
+ </div>
+ );
+ }
+}
+
+class VnfRepositorySearchBox extends Component {
+ render() {
+ let {
+ className,
+ onClick,
+ onBrowseVNF,
+ dataTestId,
+ isReadOnlyMode
+ } = this.props;
+ let showVNF = Configuration.get('showBrowseVNF');
+ return (
+ <div className={`${className}${isReadOnlyMode ? ' disabled' : ''}`}>
+ <DraggableUploadFileBox
+ dataTestId={dataTestId}
+ isReadOnlyMode={isReadOnlyMode}
+ className={'upload'}
+ onClick={onClick}
+ />
+
+ <div className={`${'verticalLine'}${showVNF ? '' : ' hide'}`} />
+
+ <VNFBrowse
+ onBrowseVNF={onBrowseVNF}
+ isReadOnlyMode={isReadOnlyMode}
+ />
+ </div>
+ );
+ }
+}
+export default VnfRepositorySearchBox;
diff --git a/openecomp-ui/src/nfvo-utils/RestAPIUtil.js b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js
index 1a5817de66..6be5db765c 100644
--- a/openecomp-ui/src/nfvo-utils/RestAPIUtil.js
+++ b/openecomp-ui/src/nfvo-utils/RestAPIUtil.js
@@ -41,6 +41,9 @@ const CONTENT_MD5_HEADER = 'Content-MD5';
function applySecurity(options, data) {
let headers = options.headers || (options.headers = {});
+ if (options.isAnonymous) {
+ return;
+ }
let authToken = localStorage.getItem(STORAGE_AUTH_KEY);
if (authToken) {
diff --git a/openecomp-ui/src/nfvo-utils/i18n/en.json b/openecomp-ui/src/nfvo-utils/i18n/en.json
index cbc2031b4b..10ddb4202e 100644
--- a/openecomp-ui/src/nfvo-utils/i18n/en.json
+++ b/openecomp-ui/src/nfvo-utils/i18n/en.json
@@ -620,5 +620,14 @@
"VSPQuestionnaire/general/storageDataReplication/storageReplicationFrequency" : "Storage Replication Frequency",
"VSPQuestionnaire/general/storageDataReplication/storageReplicationDestination" : "Storage Replication Destination",
+ "VNF List Title": "VNF List",
+ "VNF import failed title" : "VNF import failed",
+ "VNF import failed msg" : "VNF Repository Server is not responding or not reachable. Please check server address in configuration file.",
+ "VNF Header Name" : "Name",
+ "VNF Header Version" : "Version",
+ "VNF Header Vendor" : "Vendor",
+ "VNF Header Desc" : "Description",
+ "VNF Header Action" : "Action",
+
"GENERIC_ERROR": "An error has occurred. Please contact your System Administrator for further assistance."
}
diff --git a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
index 5b28c5d7fa..745f01d0eb 100644
--- a/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
+++ b/openecomp-ui/src/sdc-app/common/modal/ModalContentMapper.js
@@ -24,6 +24,7 @@ import NICCreation from 'sdc-app/onboarding/softwareProduct/components/network/N
import SoftwareProductComponentsNICEditor from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNICEditor.js';
import ComponentCreation from 'sdc-app/onboarding/softwareProduct/components/creation/SoftwareProductComponentCreation.js';
import SoftwareProductDeploymentEditor from 'sdc-app/onboarding/softwareProduct/deployment/editor/SoftwareProductDeploymentEditor.js';
+import VNFImport from 'sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImport.js';
import PermissionsManager from 'sdc-app/onboarding/permissions/PermissionsManager.js';
import CommitCommentModal from 'nfvo-components/panel/versionController/components/CommitCommentModal.jsx';
import Tree from 'nfvo-components/tree/Tree.jsx';
@@ -48,7 +49,8 @@ export const modalContentMapper = {
VERSION_TREE: 'VERSION_TREE',
MERGE_EDITOR: 'MERGE_EDITOR',
REVISIONS_LIST: 'REVISIONS_LIST',
- VENDOR_SELECTOR: 'VENDOR_SELECTOR'
+ VENDOR_SELECTOR: 'VENDOR_SELECTOR',
+ VNF_IMPORT: 'VNF_IMPORT'
};
export const modalContentComponents = {
@@ -67,5 +69,6 @@ export const modalContentComponents = {
VERSION_TREE: Tree,
MERGE_EDITOR: MergeEditor,
REVISIONS_LIST: Revisions,
- VENDOR_SELECTOR: VendorSelector
+ VENDOR_SELECTOR: VendorSelector,
+ VNF_IMPORT: VNFImport
};
diff --git a/openecomp-ui/src/sdc-app/config/config.json b/openecomp-ui/src/sdc-app/config/config.json
index fbfaf1de66..e9e0b55114 100644
--- a/openecomp-ui/src/sdc-app/config/config.json
+++ b/openecomp-ui/src/sdc-app/config/config.json
@@ -7,5 +7,6 @@
"defaultRestCatalogPrefix": "/sdc1/feProxy/rest",
"defaultWebsocketPort" : "8181",
"defaultDebugWebsocketPort" : "9000",
- "defaultWebsocketPath" : "notification-api/ws/notificationHandler"
+ "defaultWebsocketPath" : "notification-api/ws/notificationHandler",
+ "showBrowseVNF" : true
}
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
index 25bd32e468..877c7869bd 100644
--- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js
@@ -81,6 +81,12 @@ function uploadFile(vspId, formData, version) {
);
}
+function uploadVNFFile(csarId, softwareProductId, version) {
+ let verId = typeof version === 'object' ? version.id : version;
+ return RestAPIUtil.post(
+ `${baseUrl()}${softwareProductId}/versions/${verId}/vnfrepository/vnfpackage/${csarId}/import`
+ );
+}
function putSoftwareProduct({ softwareProduct, version }) {
return RestAPIUtil.put(
`${baseUrl()}${softwareProduct.id}/versions/${version.id}`,
@@ -421,6 +427,54 @@ const SoftwareProductActionHelper = {
});
},
+ uploadVNFFile(
+ dispatch,
+ { csarId, failedNotificationTitle, softwareProductId, version }
+ ) {
+ dispatch({
+ type: HeatSetupActions.FILL_HEAT_SETUP_CACHE,
+ payload: {}
+ });
+
+ Promise.resolve()
+ .then(() => uploadVNFFile(csarId, softwareProductId, version))
+ .then(response => {
+ if (response.status === 'Success') {
+ dispatch({
+ type: commonActionTypes.DATA_CHANGED,
+ deltaData: {
+ onboardingOrigin: response.onboardingOrigin
+ },
+ formName: forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
+ });
+ switch (response.onboardingOrigin) {
+ case onboardingOriginTypes.ZIP:
+ OnboardingActionHelper.navigateToSoftwareProductAttachmentsSetupTab(
+ dispatch,
+ { softwareProductId, version }
+ );
+ break;
+ case onboardingOriginTypes.CSAR:
+ OnboardingActionHelper.navigateToSoftwareProductAttachmentsValidationTab(
+ dispatch,
+ { softwareProductId, version }
+ );
+ break;
+ }
+ } else {
+ throw new Error(parseUploadErrorMsg(response.errors));
+ }
+ })
+ .catch(error => {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_ERROR,
+ data: {
+ title: failedNotificationTitle,
+ msg: error.message
+ }
+ });
+ });
+ },
downloadHeatFile(
dispatch,
{ softwareProductId, heatCandidate, isReadOnlyMode, version }
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js
index f3de517a1c..fd4f02cde1 100644
--- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/SoftwareProductReducer.js
@@ -56,6 +56,8 @@ import {
import { NIC_QUESTIONNAIRE } from 'sdc-app/onboarding/softwareProduct/components/network/SoftwareProductComponentsNetworkConstants.js';
import { IMAGE_QUESTIONNAIRE } from 'sdc-app/onboarding/softwareProduct/components/images/SoftwareProductComponentsImageConstants.js';
+import VNFImportReducer from './vnfMarketPlace/VNFImportReducer.js';
+
export default combineReducers({
softwareProductAttachments: combineReducers({
attachmentsDetails: SoftwareProductAttachmentsReducer,
@@ -150,5 +152,8 @@ export default combineReducers({
}
return state;
},
- softwareProductQuestionnaire: createJSONSchemaReducer(PRODUCT_QUESTIONNAIRE)
+ softwareProductQuestionnaire: createJSONSchemaReducer(
+ PRODUCT_QUESTIONNAIRE
+ ),
+ VNFMarketPlaceImport: VNFImportReducer
});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
index 34bfceec24..f5f3b7ebdb 100644
--- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPage.js
@@ -21,6 +21,7 @@ import { actionTypes as modalActionTypes } from 'nfvo-components/modal/GlobalMod
import { onboardingMethod } from '../SoftwareProductConstants.js';
import ScreensHelper from 'sdc-app/common/helpers/ScreensHelper.js';
import { enums, screenTypes } from 'sdc-app/onboarding/OnboardingConstants.js';
+import VNFImportActionHelper from '../vnfMarketPlace/VNFImportActionHelper.js';
export const mapStateToProps = ({
softwareProduct,
@@ -137,7 +138,12 @@ const mapActionsToProps = (dispatch, { version }) => {
props: { softwareProductId, version, componentId }
}),
/** for the next version */
- onAddComponent: () => SoftwareProductActionHelper.addComponent(dispatch)
+ onAddComponent: () =>
+ SoftwareProductActionHelper.addComponent(dispatch),
+
+ onBrowseVNF: currentSoftwareProduct => {
+ VNFImportActionHelper.open(dispatch, currentSoftwareProduct);
+ }
};
};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx
index bc8a2be646..00f0c2a0cb 100644
--- a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/landingPage/SoftwareProductLandingPageView.jsx
@@ -19,7 +19,9 @@ import classnames from 'classnames';
import Dropzone from 'react-dropzone';
import i18n from 'nfvo-utils/i18n/i18n.js';
+import Configuration from 'sdc-app/config/Configuration.js';
import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx';
+import VnfRepositorySearchBox from 'nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx';
import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
import SoftwareProductComponentsList from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js';
@@ -122,26 +124,55 @@ class SoftwareProductLandingPageView extends React.Component {
}
renderProductDetails(isManual, isReadOnlyMode) {
- return (
- <div className="details-panel">
- {!isManual && (
- <div>
- <div className="software-product-landing-view-heading-title">
- {i18n('Software Product Attachments')}
+ let { onBrowseVNF, currentSoftwareProduct } = this.props;
+
+ if (Configuration.get('showBrowseVNF')) {
+ return (
+ <div className="details-panel">
+ {!isManual && (
+ <div>
+ <div className="software-product-landing-view-heading-title">
+ {i18n('Software Product Attachments')}
+ </div>
+ <VnfRepositorySearchBox
+ dataTestId="upload-btn"
+ isReadOnlyMode={isReadOnlyMode}
+ className={classnames(
+ 'software-product-landing-view-top-block-col-upl showVnf',
+ { disabled: isReadOnlyMode }
+ )}
+ onClick={() => this.refs.fileInput.open()}
+ onBrowseVNF={() =>
+ onBrowseVNF(currentSoftwareProduct)
+ }
+ />
</div>
- <DraggableUploadFileBox
- dataTestId="upload-btn"
- isReadOnlyMode={isReadOnlyMode}
- className={classnames(
- 'software-product-landing-view-top-block-col-upl',
- { disabled: isReadOnlyMode }
- )}
- onClick={() => this.refs.fileInput.open()}
- />
- </div>
- )}
- </div>
- );
+ )}
+ </div>
+ );
+ } else {
+ return (
+ <div className="details-panel">
+ {!isManual && (
+ <div>
+ <div className="software-product-landing-view-heading-title">
+ {i18n('Software Product Attachments')}
+ </div>
+ <DraggableUploadFileBox
+ dataTestId="upload-btn"
+ isReadOnlyMode={isReadOnlyMode}
+ className={classnames(
+ 'software-product-landing-view-top-block-col-upl',
+ { disabled: isReadOnlyMode }
+ )}
+ onClick={() => this.refs.fileInput.open()}
+ onBrowseVNF={() => onBrowseVNF()}
+ />
+ </div>
+ )}
+ </div>
+ );
+ }
}
handleImportSubmit(files, isReadOnlyMode, isManual) {
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImport.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImport.js
new file mode 100644
index 0000000000..19efab68c8
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImport.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.
+ */
+
+import { connect } from 'react-redux';
+import VNFImportView from './VNFImportView.jsx';
+import VNFImportActionHelper from './VNFImportActionHelper.js';
+
+export const mapStateToProps = response => {
+ const {
+ softwareProduct: { VNFMarketPlaceImport: { vnfItems } }
+ } = response;
+ return {
+ vnfItems: vnfItems
+ };
+};
+
+export const mapActionsToProps = dispatch => {
+ return {
+ onCancel: () => VNFImportActionHelper.resetData(dispatch),
+ onSubmit: (csarId, selectedVendor) => {
+ VNFImportActionHelper.uploadData(selectedVendor, csarId, dispatch);
+ }
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {
+ withRef: true
+})(VNFImportView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportActionHelper.js
new file mode 100644
index 0000000000..3843330449
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportActionHelper.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.
+ */
+
+import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js';
+import Configuration from 'sdc-app/config/Configuration.js';
+import {
+ actionTypes as modalActionTypes,
+ modalSizes
+} from 'nfvo-components/modal/GlobalModalConstants.js';
+import { modalContentMapper } from 'sdc-app/common/modal/ModalContentMapper.js';
+import SoftwareProductActionHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductActionHelper.js';
+import { actionTypes } from './VNFImportConstants.js';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+function baseUrl(selectedVendor) {
+ const restPrefix = Configuration.get('restPrefix');
+ let vspId = selectedVendor.id;
+ let version = selectedVendor.version;
+ return `${restPrefix}/v1.0/vendor-software-products/${vspId}/versions/${
+ version.id
+ }/vnfrepository`;
+}
+
+function getVNFMarketplace(dispatch, currentSoftwareProduct) {
+ return RestAPIUtil.fetch(`${baseUrl(currentSoftwareProduct)}/vnfpackages`, {
+ isAnonymous: false
+ })
+ .then(response => {
+ dispatch({
+ type: actionTypes.OPEN,
+ response
+ });
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_SHOW,
+ data: {
+ modalComponentName: modalContentMapper.VNF_IMPORT,
+ title: i18n('Browse VNF'),
+ modalComponentProps: {
+ currentSoftwareProduct,
+ size: modalSizes.LARGE
+ }
+ }
+ });
+ })
+ .catch(error => {
+ let errMessage = error.responseJSON
+ ? error.responseJSON.message
+ : i18n('VNF import failed msg');
+
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_ERROR,
+ data: {
+ title: i18n('VNF import failed title'),
+ msg: errMessage,
+ cancelButtonText: i18n('Ok')
+ }
+ });
+ });
+}
+
+function downloadCSARFile(csarId, currSoftwareProduct) {
+ let url = `${baseUrl(currSoftwareProduct)}/vnfpackage/${csarId}/download`;
+ return RestAPIUtil.fetch(url, {
+ dataType: 'binary',
+ isAnonymous: false
+ });
+}
+
+function getFileName(xhr, defaultFilename) {
+ let filename = '';
+ let contentDisposition =
+ xhr && xhr.getResponseHeader('Content-Disposition')
+ ? xhr.getResponseHeader('Content-Disposition')
+ : '';
+ let match = contentDisposition.match(/filename=(.*?)(;|$)/);
+ if (match) {
+ filename = match[1].replace(/['"]/g, '');
+ } else {
+ filename = defaultFilename;
+ }
+ return filename;
+}
+
+function uploadVNFData(csarId, currSoftwareProduct, dispatch) {
+ let softwareProductId = currSoftwareProduct.id;
+ let version = { id: currSoftwareProduct.version };
+
+ SoftwareProductActionHelper.uploadVNFFile(dispatch, {
+ csarId,
+ currSoftwareProduct,
+ failedNotificationTitle: i18n('Upload validation failed'),
+ softwareProductId,
+ version
+ });
+}
+
+function getTimestampString() {
+ let date = new Date();
+ let z = n => (n < 10 ? '0' + n : n);
+ return `${date.getFullYear()}-${z(date.getMonth())}-${z(
+ date.getDate()
+ )}_${z(date.getHours())}-${z(date.getMinutes())}`;
+}
+
+function showFileSaveDialog({ blob, xhr, defaultFilename, addTimestamp }) {
+ let filename = getFileName(xhr, defaultFilename);
+
+ if (addTimestamp) {
+ filename = filename.replace(
+ /(^.*?)\.([^.]+$)/,
+ `$1_${getTimestampString()}.$2`
+ );
+ }
+
+ let link = document.createElement('a');
+
+ let url = URL.createObjectURL(blob.blob);
+
+ link.href = url;
+ link.download = filename;
+ link.style.display = 'none';
+ document.body.appendChild(link);
+ link.click();
+ setTimeout(function() {
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ }, 0);
+}
+
+const VNFImportActionHelper = {
+ open(dispatch, currentSoftwareProduct) {
+ getVNFMarketplace(dispatch, currentSoftwareProduct);
+ },
+
+ download(csarId, currSoftwareProduct) {
+ downloadCSARFile(csarId, currSoftwareProduct).then(
+ (blob, statusText, xhr) =>
+ showFileSaveDialog({
+ blob,
+ xhr,
+ defaultFilename: 'MyNewCSAR.csar',
+ addTimestamp: true
+ })
+ );
+ },
+
+ resetData(dispatch) {
+ dispatch({
+ type: modalActionTypes.GLOBAL_MODAL_CLOSE
+ });
+
+ dispatch({
+ type: actionTypes.RESET_DATA
+ });
+ },
+
+ getVNFMarketplace(dispatch) {
+ return getVNFMarketplace(dispatch);
+ },
+
+ uploadData(currSoftwareProduct, csarId, dispatch) {
+ this.resetData(dispatch);
+ uploadVNFData(csarId, currSoftwareProduct, dispatch);
+ }
+};
+
+export default VNFImportActionHelper;
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportConstants.js
new file mode 100644
index 0000000000..e4540dda3b
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportConstants.js
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.
+ */
+import keyMirror from 'nfvo-utils/KeyMirror.js';
+
+export const actionTypes = keyMirror({
+ OPEN: null,
+ RESET_DATA: null
+});
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportReducer.js
new file mode 100644
index 0000000000..0f2638f7ed
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportReducer.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.
+ */
+import { actionTypes } from './VNFImportConstants.js';
+
+export default (state = {}, action) => {
+ switch (action.type) {
+ case actionTypes.OPEN:
+ return {
+ ...state,
+ showModal: true,
+ vnfItems: action.response
+ };
+ case actionTypes.RESET_DATA:
+ return {};
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportView.jsx
new file mode 100644
index 0000000000..3a90c8042f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/vnfMarketPlace/VNFImportView.jsx
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2017 Huawei Technologies Co., Ltd.
+ *
+ * Licensed 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.
+ */
+
+import React from 'react';
+import GridSection from 'nfvo-components/grid/GridSection.jsx';
+import GridItem from 'nfvo-components/grid/GridItem.jsx';
+import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
+import Button from 'sdc-ui/lib/react/Button.js';
+import VNFImportActionHelper from '../vnfMarketPlace/VNFImportActionHelper.js';
+
+function VNFAction({
+ action,
+ isHeader,
+ downloadCSAR,
+ id,
+ currSoftwareProduct
+}) {
+ if (isHeader) {
+ return <span>{action}</span>;
+ }
+ return (
+ <span>
+ <SVGIcon
+ name="download"
+ color="positive"
+ onClick={() => {
+ downloadCSAR(id, currSoftwareProduct);
+ }}
+ />
+ </span>
+ );
+}
+
+function VNFSortableCellHeader({
+ isHeader,
+ data,
+ isDes,
+ onSort,
+ activeSortColumn
+}) {
+ //TODO check icon sdc-ui
+ if (isHeader) {
+ if (activeSortColumn === data) {
+ return (
+ <span
+ className="vnf-table-header"
+ onClick={() => {
+ onSort(activeSortColumn);
+ }}>
+ <span>{data}</span>
+ <span
+ className={`header-sort-arrow ${isDes ? 'up' : 'down'}`}
+ />
+ </span>
+ );
+ } else {
+ return (
+ <span
+ className="vnf-table-header"
+ onClick={() => {
+ activeSortColumn = data;
+ onSort(activeSortColumn);
+ }}>
+ <span>{data}</span>
+ </span>
+ );
+ }
+ }
+ return (
+ <span className="vnf-table-cell">
+ <span>{data}</span>
+ </span>
+ );
+}
+
+export function VNFItemList({
+ vnf,
+ isHeader,
+ isDes,
+ onSort,
+ activeSortColumn,
+ downloadCSAR,
+ selectTableRow,
+ selectedRow,
+ currentSoftwareProduct
+}) {
+ let { csarId, name, version, provider, shortDesc, action } = vnf;
+ return (
+ <li
+ className={`vnfBrowse-list-item ${isHeader ? 'header' : ''} ${
+ csarId === selectedRow ? 'selectedRow' : ''
+ }`}
+ data-test-id="vnfBrowse-list-item"
+ onClick={() => {
+ selectTableRow(csarId);
+ }}>
+ <div
+ className="table-cell vnftable-name"
+ data-test-id="vnftable-name">
+ <VNFSortableCellHeader
+ isHeader={isHeader}
+ data={name}
+ isDes={isDes}
+ onSort={activeSort => {
+ onSort('name', activeSort);
+ }}
+ activeSortColumn={activeSortColumn}
+ />
+ </div>
+ <div
+ className="table-cell vnftable-version"
+ data-test-id="vnftable-version">
+ <VNFSortableCellHeader
+ isHeader={isHeader}
+ data={version}
+ isDes={isDes}
+ onSort={activeSort => {
+ onSort('version', activeSort);
+ }}
+ activeSortColumn={activeSortColumn}
+ />
+ </div>
+ <div
+ className="table-cell vnftable-provider"
+ data-test-id="vnftable-provider">
+ <VNFSortableCellHeader
+ isHeader={isHeader}
+ data={provider}
+ isDes={isDes}
+ onSort={activeSort => {
+ onSort('provider', activeSort);
+ }}
+ activeSortColumn={activeSortColumn}
+ />
+ </div>
+ <div
+ className="table-cell vnftable-shortDesc"
+ data-test-id="vnftable-shortDesc">
+ <VNFSortableCellHeader
+ isHeader={isHeader}
+ data={shortDesc}
+ isDes={isDes}
+ onSort={activeSort => {
+ onSort('shortDesc', activeSort);
+ }}
+ activeSortColumn={activeSortColumn}
+ />
+ </div>
+ <div
+ className="table-cell vnftable-action"
+ data-test-id="vnftable-action">
+ <VNFAction
+ isHeader={isHeader}
+ action={action}
+ downloadCSAR={downloadCSAR}
+ id={csarId}
+ currSoftwareProduct={currentSoftwareProduct}
+ />
+ </div>
+ </li>
+ );
+}
+
+class VNFImportView extends React.Component {
+ state = {
+ localFilter: '',
+ sortDescending: true,
+ sortCrit: 'name',
+ activeSortColumn: 'Name',
+ selectedRow: ''
+ };
+
+ render() {
+ let { onCancel, onSubmit, currentSoftwareProduct } = this.props;
+
+ return (
+ <div className="vnf-creation-page">
+ <GridSection className="vnf-grid-section">
+ <GridItem colSpan="4">
+ <ListEditorView
+ title={i18n('VNF List Title')}
+ filterValue={this.state.localFilter}
+ onFilter={filter =>
+ this.setState({ localFilter: filter })
+ }>
+ <VNFItemList
+ isHeader={true}
+ vnf={{
+ csarId: 0,
+ name: i18n('VNF Header Name'),
+ version: i18n('VNF Header Version'),
+ provider: i18n('VNF Header Vendor'),
+ shortDesc: i18n('VNF Header Desc'),
+ action: i18n('VNF Header Action')
+ }}
+ isDes={this.state.sortDescending}
+ onSort={(sortCriteria, activeSortCol) =>
+ this.setState({
+ sortDescending: !this.state
+ .sortDescending,
+ sortCrit: sortCriteria,
+ activeSortColumn: activeSortCol
+ })
+ }
+ activeSortColumn={this.state.activeSortColumn}
+ />
+ {this.sortVNFItems(
+ this.filterVNFItems(),
+ this.state.sortDescending,
+ this.state.sortCrit
+ ).map(vnf => (
+ <VNFItemList
+ key={vnf.id}
+ vnf={vnf}
+ downloadCSAR={this.downloadCSAR}
+ selectTableRow={selID => {
+ this.setState({ selectedRow: selID });
+ this.selectTableRow(selID);
+ }}
+ selectedRow={this.state.selectedRow}
+ currentSoftwareProduct={
+ currentSoftwareProduct
+ }
+ />
+ ))}
+ </ListEditorView>
+ </GridItem>
+ <GridItem colSpan="4">
+ <div className="vnf-modal">
+ <Button
+ className="vnf-submit"
+ type="button"
+ btnType="default"
+ onClick={() =>
+ onSubmit(
+ this.state.selectedRow,
+ currentSoftwareProduct
+ )
+ }>
+ {i18n('OK')}
+ </Button>
+ <Button
+ className="Cancel"
+ type="button"
+ btnType="outline"
+ onClick={onCancel}>
+ {i18n('Cancel')}
+ </Button>
+ </div>
+ </GridItem>
+ </GridSection>
+ </div>
+ );
+ }
+
+ filterVNFItems() {
+ let { vnfItems } = this.props;
+ let { localFilter } = this.state;
+ if (localFilter.trim()) {
+ const filter = new RegExp(escape(localFilter), 'i');
+ return vnfItems.filter(
+ ({ name = '', provider = '', version = '', shortDesc = '' }) =>
+ escape(name).match(filter) ||
+ escape(provider).match(filter) ||
+ escape(version).match(filter) ||
+ escape(shortDesc).match(filter)
+ );
+ } else {
+ return vnfItems;
+ }
+ }
+
+ sortVNFItems(vnfItems, sortDesc, sortCrit) {
+ if (sortDesc) {
+ return vnfItems.sort((a, b) => {
+ if (a[sortCrit] < b[sortCrit]) {
+ return -1;
+ } else if (a[sortCrit] > b[sortCrit]) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ } else {
+ return vnfItems.reverse();
+ }
+ }
+
+ downloadCSAR(id, currSoftwareProduct) {
+ VNFImportActionHelper.download(id, currSoftwareProduct);
+ }
+}
+
+export default VNFImportView;