diff options
author | Denes Nemeth <denes.nemeth@nokia.com> | 2018-02-12 20:55:54 +0100 |
---|---|---|
committer | Denes Nemeth <denes.nemeth@nokia.com> | 2018-02-23 11:44:45 +0100 |
commit | b17042b955489d8a023d09abad5436ff9b900dc3 (patch) | |
tree | 1e4392ac04a2fb1ed8d17075d504cf6594acaf16 /nokiav2/driver/src/main/java/org/onap/vfc | |
parent | d4982f7b1777e9cdae9a4cc7d0d104263889ac69 (diff) |
Updating Nokia driver
Change-Id: I950afe6acbdb359cd67a448024f006a45e8fc293
Signed-off-by: Denes Nemeth <denes.nemeth@nokia.com>
Issue-ID: VFC-728
Diffstat (limited to 'nokiav2/driver/src/main/java/org/onap/vfc')
48 files changed, 5338 insertions, 0 deletions
diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/NokiaSvnfmApplication.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/NokiaSvnfmApplication.java new file mode 100644 index 00000000..e75159f6 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/NokiaSvnfmApplication.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia; + +import org.apache.log4j.Logger; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.SelfRegistrationManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.JobManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.stereotype.Component; + +/** + * Represents the spring boot application + */ +@SpringBootApplication +public class NokiaSvnfmApplication { + private static Logger logger = Logger.getLogger(NokiaSvnfmApplication.class); + + /** + * Entry point for the Spring boot application + * + * @param args arguments for the application (not used) + */ + public static void main(String[] args) { + SpringApplication.run(NokiaSvnfmApplication.class, args); + } + + /** + * Responsible for starting the self registration process after the servlet has been started + * and is ready to answer REST request + * - has been disabled in the test because the application that provides the ONAP simulator + * has already not yet been started (can not answer REST requests) + */ + @Component + @Profile("!test") + public static class SelfRegistrationTrigger implements ApplicationListener<ApplicationReadyEvent> { + @Autowired + private SelfRegistrationManager selfRegistrationManager; + + @Override + public void onApplicationEvent(ApplicationReadyEvent contextRefreshedEvent) { + logger.info("Self registration started"); + try { + selfRegistrationManager.register(); + logger.info("Self registration finished"); + } catch (RuntimeException e) { + logger.error("Self registration failed", e); + throw e; + } + } + } + + /** + * Responsible for starting the un-registration process after the service has been ramped down + * - has been disabled in test because the same application that provides the ONAP simulator + * has already been ramped down (can not answer REST requests) + */ + @Component + @Profile("!test") + public static class SelfDeRegistrationTrigger implements ApplicationListener<ContextClosedEvent> { + @Autowired + private SelfRegistrationManager selfRegistrationManager; + @Autowired + private JobManager jobManager; + + @Override + public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { + logger.info("Self de-registration started"); + try { + jobManager.prepareForShutdown(); + selfRegistrationManager.deRegister(); + } catch (RuntimeException e) { + logger.error("Self de-registration failed", e); + throw e; + } + logger.info("Self de-registration finished"); + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IGrantManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IGrantManager.java new file mode 100644 index 00000000..d851215d --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IGrantManager.java @@ -0,0 +1,81 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; + +import com.nokia.cbam.lcm.v32.model.VnfInfo; +import org.onap.vnfmdriver.model.GrantVNFResponseVim; +import org.onap.vnfmdriver.model.VnfHealRequest; +import org.onap.vnfmdriver.model.VnfScaleRequest; + +/** + * Responsible for requesting grants during various LCM operations + */ +public interface IGrantManager { + + /** + * Request grant for healing + * - the affected virtual machine is added twice to the grant request (add & remove) to + * signal that it is temporary removed + * - the grant response is only used make a binary decision + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param vimId the identifier of the VIM + * @param request the heal request + * @param jobId the identifier of the job that triggered the grant + */ + void requestGrantForHeal(String vnfmId, String vnfId, String vimId, String onapCsarId, VnfHealRequest request, String jobId); + + /** + * Request grant for scaling + * - the affected virtual machines are calculated from the Heat mapping section of the corresponding aspect + * - the grant response is only used make a binary decision + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param vimId the identifier of the VIM + * @param onapCsarId the CSAR ID of the ONAP + * @param request the scaling request + * @param jobId the identifier of the job that triggered the grant + */ + void requestGrantForScale(String vnfmId, String vnfId, String vimId, String onapCsarId, VnfScaleRequest request, String jobId); + + /** + * Request grant for termination + * - the resources removed is the previously deployed resources based on VNF query + * - the grant response is only used make a binary decision + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param vimId the identifier of the VIM + */ + void requestGrantForTerminate(String vnfmId, String vnfId, String vimId, String onapVnfdId, VnfInfo vnf, String jobId); + + /** + * Request grant for instantiation + * - the added resources are calculated from the VNFD by counting the VDUs in the selected the instantiation level + * - the only parameter used from the grant response in the VIM to which the VNF is to be deployed to + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param vimId the identifier of the VIM + * @param onapVnfdId the identifier of the VNF package in ONAP + * @param instantiationLevelId the instantiation level + * @param cbamVnfdContent the content of the CBAM VNFD + * @return the grant response + */ + GrantVNFResponseVim requestGrantForInstantiate(String vnfmId, String vnfId, String vimId, String onapVnfdId, String instantiationLevelId, String cbamVnfdContent, String jobId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/INotificationSender.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/INotificationSender.java new file mode 100644 index 00000000..92099cc3 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/INotificationSender.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; + +import com.nokia.cbam.lcm.v32.model.OperationExecution; +import com.nokia.cbam.lcm.v32.model.VnfLifecycleChangeNotification; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.ReportedAffectedConnectionPoints; + +/** + * Responsible for processing the preprocessed notification from CBAM and making the changes + * based on the notification in various ONAP sub systems. + */ +public interface INotificationSender { + /** + * Execute changes in the ONAP subsystem based on the received notification + * + * @param receivedNotification the notification from CBAM + * @param operationExecution the executed operation that triggered the LCN + * @param affectedConnectionPoints the affected connection points during the operation + * @param vimId the identifier of the VIM in ONAP + */ + void processNotification(VnfLifecycleChangeNotification receivedNotification, OperationExecution operationExecution, ReportedAffectedConnectionPoints affectedConnectionPoints, String vimId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IPackageProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IPackageProvider.java new file mode 100644 index 00000000..a652354d --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/IPackageProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; + +/** + * Provides a VNF package from ONAP repositories + */ +public interface IPackageProvider { + /** + * The location of the CBAM package within the ONAP package + */ + String CBAM_PACKAGE_NAME_IN_ZIP = "Artifacts/Deployment/OTHER/cbam.package.zip"; + + /** + * Download the package from ONAP + * + * @param csarId the CSAR identifier of the package in ONAP + * @return the binary content of the package + */ + byte[] getPackage(String csarId); + + /** + * @param csarId the CSAR identifier of the package in ONAP + * @return the identifier of the package in CBAM + */ + String getCbamVnfdId(String csarId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VimInfoProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VimInfoProvider.java new file mode 100644 index 00000000..111f44b3 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VimInfoProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; + +import org.onap.vnfmdriver.model.VimInfo; + +/** + * Responsible for providing information from the VIM to be used for the VNF + */ +public interface VimInfoProvider { + + /** + * @param vimId the identifier of the VIM + * @return the description of the VIM + */ + VimInfo getVimInfo(String vimId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VnfmInfoProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VnfmInfoProvider.java new file mode 100644 index 00000000..cfcb1d41 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/VnfmInfoProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; + +import org.onap.vnfmdriver.model.VnfmInfo; + +/** + * Responsible for providing information from the VNFM itself + */ +public interface VnfmInfoProvider { + + /** + * The name of the VNFM info cache eviction in the properties file + */ + String VNFM_INFO_CACHE_EVICTION_IN_MS = "vnfmInfoCacheEvictionInMs"; + + /** + * The default VNFM info cache eviction in milliseconds + */ + int DEFAULT_CACHE_EVICTION_TIMEOUT_IN_MS = 10 * 60 * 1000; + + /** + * @param vnfmId the identifier of the VNFM + * @return the description of the VNFM + */ + VnfmInfo getVnfmInfo(String vnfmId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/package-info.java new file mode 100644 index 00000000..4463b26a --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/api/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * The collection of interfaces to connect to core systems. The + * driver is able to interface with VF-C xor with SDC, A&AI directly. + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/GenericExternalSystemInfoProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/GenericExternalSystemInfoProvider.java new file mode 100644 index 00000000..3e1e05a5 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/GenericExternalSystemInfoProvider.java @@ -0,0 +1,83 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core; + +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VimInfoProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VnfmInfoProvider; +import org.onap.vnfmdriver.model.VnfmInfo; +import org.slf4j.Logger; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.env.Environment; + +import java.util.concurrent.TimeUnit; + +import static com.google.common.cache.CacheBuilder.newBuilder; +import static java.lang.Long.valueOf; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for providing access to core systems + */ +abstract public class GenericExternalSystemInfoProvider extends IpMappingProvider implements VnfmInfoProvider, VimInfoProvider, InitializingBean { + private static Logger logger = getLogger(GenericExternalSystemInfoProvider.class); + private final Environment environment; + private LoadingCache<String, VnfmInfo> vnfmInfoCache; + + public GenericExternalSystemInfoProvider(Environment environment) { + super(environment); + this.environment = environment; + } + + /** + * After the Bean has been initialized the IP mapping and the VMFM cache is initialized + * It is done in this phase because the logic requires the the @Value anoted fields to + * be specified + */ + @Override + public void afterPropertiesSet() throws Exception { + super.afterPropertiesSet(); + vnfmInfoCache = newBuilder().expireAfterWrite(environment.getProperty(VNFM_INFO_CACHE_EVICTION_IN_MS, Long.class, valueOf(DEFAULT_CACHE_EVICTION_TIMEOUT_IN_MS)), TimeUnit.MILLISECONDS).concurrencyLevel(1).build(new CacheLoader<String, VnfmInfo>() { + @Override + public VnfmInfo load(String vnfmId) throws Exception { + logger.info("Quering VNFM info from source with " + vnfmId + " identifier"); + return queryVnfmInfoFromSource(vnfmId); + } + }); + } + + /* + * @param vnfmId the identifier of the VNFM + * @return the cached VNFM + */ + public VnfmInfo getVnfmInfo(String vnfmId) { + try { + return vnfmInfoCache.get(vnfmId); + } catch (Exception e) { + throw fatalFailure(logger, "Unable to query VNFM info for " + vnfmId, e); + } + } + + /** + * Load the information related to the VNFM from the remote source + * + * @param vnfmId the identifier of the VNFM + * @return the description of the VNFM + */ + public abstract VnfmInfo queryVnfmInfoFromSource(String vnfmId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/IpMappingProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/IpMappingProvider.java new file mode 100644 index 00000000..a2472283 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/IpMappingProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static com.google.common.base.Splitter.on; +import static com.google.common.collect.Lists.newArrayList; + +/** + * Responsible for remapping IP/DNS names in URLs based on property file + */ +@Component +public class IpMappingProvider implements InitializingBean { + public static final String IP_MAP = "ipMap"; + private final Environment environment; + private final Map<String, String> ipMap = new HashMap<>(); + + @Autowired + IpMappingProvider(Environment environment) { + this.environment = environment; + } + + /** + * After the Bean has been initialized the IP mapping and the VMFM cache is initialized + * It is done in this phase because it requires the environment to be initialized + */ + @Override + public void afterPropertiesSet() throws Exception { + on(",").trimResults().omitEmptyStrings().split(environment.getProperty(IP_MAP, String.class, "")).forEach(new Consumer<String>() { + @Override + public void accept(String item) { + ArrayList<String> ip = newArrayList(on("->").trimResults().split(item)); + ipMap.put(ip.get(0), ip.get(1)); + } + }); + } + + /** + * Map IP addresses based on configuration parameter ipMap + * + * @param ip the original IP address + * @return the mapped IP address + */ + public String mapPrivateIpToPublicIp(String ip) { + return ipMap.getOrDefault(ip, ip); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/MsbApiProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/MsbApiProvider.java new file mode 100644 index 00000000..b6f4644a --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/MsbApiProvider.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core; + +import org.onap.msb.sdk.discovery.common.RouteException; +import org.onap.msb.sdk.discovery.entity.MicroServiceFullInfo; +import org.onap.msb.sdk.discovery.entity.NodeInfo; +import org.onap.msb.sdk.httpclient.msb.MSBServiceClient; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import static java.lang.Integer.valueOf; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for providing REST client to access MSB API + */ +@Component +public class MsbApiProvider extends IpMappingProvider { + private static Logger logger = getLogger(MsbApiProvider.class); + @Value("${messageBusIp}") + private String messageBusIp; + @Value("${messageBusPort}") + private String messageBusPort; + + @Autowired + MsbApiProvider(Environment environment) { + super(environment); + } + + /** + * @return API to access ONAP MSB + */ + public MSBServiceClient getMsbClient() { + return new MSBServiceClient(messageBusIp, valueOf(messageBusPort)); + } + + /** + * @param name the name of the micro service + * @param version the version of the micro service + * @return the base URL of the micro service (ex. https://1.2.3.4/path ) + */ + public String getMicroServiceUrl(String name, String version) { + MicroServiceFullInfo microServiceFullInfo = getMicroServiceInfo(name, version); + String protocol = "http://"; //FIXME the enable_ssl field should be used, but it is not available in SDK + String ipAnPort = getNodeIpAnPort(microServiceFullInfo); + //the field name in A&AI is misleading the URL is relative path postfixed to http(s)://ip:port + String fullUrl = protocol + ipAnPort + microServiceFullInfo.getUrl(); + return fullUrl; + } + + private MicroServiceFullInfo getMicroServiceInfo(String name, String version) throws RuntimeException { + try { + return getMsbClient().queryMicroServiceInfo(name, version); + } catch (RouteException e) { + throw fatalFailure(logger, "Unable to get micro service URL for " + name + " with version " + version, e); + } + } + + private String getNodeIpAnPort(MicroServiceFullInfo microServiceFullInfo) { + for (NodeInfo nodeInfo : microServiceFullInfo.getNodes()) { + if (!nodeInfo.getIp().startsWith("172.")) { // FIXME how to know which of the multiple addresses to use? + return mapPrivateIpToPublicIp(nodeInfo.getIp()) + ":" + nodeInfo.getPort(); + } + } + throw fatalFailure(logger, "The " + microServiceFullInfo.getServiceName() + " service with " + microServiceFullInfo.getVersion() + " does not have any valid nodes" + microServiceFullInfo.getNodes()); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/SelfRegistrationManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/SelfRegistrationManager.java new file mode 100644 index 00000000..3b2f1d34 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/SelfRegistrationManager.java @@ -0,0 +1,194 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core; + +import com.nokia.cbam.lcn.v32.ApiException; +import com.nokia.cbam.lcn.v32.api.SubscriptionsApi; +import com.nokia.cbam.lcn.v32.model.*; +import org.onap.msb.sdk.discovery.common.RouteException; +import org.onap.msb.sdk.discovery.entity.MicroServiceFullInfo; +import org.onap.msb.sdk.discovery.entity.MicroServiceInfo; +import org.onap.msb.sdk.discovery.entity.Node; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashSet; + +import static com.nokia.cbam.lcn.v32.model.SubscriptionAuthentication.TypeEnum.NONE; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCN_API_VERSION; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for registering the driver in the core systems. + */ +@Component +public class SelfRegistrationManager { + public static final String DRIVER_VERSION = "v1"; + public static final String SERVICE_NAME = "NokiaSVNFM"; + // 1 means internal 0 means core :) + public static final String INTERNAL_SERVICE = "1"; + public static final String SWAGGER_API_DEFINITION = "self.swagger.json"; + private static Logger logger = getLogger(SelfRegistrationManager.class); + private final DriverProperties driverProperties; + private final MsbApiProvider msbApiProvider; + private final CbamRestApiProvider cbamRestApiProvider; + + @Value("${driverMsbExternalIp}") + private String driverMsbExternalIp; + @Value("${driverVnfmExternalIp}") + private String driverVnfmExternalIp; + @Value("${server.port}") + private String driverPort; + private volatile boolean ready = false; + + @Autowired + SelfRegistrationManager(DriverProperties driverProperties, MsbApiProvider msbApiProvider, CbamRestApiProvider cbamRestApiProvider) { + this.cbamRestApiProvider = cbamRestApiProvider; + this.msbApiProvider = msbApiProvider; + this.driverProperties = driverProperties; + } + + /** + * Register the driver in micro-service bus and subscribe to LCNs from CBAM + */ + public void register() { + //the order is important (only publish it's existence after the subscription has been created) + subscribeToLcn(driverProperties.getVnfmId()); + try { + registerMicroService(); + } catch (RuntimeException e) { + deleteSubscription(driverProperties.getVnfmId()); + throw e; + } + ready = true; + } + + /** + * De-register the VNFM driver from the micro-service bus + */ + public void deRegister() { + try { + logger.info("Cancelling micro service registration"); + msbApiProvider.getMsbClient().cancelMicroServiceInfo(SERVICE_NAME, DRIVER_VERSION); + } catch (RouteException e) { + //ONAP throws 500 internal server error, but deletes the micro service + try { + MicroServiceFullInfo microServiceFullInfo = msbApiProvider.getMsbClient().queryMicroServiceInfo(SERVICE_NAME, DRIVER_VERSION); + logger.error("Unable to deRegister Nokia VNFM driver", e); + //the micro service still exists + throw new RuntimeException(e); + } catch (RouteException e1) { + // the micro service was deleted (even though 500 HTTP code was reported) + } + } + deleteSubscription(driverProperties.getVnfmId()); + } + + /** + * @return the swagger API definition + */ + public byte[] getSwaggerApiDefinition() { + return SystemFunctions.systemFunctions().loadFile(SWAGGER_API_DEFINITION); + } + + private String getDriverVnfmUrl() { + return "http://" + driverVnfmExternalIp + ":" + driverPort + DriverProperties.BASE_URL; + } + + private void deleteSubscription(String vnfmId) { + logger.info("Deleting CBAM LCN subscription"); + SubscriptionsApi lcnApi = cbamRestApiProvider.getCbamLcnApi(vnfmId); + try { + String callbackUrl = getDriverVnfmUrl() + DriverProperties.LCN_PATH; + for (Subscription subscription : lcnApi.subscriptionsGet(NOKIA_LCN_API_VERSION)) { + if (subscription.getCallbackUrl().equals(callbackUrl)) { + lcnApi.subscriptionsSubscriptionIdDelete(subscription.getId(), NOKIA_LCN_API_VERSION); + } + } + } catch (ApiException e) { + logger.error("Unable to delete CBAM LCN subscription"); + throw new RuntimeException(e); + } + } + + private MicroServiceFullInfo registerMicroService() { + logger.info("Registering micro service"); + MicroServiceInfo microServiceInfo = new MicroServiceInfo(); + microServiceInfo.setUrl(DriverProperties.BASE_URL); + //the PATH should not be set + microServiceInfo.setProtocol("REST"); + microServiceInfo.setVisualRange(INTERNAL_SERVICE); + microServiceInfo.setServiceName(SERVICE_NAME); + microServiceInfo.setVersion(DRIVER_VERSION); + //FIXME set enable_ssl to false after the field has been added to MSB SDK + //currently defaults to false, which is good + Node node = new Node(); + microServiceInfo.setNodes(new HashSet<>()); + microServiceInfo.getNodes().add(node); + node.setIp(driverMsbExternalIp); + node.setPort(driverPort); + node.setTtl("0"); + try { + return msbApiProvider.getMsbClient().registerMicroServiceInfo(microServiceInfo); + } catch (RouteException e) { + logger.error("Unable to register Nokia VNFM driver", e); + throw new RuntimeException(e); + } + } + + private void subscribeToLcn(String vnfmId) { + String callbackUrl = getDriverVnfmUrl() + DriverProperties.LCN_PATH; + logger.info("Subscribing to CBAM LCN " + driverProperties.getCbamLcnUrl() + " with callback to " + callbackUrl); + SubscriptionsApi lcnApi = cbamRestApiProvider.getCbamLcnApi(vnfmId); + try { + for (Subscription subscription : lcnApi.subscriptionsGet(NOKIA_LCN_API_VERSION)) { + if (subscription.getCallbackUrl().equals(callbackUrl)) { + return; + } + } + CreateSubscriptionRequest request = new CreateSubscriptionRequest(); + request.setFilter(new SubscriptionFilter()); + request.getFilter().setNotificationTypes(new ArrayList<>()); + request.getFilter().getNotificationTypes().add(VnfNotificationType.VNFLIFECYCLECHANGENOTIFICATION); + request.setCallbackUrl(callbackUrl); + request.getFilter().addOperationTypesItem(OperationType.HEAL); + request.getFilter().addOperationTypesItem(OperationType.INSTANTIATE); + request.getFilter().addOperationTypesItem(OperationType.SCALE); + request.getFilter().addOperationTypesItem(OperationType.TERMINATE); + SubscriptionAuthentication subscriptionAuthentication = new SubscriptionAuthentication(); + subscriptionAuthentication.setType(NONE);//FIXME improve authentication + request.setAuthentication(subscriptionAuthentication); + lcnApi.subscriptionsPost(request, NOKIA_LCN_API_VERSION); + } catch (ApiException e) { + logger.error("Unable to subscribe to CBAM LCN", e); + throw new RuntimeException(e); + } + } + + /** + * @return is the component ready to serve requests + */ + public boolean isReady() { + return ready; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/package-info.java new file mode 100644 index 00000000..67bdef8a --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/core/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * Handles communication with ONAP core functions + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/package-info.java new file mode 100644 index 00000000..eea0066b --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * Handles information exchange with ONAP components + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcExternalSystemInfoProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcExternalSystemInfoProvider.java new file mode 100644 index 00000000..1959e480 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcExternalSystemInfoProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.GenericExternalSystemInfoProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring.Conditions; +import org.onap.vnfmdriver.ApiException; +import org.onap.vnfmdriver.model.VimInfo; +import org.onap.vnfmdriver.model.VnfmInfo; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for providing information related to the VNFM from VF-C source + */ +@Component +@Conditional(value = Conditions.UseForVfc.class) +public class VfcExternalSystemInfoProvider extends GenericExternalSystemInfoProvider { + private static Logger logger = getLogger(VfcExternalSystemInfoProvider.class); + private final VfcRestApiProvider vfcRestApiProvider; + + @Autowired + VfcExternalSystemInfoProvider(Environment environment, VfcRestApiProvider vfcRestApiProvider) { + super(environment); + this.vfcRestApiProvider = vfcRestApiProvider; + } + + @Override + public VnfmInfo queryVnfmInfoFromSource(String vnfmId) { + try { + return vfcRestApiProvider.getNsLcmApi().queryVnfmInfo(vnfmId); + } catch (ApiException e) { + throw fatalFailure(logger, "Unable to query VNFM from VF-C with " + vnfmId + " identifier", e); + } + } + + @Override + public VimInfo getVimInfo(String vimId) { + try { + return vfcRestApiProvider.getNsLcmApi().queryVIMInfo(vimId); + } catch (org.onap.vnfmdriver.ApiException e) { + throw fatalFailure(logger, "Unable to query VIM from VF-C with " + vimId + " identifier", e); + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcGrantManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcGrantManager.java new file mode 100644 index 00000000..fd68aebe --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcGrantManager.java @@ -0,0 +1,220 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.nokia.cbam.lcm.v32.ApiException; +import com.nokia.cbam.lcm.v32.model.VnfInfo; +import com.nokia.cbam.lcm.v32.model.VnfcResourceInfo; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IGrantManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring.Conditions; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CatalogManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider; +import org.onap.vnfmdriver.model.*; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; +import org.yaml.snakeyaml.Yaml; + +import java.util.*; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for handling granting before the execution of a VNF operation + */ +@Component +@Conditional(value = Conditions.UseForVfc.class) +public class VfcGrantManager implements IGrantManager { + private static Logger logger = getLogger(VfcGrantManager.class); + private final CatalogManager catalogManager; + private final CbamRestApiProvider cbamRestApiProvider; + private final VfcRestApiProvider vfcRestApiProvider; + + @Autowired + VfcGrantManager(CatalogManager catalogManager, CbamRestApiProvider cbamRestApiProvider, VfcRestApiProvider vfcRestApiProvider) { + this.catalogManager = catalogManager; + this.cbamRestApiProvider = cbamRestApiProvider; + this.vfcRestApiProvider = vfcRestApiProvider; + } + + @Override + public void requestGrantForHeal(String vnfmId, String vnfId, String vimId, String onapCsarId, VnfHealRequest request, String jobId) { + GrantVNFRequest grantRequest = buildGrantRequest(vnfmId, vimId, onapCsarId, jobId, OperationType.HEAL); + ResourceChange resourceChange = new ResourceChange(); + resourceChange.setType(ChangeType.VDU); + resourceChange.setVdu(request.getAffectedvm().getVduid()); + resourceChange.setResourceDefinitionId(UUID.randomUUID().toString()); + grantRequest.getRemoveResource().add(resourceChange); + grantRequest.getAddResource().add(resourceChange); + grantRequest.setVnfInstanceId(vnfId); + requestGrant(grantRequest); + } + + @Override + public void requestGrantForScale(String vnfmId, String vnfId, String vimId, String onapCsarId, VnfScaleRequest request, String jobId) { + try { + OperationType operationType = ScaleDirection.IN.equals(request.getType()) ? OperationType.SCALEIN : OperationType.SCALEOUT; + GrantVNFRequest grantRequest = buildGrantRequest(vnfmId, vimId, onapCsarId, jobId, operationType); + com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, vnf.getVnfdId()); + Set<ResourceChange> resourceChanges = calculateResourceChangeDuringScaling(vnfdContent, request.getAspectId(), Integer.parseInt(request.getNumberOfSteps())); + switch (request.getType()) { + case IN: + grantRequest.getRemoveResource().addAll(resourceChanges); + break; + case OUT: + grantRequest.getAddResource().addAll(resourceChanges); + break; + } + grantRequest.setVnfInstanceId(vnfId); + requestGrant(grantRequest); + } catch (ApiException e) { + logger.error("Unable to query VNF " + vnfId, e); + throw new RuntimeException("Unable to query VNF " + vnfId, e); + } + } + + @Override + public void requestGrantForTerminate(String vnfmId, String vnfId, String vimId, String onapVnfdId, VnfInfo vnf, String jobId) { + switch (vnf.getInstantiationState()) { + case NOT_INSTANTIATED: + break; + case INSTANTIATED: + GrantVNFRequest grantRequest; + try { + grantRequest = buildGrantRequest(vnfmId, vimId, onapVnfdId, jobId, OperationType.TERMINAL); + if (vnf.getInstantiatedVnfInfo().getVnfcResourceInfo() != null) { + for (VnfcResourceInfo vnfc : vnf.getInstantiatedVnfInfo().getVnfcResourceInfo()) { + ResourceChange resourceChange = new ResourceChange(); + grantRequest.getRemoveResource().add(resourceChange); + resourceChange.setVdu(vnfc.getVduId()); + resourceChange.setType(ChangeType.VDU); + resourceChange.setResourceDefinitionId(UUID.randomUUID().toString()); + } + } + grantRequest.setVnfInstanceId(vnfId); + } catch (Exception e) { + logger.error("Unable to prepare grant request for termination", e); + throw new RuntimeException("Unable to prepare grant request for termination", e); + } + requestGrant(grantRequest); + break; + } + } + + @Override + public GrantVNFResponseVim requestGrantForInstantiate(String vnfmId, String vnfId, String vimId, String onapVnfdId, String instantiationLevelId, String cbamVnfdContent, String jobId) { + GrantVNFRequest grantRequest; + try { + grantRequest = buildGrantRequest(vnfmId, vimId, onapVnfdId, jobId, OperationType.INSTANTIATE); + grantRequest.setVnfInstanceId(vnfId); + grantRequest.setAddResource(new ArrayList<>()); + grantRequest.getAddResource().addAll(calculateResourceChangeDuringInstantiate(cbamVnfdContent, instantiationLevelId)); + } catch (Exception e) { + logger.error("Unable to prepare grant request for instantiation", e); + throw new RuntimeException("Unable to prepare grant request for instantiation", e); + } + return requestGrant(grantRequest); + } + + private GrantVNFRequest buildGrantRequest(String vnfmId, String vimId, String onapCsarId, String jobId, OperationType operationType) { + //FIXME the vimId should not be required for grant request see VFC-603 issue + GrantVNFRequest grantVNFRequest = new GrantVNFRequest(); + grantVNFRequest.setAdditionalParam(new AdditionalGrantParams(vnfmId, vimId)); + grantVNFRequest.setVnfDescriptorId(onapCsarId); + grantVNFRequest.setJobId(jobId); + grantVNFRequest.setLifecycleOperation(operationType); + grantVNFRequest.setAddResource(new ArrayList<>()); + grantVNFRequest.setRemoveResource(new ArrayList<>()); + return grantVNFRequest; + } + + private GrantVNFResponseVim requestGrant(GrantVNFRequest grantRequest) { + try { + return vfcRestApiProvider.getNsLcmApi().grantvnf(grantRequest).getVim(); + } catch (org.onap.vnfmdriver.ApiException e) { + logger.error("Unable to request grant", e); + throw new RuntimeException(e); + } + } + + private Set<ResourceChange> calculateResourceChangeDuringInstantiate(String vnfdContent, String instantiationLevelId) { + JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject(); + JsonObject capabilities = CbamUtils.child(CbamUtils.child(CbamUtils.child(root, "topology_template"), "substitution_mappings"), "capabilities"); + JsonObject deploymentFlavorProperties = CbamUtils.child(CbamUtils.child(capabilities, "deployment_flavour"), "properties"); + JsonObject instantiationLevels = CbamUtils.child(deploymentFlavorProperties, "instantiation_levels"); + Set<ResourceChange> resourceChanges = new HashSet<>(); + for (Map.Entry<String, JsonElement> vdu_level : CbamUtils.child(CbamUtils.child(instantiationLevels, instantiationLevelId), ("vdu_levels")).entrySet()) { + JsonElement number_of_instances = vdu_level.getValue().getAsJsonObject().get("number_of_instances"); + for (int i = 0; i < number_of_instances.getAsLong(); i++) { + ResourceChange resourceChange = new ResourceChange(); + resourceChanges.add(resourceChange); + resourceChange.setVdu(vdu_level.getKey()); + resourceChange.setType(ChangeType.VDU); + resourceChange.setResourceDefinitionId(UUID.randomUUID().toString()); + } + } + return resourceChanges; + } + + private Set<ResourceChange> calculateResourceChangeDuringScaling(String vnfdContent, String aspectId, int steps) { + JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject(); + Set<ResourceChange> resourceChanges = new HashSet<>(); + JsonArray policies = CbamUtils.child(root, "topology_template").getAsJsonObject().get("policies").getAsJsonArray(); + for (JsonElement policy : policies) { + if (policy.getAsJsonObject().entrySet().iterator().next().getKey().equals("heat_mapping")) { + JsonObject aspects = policy.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject().get("properties").getAsJsonObject().get("aspects").getAsJsonObject(); + JsonObject aspect = aspects.get(aspectId).getAsJsonObject(); + if (aspect.has("vdus")) { + for (Map.Entry<String, JsonElement> vdu : aspect.get("vdus").getAsJsonObject().entrySet()) { + String vduId = vdu.getKey(); + for (int step = 0; step < steps; step++) { + for (int i = 0; i < vdu.getValue().getAsJsonArray().size(); i++) { + ResourceChange resourceChange = new ResourceChange(); + resourceChange.setVdu(vduId); + resourceChange.setType(ChangeType.VDU); + resourceChange.setResourceDefinitionId(UUID.randomUUID().toString()); + resourceChanges.add(resourceChange); + } + } + } + } + } + } + return resourceChanges; + } + + /** + * Represents the mandatory parameters that must be sent during grant request to VF-C + */ + private static class AdditionalGrantParams { + private final String vnfmId; + private final String vimId; + + AdditionalGrantParams(String vnfmId, String vimId) { + this.vnfmId = vnfmId; + this.vimId = vimId; + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcNotificationSender.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcNotificationSender.java new file mode 100644 index 00000000..26439b5c --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcNotificationSender.java @@ -0,0 +1,202 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc; + +import com.google.gson.Gson; +import com.nokia.cbam.lcm.v32.model.OperationExecution; +import com.nokia.cbam.lcm.v32.model.ScaleVnfRequest; +import com.nokia.cbam.lcm.v32.model.VnfLifecycleChangeNotification; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.INotificationSender; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring.Conditions; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.ReportedAffectedConnectionPoints; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.ReportedAffectedCp; +import org.onap.vnfmdriver.model.*; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager.SEPARATOR; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.JobManager.extractOnapJobId; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for sending notifications to VF-C + */ +@Component +@Conditional(value = Conditions.UseForVfc.class) +public class VfcNotificationSender implements INotificationSender { + private static Logger logger = getLogger(VfcNotificationSender.class); + private final DriverProperties driverProperties; + private final VfcRestApiProvider vfcRestApiProvider; + + @Autowired + VfcNotificationSender(DriverProperties driverProperties, VfcRestApiProvider vfcRestApiProvider) { + this.driverProperties = driverProperties; + this.vfcRestApiProvider = vfcRestApiProvider; + } + + @Override + public void processNotification(VnfLifecycleChangeNotification recievedNotification, OperationExecution operationExecution, ReportedAffectedConnectionPoints affectedCps, String vimId) { + VNFLCMNotification notificationToSend = new VNFLCMNotification(); + notificationToSend.setJobId(extractOnapJobId(operationExecution.getOperationParams())); + notificationToSend.setOperation(getOperation(driverProperties.getVnfmId(), recievedNotification.getVnfInstanceId(), operationExecution, recievedNotification.getOperation(), recievedNotification.getAffectedVnfcs())); + notificationToSend.setVnfInstanceId(recievedNotification.getVnfInstanceId()); + switch (recievedNotification.getStatus()) { + case FINISHED: + case FAILED: + notificationToSend.setStatus(VnfLcmNotificationStatus.RESULT); + addAffectedVirtualLinks(recievedNotification, notificationToSend); + addAffectedVnfcs(vimId, recievedNotification.getVnfInstanceId(), notificationToSend, recievedNotification); + addAffectedCps(vimId, notificationToSend, affectedCps); + break; + default: + notificationToSend.setStatus(VnfLcmNotificationStatus.START); + break; + } + sendNotification(notificationToSend); + } + + private void sendNotification(VNFLCMNotification notification) { + try { + logger.info("Sending LCN: " + new Gson().toJson(notification)); + vfcRestApiProvider.getNsLcmApi().vNFLCMNotification(driverProperties.getVnfmId(), notification.getVnfInstanceId(), notification); + } catch (Exception e) { + fatalFailure(logger, "Unable to send LCN to VF-C", e); + } + } + + private AffectedCp buildAffectedCp(String vimId, String vnfId, ReportedAffectedCp affectedCp) { + AffectedCp onapAffectedCp = new AffectedCp(); + AffectedCpPortResource port = new AffectedCpPortResource(); + port.setInstId(affectedCp.getServerProviderId()); + port.setIpAddress(affectedCp.getIpAddress()); + port.setMacAddress(affectedCp.getMacAddress()); + port.setResourceid(affectedCp.getProviderId()); + port.setResourceName(affectedCp.getName()); + port.setTenant(affectedCp.getTenantId()); + port.setVimid(vimId); + onapAffectedCp.setPortResource(port); + onapAffectedCp.setCpdid(affectedCp.getCpId()); + onapAffectedCp.setCpinstanceid(vnfId + SEPARATOR + affectedCp.getCpId()); + onapAffectedCp.setVirtualLinkInstanceId(affectedCp.getNetworkProviderId()); + onapAffectedCp.setChangeType(transform(affectedCp.getChangeType())); + //owner id & type can be left empty it will default to VNF id on VF-C + return onapAffectedCp; + } + + private VnfCpNotificationType transform(com.nokia.cbam.lcm.v32.model.ChangeType changeType) { + switch (changeType) { + case ADDED: + return VnfCpNotificationType.ADDED; + case REMOVED: + return VnfCpNotificationType.REMOVED; + default: //can only be MODIFIED + return VnfCpNotificationType.CHANGED; + } + } + + private void addAffectedVnfcs(String vimId, String vnfId, VNFLCMNotification notificationToSend, VnfLifecycleChangeNotification request) { + if (request.getAffectedVnfcs() != null) { + notificationToSend.setAffectedVnfc(new ArrayList<>()); + for (com.nokia.cbam.lcm.v32.model.AffectedVnfc affectedVnfc : request.getAffectedVnfcs()) { + org.onap.vnfmdriver.model.AffectedVnfc onapVnfc = new org.onap.vnfmdriver.model.AffectedVnfc(); + onapVnfc.setChangeType(getChangeType(affectedVnfc.getChangeType())); + onapVnfc.setVduId(affectedVnfc.getVduId()); + onapVnfc.setVmid(affectedVnfc.getComputeResource().getResourceId()); + onapVnfc.setVmname(extractServerName(affectedVnfc.getComputeResource().getAdditionalData())); + onapVnfc.setVnfcInstanceId(vnfId + SEPARATOR + affectedVnfc.getId()); + onapVnfc.setVimid(vimId); + notificationToSend.getAffectedVnfc().add(onapVnfc); + } + } + } + + private void addAffectedVirtualLinks(VnfLifecycleChangeNotification request, VNFLCMNotification notification) { + if (request.getAffectedVirtualLinks() != null) { + notification.setAffectedVl(new ArrayList<>()); + for (com.nokia.cbam.lcm.v32.model.AffectedVirtualLink affectedVirtualLink : request.getAffectedVirtualLinks()) { + org.onap.vnfmdriver.model.AffectedVirtualLink onapVirtualLink = new org.onap.vnfmdriver.model.AffectedVirtualLink(); + onapVirtualLink.setVlInstanceId(request.getVnfInstanceId() + SEPARATOR + affectedVirtualLink.getId()); + onapVirtualLink.setChangeType(getChangeType(affectedVirtualLink.getChangeType())); + onapVirtualLink.setVldid(affectedVirtualLink.getVirtualLinkDescId()); + AffectedVirtualLinkNetworkResource networkResource = new AffectedVirtualLinkNetworkResource(); + onapVirtualLink.setNetworkResource(networkResource); + networkResource.setResourceId(affectedVirtualLink.getResource().getResourceId()); + networkResource.setResourceType(AffectedVirtualLinkType.NETWORK); + notification.getAffectedVl().add(onapVirtualLink); + } + } + } + + private void addAffectedCps(String vimId, VNFLCMNotification notificationToSend, ReportedAffectedConnectionPoints affectedCps) { + if (affectedCps != null) { + notificationToSend.setAffectedCp(new ArrayList<>()); + for (ReportedAffectedCp affectedCp : affectedCps.getPost()) { + if (affectedCp.getCpdId() != null) { + AffectedCp onapAffectedCp = buildAffectedCp(vimId, notificationToSend.getVnfInstanceId(), affectedCp); + onapAffectedCp.setCpdid(affectedCp.getCpdId()); + notificationToSend.getAffectedCp().add(onapAffectedCp); + } + if (affectedCp.getEcpdId() != null) { + AffectedCp onapAffectedCp = buildAffectedCp(vimId, notificationToSend.getVnfInstanceId(), affectedCp); + onapAffectedCp.setCpdid(affectedCp.getEcpdId()); + notificationToSend.getAffectedCp().add(onapAffectedCp); + } + } + } + } + + private org.onap.vnfmdriver.model.OperationType getOperation(String vnfmId, String vnfId, OperationExecution operationExecution, com.nokia.cbam.lcm.v32.model.OperationType type, List<com.nokia.cbam.lcm.v32.model.AffectedVnfc> affectedVnfcs) { + switch (type) { + case TERMINATE: + return org.onap.vnfmdriver.model.OperationType.TERMINAL; + case INSTANTIATE: + return org.onap.vnfmdriver.model.OperationType.INSTANTIATE; + case SCALE: + ScaleVnfRequest originalRequest = new Gson().fromJson(new Gson().toJson(operationExecution.getOperationParams()), ScaleVnfRequest.class); + switch (originalRequest.getType()) { + case IN: + return org.onap.vnfmdriver.model.OperationType.SCALEIN; + default: //OUT + return org.onap.vnfmdriver.model.OperationType.SCALEOUT; + } + default: + return org.onap.vnfmdriver.model.OperationType.HEAL; + } + } + + private String extractServerName(Object additionalData) { + return new Gson().toJsonTree(additionalData).getAsJsonObject().get("name").getAsString(); + } + + private org.onap.vnfmdriver.model.VnfNotificationType getChangeType(com.nokia.cbam.lcm.v32.model.ChangeType changeType) { + switch (changeType) { + case ADDED: + return org.onap.vnfmdriver.model.VnfNotificationType.ADDED; + case REMOVED: + return org.onap.vnfmdriver.model.VnfNotificationType.REMOVED; + default: //case MODIFIED: + return org.onap.vnfmdriver.model.VnfNotificationType.MODIFIED; + } + } + +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcPackageProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcPackageProvider.java new file mode 100644 index 00000000..b8de2378 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcPackageProvider.java @@ -0,0 +1,107 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc; + +import com.google.common.io.ByteStreams; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IPackageProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.IpMappingProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring.Conditions; +import org.onap.vfccatalog.api.VnfpackageApi; +import org.onap.vfccatalog.model.VnfPkgDetailInfo; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE; + +/** + * Retrieves a package from VF-C + */ +@Component +@Conditional(value = Conditions.UseForVfc.class) +public class VfcPackageProvider implements IPackageProvider { + private static Logger logger = getLogger(VfcPackageProvider.class); + private final VfcRestApiProvider restApiProvider; + private final IpMappingProvider ipMappingProvider; + + @Autowired + VfcPackageProvider(VfcRestApiProvider restApiProvider, IpMappingProvider ipMappingProvider) { + this.restApiProvider = restApiProvider; + this.ipMappingProvider = ipMappingProvider; + } + + @Override + public String getCbamVnfdId(String csarId) { + try { + VnfpackageApi onapCatalogApi = restApiProvider.getOnapCatalogApi(); + VnfPkgDetailInfo vnfPackageDetails = onapCatalogApi.queryVnfPackage(csarId); + JsonElement vnfdModel = new JsonParser().parse(vnfPackageDetails.getPackageInfo().getVnfdModel()); + return vnfdModel.getAsJsonObject().get("metadata").getAsJsonObject().get("resourceVendorModelNumber").getAsString(); + } catch (Exception e) { + throw fatalFailure(logger, "Unable to query VNF package with " + csarId + " from VF-C", e); + } + } + + @Override + public byte[] getPackage(String csarId) { + String downloadUrl; + try { + VnfpackageApi onapCatalogApi = restApiProvider.getOnapCatalogApi(); + VnfPkgDetailInfo vnfPackageDetails = onapCatalogApi.queryVnfPackage(csarId); + downloadUrl = vnfPackageDetails.getPackageInfo().getDownloadUrl(); + String host = new URL(downloadUrl).getHost(); + if (!ipMappingProvider.mapPrivateIpToPublicIp(host).equals(host)) { + downloadUrl = downloadUrl.replaceFirst("://" + host, "://" + ipMappingProvider.mapPrivateIpToPublicIp(host)); + } + } catch (Exception e) { + throw fatalFailure(logger, "Unable to query VNF package with " + csarId + " from VF-C", e); + } + try { + return downloadCbamVnfPackage(downloadUrl); + } catch (Exception e) { + throw fatalFailure(logger, "Unable to download package from " + downloadUrl + " from VF-C", e); + } + } + + private byte[] downloadCbamVnfPackage(String downloadUri) throws IOException { + CloseableHttpClient client = systemFunctions().getHttpClient(); + HttpGet httpget = new HttpGet(downloadUri); + httpget.setHeader(HttpHeaders.ACCEPT, APPLICATION_OCTET_STREAM_VALUE); + CloseableHttpResponse response = client.execute(httpget); + HttpEntity entity = response.getEntity(); + InputStream is = entity.getContent(); + ByteArrayOutputStream cbamInZip = new ByteArrayOutputStream(); + byte[] bytes = ByteStreams.toByteArray(is); + client.close(); + return bytes; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcRestApiProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcRestApiProvider.java new file mode 100644 index 00000000..fd4e6932 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/onap/vfc/VfcRestApiProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.MsbApiProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring.Conditions; +import org.onap.vfccatalog.api.VnfpackageApi; +import org.onap.vnfmdriver.api.NslcmApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; + +/** + * Responsible for providing access to VF-C REST APIs + */ +@Component +@Conditional(value = Conditions.UseForVfc.class) +public class VfcRestApiProvider { + static final String NSLCM_API_SERVICE_NAME = "nslcm"; + static final String NSLCM_API_VERION = "v1"; + static final String NSCATALOG_SERVICE_NAME = "catalog"; + static final String NSCATALOG_API_VERSION = "v1"; + private final MsbApiProvider msbApiProvider; + + @Autowired + VfcRestApiProvider(MsbApiProvider msbApiProvider) { + this.msbApiProvider = msbApiProvider; + } + + /** + * @return API to access VF-C for granting & LCN API + */ + public NslcmApi getNsLcmApi() { + org.onap.vnfmdriver.ApiClient apiClient = new org.onap.vnfmdriver.ApiClient(); + String correctedUrl = fixIncorrectUrl(); + apiClient.setBasePath(correctedUrl); + return new NslcmApi(apiClient); + } + + /** + * @return API to access VF-C catalog API + */ + public VnfpackageApi getOnapCatalogApi() { + org.onap.vfccatalog.ApiClient vfcApiClient = new org.onap.vfccatalog.ApiClient(); + vfcApiClient.setBasePath(msbApiProvider.getMicroServiceUrl(NSCATALOG_SERVICE_NAME, NSCATALOG_API_VERSION)); + return new VnfpackageApi(vfcApiClient); + } + + /** + * The swagger schema definition is not consistent with MSB info. The MSB reports + * the base path /restapi/nsclm/v1 (correct) and the paths defined in swagger is + * /nsclm/v1 making all API calls /restapi/nsclm/v1/nsclm/v1 (incorrect) + * + * @return + */ + private String fixIncorrectUrl() { + String urlInMsb = msbApiProvider.getMicroServiceUrl(NSLCM_API_SERVICE_NAME, NSLCM_API_VERION); + //FIXME in VF-C swagger API definitions + return urlInMsb.replaceFirst("/nslcm/v1", ""); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfPackageBuilder.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfPackageBuilder.java new file mode 100644 index 00000000..5f9f6341 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfPackageBuilder.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer; + +import com.google.common.io.ByteStreams; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; + +/** + * Builds a CBAM VNF package capable to be deployed on ONAP from a CBAM package + */ +public class CbamVnfPackageBuilder { + + /** + * @param originalCbamVnfPackage the original CBAM VNF package + * @param vnfdLocation the location of the VNFD within the CBAM VNF package + * @param modifiedCbamVnfdContent the modified CBAM VNFD content + * @return the mod + */ + public byte[] toModifiedCbamVnfPackage(byte[] originalCbamVnfPackage, String vnfdLocation, String modifiedCbamVnfdContent) throws IOException { + ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(originalCbamVnfPackage)); + ByteArrayOutputStream result = new ByteArrayOutputStream(); + ZipOutputStream out = new ZipOutputStream(result); + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + if (zipEntry.getName().matches(vnfdLocation)) { + out.putNextEntry(new ZipEntry(vnfdLocation)); + out.write(modifiedCbamVnfdContent.getBytes()); + out.closeEntry(); + } else { + out.putNextEntry(new ZipEntry(zipEntry.getName())); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ByteStreams.copy(zipInputStream, byteArrayOutputStream); + out.write(byteArrayOutputStream.toByteArray()); + out.closeEntry(); + } + } + out.putNextEntry(new ZipEntry("javascript/cbam.pre.collectConnectionPoints.js")); + out.write(systemFunctions().loadFile("cbam.pre.collectConnectionPoints.js")); + out.closeEntry(); + out.putNextEntry(new ZipEntry("javascript/cbam.collectConnectionPoints.js")); + out.write(systemFunctions().loadFile("cbam.collectConnectionPoints.js")); + out.closeEntry(); + out.putNextEntry(new ZipEntry("javascript/cbam.post.collectConnectionPoints.js")); + out.write(systemFunctions().loadFile("cbam.post.collectConnectionPoints.js")); + out.closeEntry(); + out.close(); + zipInputStream.close(); + return result.toByteArray(); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfdBuilder.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfdBuilder.java new file mode 100644 index 00000000..ba17bbfd --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/CbamVnfdBuilder.java @@ -0,0 +1,100 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import com.google.gson.*; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.child; + +/** + * Modifies a CBAM VNFD to fit ONAP + */ +public class CbamVnfdBuilder { + + /** + * @param cbamVnfdContent the original CBAM VNFD + * @return the modified content CBAM VNFD + */ + public String build(String cbamVnfdContent) throws IOException { + JsonObject root = new Gson().toJsonTree(new Yaml().load(cbamVnfdContent)).getAsJsonObject(); + JsonObject substitution_mappings = child(child(root, "topology_template"), "substitution_mappings"); + JsonObject extensions = addChild(addChild(addChild(addChild(addChild(substitution_mappings, "capabilities"), "vnf"), "properties"), "modifiable_attributes"), "extensions"); + JsonObject onapCsarId = addChild(extensions, "onapCsarId"); + onapCsarId.add("default", new JsonPrimitive("kuku")); + JsonObject vimId = addChild(extensions, "vimId"); + vimId.add("default", new JsonPrimitive("kuku")); + JsonObject interfaces = child(substitution_mappings, "interfaces"); + JsonObject basic = addChild(interfaces, "Basic"); + addOperationParams(addChild(basic, "instantiate")); + addOperationParams(addChild(basic, "terminate")); + if (interfaces.has("Scalable")) { + addOperationParams(addChild(child(interfaces, "Scalable"), "scale")); + } + if (interfaces.has("Healable")) { + addOperationParams(addChild(child(interfaces, "Healable"), "heal")); + } + JsonNode jsonNodeTree = new ObjectMapper().readTree(new GsonBuilder().setPrettyPrinting().create().toJson(root)); + return new YAMLMapper().writeValueAsString(jsonNodeTree); + } + + private void addOperationParams(JsonObject operation) { + JsonObject inputs = addChild(operation, "inputs"); + JsonObject extensions = addChild(inputs, "extensions"); + JsonArray pre_actions = addChildArray(extensions, "pre_actions"); + pre_actions.add(addAction("javascript/cbam.pre.collectConnectionPoints.js")); + JsonArray post_actions = addChildArray(extensions, "post_actions"); + post_actions.add(addAction("javascript/cbam.post.collectConnectionPoints.js")); + JsonObject additional_parameters = addChild(inputs, "additional_parameters"); + additional_parameters.addProperty("jobId", "kuku"); + } + + private JsonElement addAction(String jsAction) { + JsonObject action = new JsonObject(); + action.addProperty("javascript", jsAction); + JsonArray myInclude = new JsonArray(); + myInclude.add("javascript/cbam.collectConnectionPoints.js"); + action.add("include", myInclude); + action.addProperty("output", "operation_result"); + return action; + } + + private JsonArray addChildArray(JsonObject root, String name) { + if (root.has(name)) { + return root.get(name).getAsJsonArray(); + } else { + JsonArray child = new JsonArray(); + root.add(name, child); + return child; + } + } + + private JsonObject addChild(JsonObject root, String name) { + if (root.has(name)) { + return root.get(name).getAsJsonObject(); + } else { + JsonObject child = new JsonObject(); + root.add(name, child); + return child; + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfPackageBuilder.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfPackageBuilder.java new file mode 100644 index 00000000..f769becb --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfPackageBuilder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer; + +import com.google.common.io.ByteStreams; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CatalogManager.getFileInZip; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CatalogManager.getVnfdLocation; + +/** + * Transforms a CBAM package into an ONAP package + */ + +public class OnapVnfPackageBuilder { + + /** + * Entry point for the command line package transformer + * + * @param args not used (required due to signature) + */ + public static void main(String[] args) throws Exception { + byte[] covert = new OnapVnfPackageBuilder().covert(systemFunctions().in()); + systemFunctions().out().write(covert); + } + + /** + * @param zip the original CBAM package + * @return the converted ONAP package + */ + public byte[] covert(InputStream zip) throws Exception { + byte[] cbamVnfPackage = ByteStreams.toByteArray(zip); + String vnfdLocation = getVnfdLocation(new ByteArrayInputStream(cbamVnfPackage)); + ByteArrayOutputStream vnfdContent = getFileInZip(new ByteArrayInputStream(cbamVnfPackage), vnfdLocation); + byte[] cbamVnfdContent = vnfdContent.toByteArray(); + String onapVnfd = new OnapVnfdBuilder().toOnapVnfd(new String(cbamVnfdContent, StandardCharsets.UTF_8)); + byte[] modifiedCbamPackage = new CbamVnfPackageBuilder().toModifiedCbamVnfPackage(cbamVnfPackage, vnfdLocation, new CbamVnfdBuilder().build(new String(cbamVnfdContent))); + return buildNewOnapPackage(modifiedCbamPackage, onapVnfd); + } + + private byte[] buildNewOnapPackage(byte[] modifiedCbamPackage, String onapVnfd) throws Exception { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + ZipOutputStream out = new ZipOutputStream(result); + out.putNextEntry(new ZipEntry("Artifacts/Deployment/OTHER/cbam.package.zip")); + out.write(modifiedCbamPackage); + out.closeEntry(); + out.putNextEntry(new ZipEntry("TOSCA-Metadata/TOSCA.meta")); + out.write(systemFunctions().loadFile("TOSCA.meta")); + out.closeEntry(); + out.putNextEntry(new ZipEntry("MainServiceTemplate.yaml")); + out.write(onapVnfd.getBytes()); + out.closeEntry(); + out.putNextEntry(new ZipEntry("Definitions/MainServiceTemplate.yaml")); + out.write(onapVnfd.getBytes()); + out.closeEntry(); + out.putNextEntry(new ZipEntry("MainServiceTemplate.meta")); + out.write(systemFunctions().loadFile("MainServiceTemplate.meta")); + out.closeEntry(); + out.close(); + return result.toByteArray(); + } + + +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfdBuilder.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfdBuilder.java new file mode 100644 index 00000000..d4ff6e41 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/OnapVnfdBuilder.java @@ -0,0 +1,229 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.yaml.snakeyaml.Yaml; + +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.child; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.childElement; + +/** + * Transforms a CBAM package into an ONAP package + */ +public class OnapVnfdBuilder { + + private String buildHeader(JsonObject toplogyTemplate) { + JsonObject properties = child(child(toplogyTemplate, "substitution_mappings"), "properties"); + String descriptor_version = properties.get("descriptor_version").getAsString(); + return "tosca_definitions_version: tosca_simple_yaml_1_0\n" + + "\n" + + "metadata:\n" + + " vendor: Nokia\n" + + " csarVersion: " + descriptor_version + "\n" + + " csarProvider: " + properties.get("provider").getAsString() + "\n" + + " id: Simple\n" + + " version: " + properties.get("software_version").getAsString() + "\n" + + " csarType: NFAR\n" + + " name: " + properties.get("product_name").getAsString() + "\n" + + " vnfdVersion: " + descriptor_version + "\n\n" + + "topology_template:\n" + + " node_templates:\n"; + } + + private JsonElement get(String name, Set<Map.Entry<String, JsonElement>> nodes) { + for (Map.Entry<String, JsonElement> node : nodes) { + if (name.equals(node.getKey())) { + return node.getValue(); + } + } + throw new NoSuchElementException("The VNFD does not have a node called " + name + " but required by an other node"); + } + + private String buildVdu(String name, JsonObject vdu, Set<Map.Entry<String, JsonElement>> nodes) { + String memorySize = ""; + String cpuCount = ""; + StringBuilder body = new StringBuilder(); + JsonArray vduRequirements = childElement(vdu.getAsJsonObject(), "requirements").getAsJsonArray(); + for (int i = 0; i < vduRequirements.size(); i++) { + JsonObject requirement = vduRequirements.get(i).getAsJsonObject(); + Map.Entry<String, JsonElement> next = requirement.entrySet().iterator().next(); + switch (next.getKey()) { + case "virtual_compute": + JsonObject virtualCompute = get(next.getValue().getAsString(), nodes).getAsJsonObject(); + cpuCount = childElement(child(child(virtualCompute, "properties"), "virtual_cpu"), "num_virtual_cpu").getAsString(); + memorySize = childElement(child(child(virtualCompute, "properties"), "virtual_memory"), "virtual_mem_size").getAsString(); + break; + case "virtual_storage": + String item = + " - virtual_storage:\n" + + " capability: tosca.capabilities.nfv.VirtualStorage\n" + + " node: " + next.getValue().getAsString() + "\n"; + body.append(item); + break; + } + next.getValue(); + } + String header = " " + name + ":\n" + + " type: tosca.nodes.nfv.VDU.Compute\n" + + " capabilities:\n" + + " virtual_compute:\n" + + " properties:\n" + + " virtual_memory:\n" + + " virtual_mem_size: " + memorySize + "\n" + + " virtual_cpu:\n" + + " num_virtual_cpu: " + cpuCount + "\n" + + " requirements:\n"; + return header + body.toString(); + } + + /** + * @param cbamVnfd the CBAM VNFD + * @return the converted ONAP VNFD + */ + public String toOnapVnfd(String cbamVnfd) { + JsonObject root = new Gson().toJsonTree(new Yaml().load(cbamVnfd)).getAsJsonObject(); + JsonObject topology_template = child(root, "topology_template"); + if (topology_template.has("node_templates")) { + Set<Map.Entry<String, JsonElement>> node_templates = child(topology_template, "node_templates").entrySet(); + StringBuilder body = new StringBuilder(); + for (Map.Entry<String, JsonElement> node : node_templates) { + String type = childElement(node.getValue().getAsJsonObject(), "type").getAsString(); + switch (type) { + case "tosca.nodes.nfv.VDU": + body.append(buildVdu(node.getKey(), node.getValue().getAsJsonObject(), node_templates)); + break; + case "tosca.nodes.nfv.VirtualStorage": + body.append(buildVolume(node.getKey(), node.getValue().getAsJsonObject())); + break; + case "tosca.nodes.nfv.VL": + body.append(buildVl(node.getKey())); + break; + case "tosca.nodes.nfv.ICP": + body.append(buildIcp(node.getKey(), node.getValue().getAsJsonObject())); + break; + case "tosca.nodes.nfv.ECP": + body.append(buildEcp(node.getKey(), node.getValue(), node_templates)); + break; + } + } + return buildHeader(topology_template) + body.toString(); + } + return buildHeader(topology_template); + } + + private String buildEcp(String name, JsonElement ecp, Set<Map.Entry<String, JsonElement>> nodes) { + if (ecp.getAsJsonObject().has("requirements")) { + JsonArray requirements = ecp.getAsJsonObject().get("requirements").getAsJsonArray(); + String icpName = null; + for (int i = 0; i < requirements.size(); i++) { + JsonElement requirement = requirements.get(i); + Map.Entry<String, JsonElement> next = requirement.getAsJsonObject().entrySet().iterator().next(); + switch (next.getKey()) { + case "internal_connection_point": + icpName = next.getValue().getAsString(); + + } + } + if (icpName != null) { + JsonObject icpNode = get(icpName, nodes).getAsJsonObject(); + String vdu = null; + if (icpNode.has("requirements")) { + requirements = icpNode.getAsJsonObject().get("requirements").getAsJsonArray(); + for (int i = 0; i < requirements.size(); i++) { + JsonElement requirement = requirements.get(i); + Map.Entry<String, JsonElement> next = requirement.getAsJsonObject().entrySet().iterator().next(); + switch (next.getKey()) { + case "virtual_binding": + vdu = next.getValue().getAsString(); + } + } + if (vdu != null) { + JsonObject properties = child(icpNode, "properties"); + return " " + name + ":\n" + + " type: tosca.nodes.nfv.VduCpd\n" + + " properties:\n" + + " layer_protocol: " + childElement(properties, "layer_protocol").getAsString() + "\n" + + " role: leaf\n" + + (properties.has("description") ? + " description: " + childElement(properties, "description").getAsString() + "\n" : "") + + " requirements:\n" + + " - virtual_binding: " + vdu + "\n"; + } + } + } + } + return ""; + } + + private String buildIcp(String name, JsonObject icp) { + if (icp.has("requirements")) { + JsonArray requirements = icp.get("requirements").getAsJsonArray(); + String vdu = null; + String vl = null; + for (int i = 0; i < requirements.size(); i++) { + JsonElement requirement = requirements.get(i); + Map.Entry<String, JsonElement> next = requirement.getAsJsonObject().entrySet().iterator().next(); + switch (next.getKey()) { + case "virtual_binding": + vdu = next.getValue().getAsString(); + case "virtual_link": + vl = next.getValue().getAsString(); + break; + } + } + if (vdu != null && vl != null) { + JsonObject properties = child(icp, "properties"); + return " " + name + ":\n" + + " type: tosca.nodes.nfv.VduCpd\n" + + " properties:\n" + + " layer_protocol: " + childElement(properties, "layer_protocol").getAsString() + "\n" + + " role: leaf\n" + (properties.has("description") ? + " description: " + childElement(properties, "description").getAsString() + "\n" : "") + + " requirements:\n" + + " - virtual_binding: " + vdu + "\n" + + " - virtual_link: " + vl + "\n"; + } + } + return ""; + } + + private String buildVolume(String nodeName, JsonObject volume) { + return " " + nodeName + ":\n" + + " type: tosca.nodes.nfv.VDU.VirtualStorage\n" + + " properties:\n" + + " id: " + nodeName + "\n" + + " type_of_storage: volume\n" + + " size_of_storage: " + childElement(child(volume, "properties"), "size_of_storage").getAsString() + "\n"; + } + + private String buildVl(String name) { + return " " + name + ":\n" + + " type: tosca.nodes.nfv.VnfVirtualLinkDesc\n" + + " properties:\n" + + " vl_flavours:\n" + + " flavours:\n" + + " flavourId: notUsed\n"; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/package-info.java new file mode 100644 index 00000000..7a94ee79 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/packagetransformer/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * Converts a CBAM package into an ONAP package + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/ConverterApi.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/ConverterApi.java new file mode 100644 index 00000000..2013a7bf --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/ConverterApi.java @@ -0,0 +1,81 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.restapi; + +import org.apache.http.entity.ContentType; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer.OnapVnfPackageBuilder; +import org.slf4j.Logger; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties.BASE_URL; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.http.MediaType.TEXT_HTML_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +/** + * Responsible for providing the converter utilities for CBAM package format + */ +@Controller +@RequestMapping(value = BASE_URL) +public class ConverterApi { + private static Logger logger = getLogger(ConverterApi.class); + private OnapVnfPackageBuilder vnfPackageConverter = new OnapVnfPackageBuilder(); + + /** + * Return the converted ONAP package + * + * @param httpResponse the HTTP response + * @return the converted ONAP package + */ + @RequestMapping(value = "/convert", method = POST) + @ResponseBody + public void convert(HttpServletResponse httpResponse, HttpServletRequest request) throws Exception { + logger.info("REST: convert package"); + Part part = request.getParts().iterator().next(); + byte[] bytes = vnfPackageConverter.covert(part.getInputStream()); + httpResponse.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); + httpResponse.setStatus(HttpStatus.OK.value()); + httpResponse.addHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(bytes.length)); + httpResponse.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + "core.csar" + "\""); + httpResponse.getOutputStream().write(bytes); + httpResponse.getOutputStream().flush(); + } + + /** + * Return the HTTP page to upload the package + * Can be removed after the generated swagger API in ONAP is fixed. + * + * @param httpResponse the HTTP response + */ + @RequestMapping(value = "/convert", method = GET, produces = TEXT_HTML_VALUE) + @ResponseBody + public void getUploadPageForConvertingVnfd(HttpServletResponse httpResponse) throws Exception { + logger.info("REST: get converter main page"); + byte[] bytes = systemFunctions().loadFile("upload.html"); + httpResponse.addHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(bytes.length)); + httpResponse.getOutputStream().write(bytes); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcmApi.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcmApi.java new file mode 100644 index 00000000..fa7cec4c --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcmApi.java @@ -0,0 +1,149 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.restapi; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.JobManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.LifecycleManager; +import org.onap.vnfmdriver.model.*; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletResponse; + +import static org.apache.http.HttpStatus.SC_CREATED; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties.BASE_URL; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +/** + * Responsible for providing the Nokia sVNFM REST APIs + */ +@Controller +@RequestMapping(value = BASE_URL) +public class LcmApi { + private static Logger logger = getLogger(LcmApi.class); + + private final LifecycleManager lifecycleManager; + private final JobManager jobManager; + + @Autowired + LcmApi(LifecycleManager lifecycleManager, JobManager jobManager) { + this.lifecycleManager = lifecycleManager; + this.jobManager = jobManager; + } + + /** + * Instantiate the VNF (defined further in the VF-C driver integration documentation) + * + * @param request the instantiation request + * @param vnfmId the identifier of the VNFM + * @param httpResponse the HTTP response + * @return the instantiated VNF info + */ + @RequestMapping(value = "/{vnfmId}/vnfs", method = POST, produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @ResponseBody + public VnfInstantiateResponse instantiateVnf(@RequestBody VnfInstantiateRequest request, @PathVariable("vnfmId") String vnfmId, HttpServletResponse httpResponse) { + logger.info("REST: Instantiate VNF"); + VnfInstantiateResponse response = lifecycleManager.instantiate(vnfmId, request, httpResponse); + httpResponse.setStatus(SC_CREATED); + return response; + } + + /** + * Terminate the VNF (defined further in the VF-C driver integration documentation) + * + * @param request the instantiation request + * @param vnfmId the identifier of the VNFM + * @param vnfInstanceId the identifer of the VNF + * @param httpResponse the HTTP response + * @return the job representing the VNF termination operation + */ + @RequestMapping(value = "/{vnfmId}/vnfs/{vnfId}/terminate", method = POST, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public JobInfo terminateVnf(@RequestBody VnfTerminateRequest request, @PathVariable("vnfmId") String vnfmId, @PathVariable("vnfId") String vnfInstanceId, HttpServletResponse httpResponse) { + logger.info("REST: Terminate VNF"); + return lifecycleManager.terminateVnf(vnfmId, vnfInstanceId, request, httpResponse); + } + + /** + * Query the VNF (defined further in the VF-C driver integration documentation) + * + * @param vnfmId the identifier of the VNFM + * @param vnfInstanceId the identifer of the VNF + * @param httpResponse the HTTP response + * @return the VNF info + */ + @RequestMapping(value = "/{vnfmId}/vnfs/{vnfId}", method = GET, produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @ResponseBody + public VnfInfo queryVnf(@PathVariable("vnfmId") String vnfmId, @PathVariable("vnfId") String vnfInstanceId, HttpServletResponse httpResponse) { + logger.info("REST: Query VNF"); + return lifecycleManager.queryVnf(vnfmId, vnfInstanceId); + } + + /** + * Query the job (defined further in the VF-C driver integration documentation) + * + * @param jobId the identifer of the job + * @param vnfmId the identifier of the VNFM + * @param httpResponse the HTTP response + * @return the instantiated VNF info + */ + @RequestMapping(value = "/{vnfmId}/jobs/{jobId}", method = GET, produces = APPLICATION_JSON_VALUE) + @ResponseBody + public JobDetailInfo getJob(@PathVariable("vnfmId") String vnfmId, @PathVariable("jobId") String jobId, HttpServletResponse httpResponse) { + logger.debug("REST: Query job"); + return jobManager.getJob(vnfmId, jobId); + } + + /** + * Scale the VNF (defined further in the VF-C driver integration documentation) + * + * @param request the scaling request + * @param vnfmId the identifier of the VNFM + * @param vnfInstanceId the identifier of the VNF + * @param httpResponse the HTTP response + * @return the job representing the scaling operation + */ + @RequestMapping(value = "/{vnfmId}/vnfs/{vnfId}/scale", method = POST, produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @ResponseBody + public JobInfo scaleVnf(@RequestBody VnfScaleRequest request, @PathVariable("vnfmId") String vnfmId, @PathVariable("vnfId") String vnfInstanceId, HttpServletResponse httpResponse) { + logger.info("REST: Scale VNF"); + return lifecycleManager.scaleVnf(vnfmId, vnfInstanceId, request, httpResponse); + } + + /** + * Heal the VNF (defined further in the VF-C driver integration documentation) + * + * @param request the healing request + * @param vnfmId the identifier of the VNFM + * @param vnfInstanceId the identifier of the VNF + * @param httpResponse the HTTP response + * @return the job representing the healing operation + */ + @RequestMapping(value = "/{vnfmId}/vnfs/{vnfId}/heal", method = POST, produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE) + @ResponseBody + public JobInfo healVnf(@RequestBody VnfHealRequest request, @PathVariable("vnfmId") String vnfmId, @PathVariable("vnfId") String vnfInstanceId, HttpServletResponse httpResponse) { + logger.info("REST: Heal VNF"); + return lifecycleManager.healVnf(vnfmId, vnfInstanceId, request, httpResponse); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcnApi.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcnApi.java new file mode 100644 index 00000000..883c2709 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/LcnApi.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.restapi; + +import com.nokia.cbam.lcm.v32.model.VnfLifecycleChangeNotification; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.LifecycleChangeNotificationManager; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import javax.servlet.http.HttpServletResponse; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties.BASE_URL; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties.LCN_PATH; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.http.HttpStatus.NO_CONTENT; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.web.bind.annotation.RequestMethod.GET; +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +/** + * Responsible for providing the Nokia CBAM REST API for recieving LCNs from CBAM + */ +@Controller +@RequestMapping(value = BASE_URL) +public class LcnApi { + private static Logger logger = getLogger(LcnApi.class); + private final LifecycleChangeNotificationManager lcnManager; + + @Autowired + LcnApi(LifecycleChangeNotificationManager lcnManager) { + this.lcnManager = lcnManager; + } + + /** + * Provides a probe for CBAM VNFM to test LCN registration + * + * @param httpResponse the HTTP response + */ + @RequestMapping(value = LCN_PATH, method = GET) + public void testLcnConnectivity(HttpServletResponse httpResponse) { + //used to test connectivity from CBAM to driver + } + + /** + * Handle the LCN sent by CBAM + * + * @param lcn the LCN notification + * @param httpResponse the HTTP response + */ + @RequestMapping(value = LCN_PATH, method = POST, consumes = APPLICATION_JSON_VALUE) + @ResponseBody + @ResponseStatus(code = NO_CONTENT) + public void handleLcn(@RequestBody VnfLifecycleChangeNotification lcn, HttpServletResponse httpResponse) { + logger.info("REST: handle LCN"); + lcnManager.handleLcn(lcn); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/SwaggerApi.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/SwaggerApi.java new file mode 100644 index 00000000..6d4d19e6 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/SwaggerApi.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.restapi; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.SelfRegistrationManager; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletResponse; + +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties.BASE_URL; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.web.bind.annotation.RequestMethod.GET; + +/** + * Responsible for providing the Nokia S-VNFM REST APIs for accessing the swagger definitions + */ +@Controller +@RequestMapping(value = BASE_URL) +public class SwaggerApi { + private static Logger logger = getLogger(SwaggerApi.class); + private final SelfRegistrationManager selfRegistrationManager; + + @Autowired + SwaggerApi(SelfRegistrationManager selfRegistrationManager) { + this.selfRegistrationManager = selfRegistrationManager; + } + + /** + * Return the swagger definition + * + * @param httpResponse the HTTP response + * @return the job representing the healing operation + */ + @RequestMapping(value = "/swagger.json", method = GET) + @ResponseBody + public void getSwaggerApiDefinition(HttpServletResponse httpResponse) throws Exception { + logger.info("REST: get swagger definition"); + httpResponse.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + byte[] bytes = selfRegistrationManager.getSwaggerApiDefinition(); + httpResponse.addHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(bytes.length)); + httpResponse.getOutputStream().write(bytes); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/package-info.java new file mode 100644 index 00000000..4436d722 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/restapi/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * The provided REST interfaces. + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.restapi; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/Conditions.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/Conditions.java new file mode 100644 index 00000000..0554cbea --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/Conditions.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring; + +import com.google.common.collect.Sets; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.collect.Sets.newHashSet; + +/** + * Collects the possibilities of sources + */ +public class Conditions { + + private static final String USE_DIRECT_INTEGRATION = "direct"; + + private static Set<Condition> getAllSources() { + return newHashSet(new UseForVfc(), new UseForDirect()); + } + + /** + * Represents the condition for using VF-C + */ + public static class UseForVfc implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { + boolean anyOtherSourceAvailable = false; + for (Condition condition : Conditions.getAllSources()) { + if (!(condition instanceof UseForVfc) && condition.matches(conditionContext, annotatedTypeMetadata)) { + anyOtherSourceAvailable = true; + } + } + return !anyOtherSourceAvailable; + } + } + + /** + * Represents the condition for using ONAP components directly + */ + public static class UseForDirect implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { + HashSet<String> activeProfiles = Sets.newHashSet(conditionContext.getEnvironment().getActiveProfiles()); + return activeProfiles.contains(USE_DIRECT_INTEGRATION); + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/RealConfig.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/RealConfig.java new file mode 100644 index 00000000..26a3156f --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/RealConfig.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring; + +import com.nokia.cbam.lcm.v32.ApiClient; +import org.springframework.boot.autoconfigure.web.HttpMessageConverters; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.GsonHttpMessageConverter; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Responsible for configuring the raw REST input conversion + */ +@Configuration +public class RealConfig { + + /** + * Responsible for registering the RAW to POJO message converters. + * This is required since the generated POJOs are annotated with google GSON + * compatible annotations Jackson can not deserialize the stream. + * + * @return the message converter + */ + @Bean + public HttpMessageConverters customConverters() { + Collection<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); + GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter(); + gsonHttpMessageConverter.setGson(new ApiClient().getJSON().getGson()); + messageConverters.add(gsonHttpMessageConverter); + return new HttpMessageConverters(true, messageConverters); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/ServletInitializer.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/ServletInitializer.java new file mode 100644 index 00000000..fe1bb183 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/ServletInitializer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.NokiaSvnfmApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; + +/** + * Responsible for initializing the Spring application + */ +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(NokiaSvnfmApplication.class); + } + +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/package-info.java new file mode 100644 index 00000000..93f991fa --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/spring/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * Bootstraps the Spring environments + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.spring; diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/CbamUtils.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/CbamUtils.java new file mode 100644 index 00000000..7377abfc --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/CbamUtils.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.slf4j.Logger; + +public class CbamUtils { + + private CbamUtils() { + //use static way + } + + /** + * @param parent the parent JSON object + * @param name the name of the child + * @return the child JSON object of parent with given name + */ + public static JsonObject child(JsonObject parent, String name) { + return childElement(parent, name).getAsJsonObject(); + } + + /** + * @param parent the parent JSON object + * @param name the name of the child + * @return the child JSON object of parent with given name + */ + public static JsonElement childElement(JsonObject parent, String name) { + JsonElement child = parent.get(name); + if (child == null) { + throw new RuntimeException("Missing child " + name); + } + return child; + } + + /** + * Logs and throws a runtime exception + * + * @param logger the logger + * @param msg the error message + * @param e the exception to be wrapped + * @return never reached (runtime exception is thrown) + */ + public static RuntimeException fatalFailure(Logger logger, String msg, Exception e) { + logger.error(msg, e); + throw new RuntimeException(msg, e); + } + + /** + * Logs and throws a runtime exception + * + * @param logger the logger + * @param msg the error message + * @return never reached (runtime exception is thrown) + */ + public static RuntimeException fatalFailure(Logger logger, String msg) { + logger.error(msg); + throw new RuntimeException(msg); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/StoreLoader.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/StoreLoader.java new file mode 100644 index 00000000..faa436e3 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/StoreLoader.java @@ -0,0 +1,165 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util; + +import org.apache.commons.codec.binary.Base64; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStore.TrustedCertificateEntry; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Collection; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Create Truststore from the given certificates and keys + */ +public final class StoreLoader { + + public static final String PASSWORD = "password"; + private static final String RSA = "RSA"; + private static final String X_509 = "X.509"; + private static final String SUN = "SUN"; + private static final String JKS = "JKS"; + private static final String RSA_PRIVATE_KEY = "RSA PRIVATE KEY"; + private static final String CERTIFICATE = "CERTIFICATE"; + private static org.slf4j.Logger logger = getLogger(StoreLoader.class); + + private StoreLoader() { + } + + private static String getScope(String content, String scope) { + int rindex = content.indexOf(begin(scope)); + int lindex = content.indexOf(end(scope)); + if (rindex == -1 || lindex == -1) { + return ""; + } + String substring = content.substring(rindex, lindex) + end(scope); + return substring; + } + + /** + * @param content the content of the PEM ( a PEM may contain multiple certificates) + * @return the collection of certificates in the PEM + */ + public static Set<String> getCertifacates(String content) { + String lastCertificate = ""; + Set<String> certificates = new HashSet<>(); + do { + lastCertificate = getScope(content, CERTIFICATE); + content = content.replace(lastCertificate, ""); + if (!"".equals(lastCertificate)) { + certificates.add(lastCertificate); + } + } while (!"".equals(lastCertificate)); + return certificates; + } + + private static byte[] toDer(String pem, String scope) { + return Base64.decodeBase64(pem + .replace(begin(scope), "") + .replace(end(scope), "") + .replaceAll("\\s", "")); + } + + private static String begin(String scope) { + return "-----BEGIN " + scope + "-----"; + } + + private static String end(String scope) { + return "-----END " + scope + "-----"; + } + + /** + * Create new truststore from the given certificate + * + * @param pem the certificate which used to create the store + * @param storePassword the password to protect the store + * @param keyPassword the password to protect the key + * @return the created key store + */ + public static KeyStore loadStore(String pem, String storePassword, String keyPassword) { + Optional<PrivateKey> privateKey = generatePrivateKey(pem); + Certificate[] certs = createCertificates(pem); + try { + KeyStore ks = KeyStore.getInstance(JKS, SUN); + ks.load(null, storePassword.toCharArray()); + if (privateKey.isPresent()) { + ks.setKeyEntry(PASSWORD, privateKey.get(), keyPassword.toCharArray(), certs); + } else if (certs != null) { + int index = 0; + for (Certificate cert : certs) { + TrustedCertificateEntry ts = new TrustedCertificateEntry(cert); + ks.setEntry(PASSWORD + index, ts, null); + index++; + } + } + return ks; + } catch (Exception e) { + throw new RuntimeException("Unable to create keystore", e); + } + } + + private static Certificate[] createCertificates(String pem) { + Set<Certificate> certificates = new HashSet<>(); + try { + for (String certificate : getCertifacates(pem)) { + CertificateFactory certFactory = CertificateFactory.getInstance(X_509); + + InputStream is = new ByteArrayInputStream(toDer(certificate, CERTIFICATE)); + Collection<? extends Certificate> c = certFactory.generateCertificates(is); + certificates.addAll(c); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + throw new RuntimeException("Unable to load certificates", e); + } + + if (certificates.size() > 0) { + return certificates.toArray(new Certificate[certificates.size()]); + } else { + return null; + } + } + + private static Optional<PrivateKey> generatePrivateKey(String pem) { + try { + String key = getScope(pem, RSA_PRIVATE_KEY); + if (!key.isEmpty()) { + KeyFactory keyFactory = KeyFactory.getInstance(RSA); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(toDer(key, RSA_PRIVATE_KEY)); + return Optional.of(keyFactory.generatePrivate(keySpec)); + } + return Optional.empty(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + logger.error("Unable to load key", e); + throw new RuntimeException("Unable to load key", e); + } + } + +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/SystemFunctions.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/SystemFunctions.java new file mode 100644 index 00000000..e2a2e660 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/util/SystemFunctions.java @@ -0,0 +1,161 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util; + +import com.google.common.io.ByteStreams; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.InputStream; +import java.io.PrintStream; + +/** + * Wrapper class for static method calls to core or core libraries. + * Calls to static methods in core or core libraries are wrapped to be able to test + * the classes that uses static calls. + */ +public class SystemFunctions { + private static SystemFunctions INSTANCE; + + /** + * @return singleton instance + */ + public static SystemFunctions systemFunctions() { + if (INSTANCE != null) { + return INSTANCE; + } else { + synchronized (SystemFunctions.class) { + INSTANCE = new SystemFunctions(); + } + return INSTANCE; + } + } + + /** + * Causes the currently executing thread to sleep (temporarily cease + * execution) for the specified number of milliseconds, subject to + * the precision and accuracy of system timers and schedulers. The thread + * does not lose ownership of any monitors. + * + * @param millis the length of time to sleep in milliseconds + */ + public void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns the current time in milliseconds. Note that + * while the unit of time of the return value is a millisecond, + * the granularity of the value depends on the underlying + * operating system and may be larger. For example, many + * operating systems measure time in units of tens of + * milliseconds. + * <p> + * <p> See the description of the class <code>Date</code> for + * a discussion of slight discrepancies that may arise between + * Unable to load /unittests/missing * "computer time" and coordinated universal time (UTC). + * + * @return the difference, measured in milliseconds, between + * the current time and midnight, January 1, 1970 UTC. + * @see java.util.Date + */ + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + + /** + * Loads a file from the class path + * + * @param url the URL of the file + * @return the content of the file + */ + public byte[] loadFile(String url) { + try { + InputStream stream = SystemFunctions.class.getClassLoader().getResourceAsStream(url); + return ByteStreams.toByteArray(stream); + } catch (Exception e) { + throw new RuntimeException("Unable to load " + url, e); + } + } + + /** + * The "standard" error output stream. This stream is already + * open and ready to accept output data. + * <p> + * Typically this stream corresponds to display output or another + * output destination specified by the host environment or user. By + * convention, this output stream is used to display error messages + * or other information that should come to the immediate attention + * of a user even if the principal output stream, the value of the + * variable <code>out</code>, has been redirected to a file or other + * destination that is typically not continuously monitored. + */ + public PrintStream err() { + return System.err; + } + + /** + * The "standard" output stream. This stream is already + * open and ready to accept output data. Typically this stream + * corresponds to display output or another output destination + * specified by the host environment or user. + * <p> + * For simple stand-alone Java applications, a typical way to write + * a line of output data is: + * <blockquote><pre> + * System.out.println(data) + * </pre></blockquote> + * <p> + * See the <code>println</code> methods in class <code>PrintStream</code>. + * + * @see java.io.PrintStream#println() + * @see java.io.PrintStream#println(boolean) + * @see java.io.PrintStream#println(char) + * @see java.io.PrintStream#println(char[]) + * @see java.io.PrintStream#println(double) + * @see java.io.PrintStream#println(float) + * @see java.io.PrintStream#println(int) + * @see java.io.PrintStream#println(long) + * @see java.io.PrintStream#println(java.lang.Object) + * @see java.io.PrintStream#println(java.lang.String) + */ + public PrintStream out() { + return System.out; + } + + /** + * The "standard" input stream. This stream is already + * open and ready to supply input data. Typically this stream + * corresponds to keyboard input or another input source specified by + * the host environment or user. + */ + public InputStream in() { + return System.in; + } + + /** + * Wraps the static call (required for being able to test) + * + * @return the default HTTP client + */ + public CloseableHttpClient getHttpClient() { + return HttpClients.createDefault(); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/AdditionalParams.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/AdditionalParams.java new file mode 100644 index 00000000..879a9567 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/AdditionalParams.java @@ -0,0 +1,213 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.nokia.cbam.lcm.v32.model.*; +import com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum; + +import java.util.*; + +/** + * Represents the additional parameters to be sent during instantiation from VF-C to the driver + */ +public class AdditionalParams { + private VimInfoTypeEnum vimType; + private String domain; + private String instantiationLevel; + private List<VimComputeResourceFlavour> computeResourceFlavours = new ArrayList<>(); + private List<ZoneInfo> zones = new ArrayList<>(); + private List<VimSoftwareImage> softwareImages = new ArrayList<>(); + private List<ExtManagedVirtualLinkData> extManagedVirtualLinks = new ArrayList<>(); + private Map<String, List<NetworkAddress>> externalConnectionPointAddresses = new HashMap<>(); + private List<ExtVirtualLinkData> extVirtualLinks = new ArrayList<>(); + private Object additionalParams; + + AdditionalParams() { + //only used through reflection (gson) + } + + /** + * @return the additional parameters of the instantiation + */ + public Object getAdditionalParams() { + return additionalParams; + } + + /** + * @param additionalParams the additional parameters of the instantiation + */ + public void setAdditionalParams(Object additionalParams) { + this.additionalParams = additionalParams; + } + + /** + * @return the type of the VIM + */ + public VimInfoTypeEnum getVimType() { + return vimType; + } + + /** + * @param vimType the type of the VIM + */ + public void setVimType(VimInfoTypeEnum vimType) { + this.vimType = vimType; + } + + /** + * @return the domain of the OpenStack (required for v3 API) + */ + public String getDomain() { + return domain; + } + + /** + * @param domain + */ + public void setDomain(String domain) { + this.domain = domain; + } + + /** + * @return the flavours to be used for the VNF + */ + public List<VimComputeResourceFlavour> getComputeResourceFlavours() { + return computeResourceFlavours; + } + + /** + * @param computeResourceFlavours the flavours to be used for the VNF + */ + public void setComputeResourceFlavours(List<VimComputeResourceFlavour> computeResourceFlavours) { + this.computeResourceFlavours = computeResourceFlavours; + } + + /** + * @return the images to be used + */ + public List<VimSoftwareImage> getSoftwareImages() { + return softwareImages; + } + + /** + * @param softwareImages the images to be used + */ + public void setSoftwareImages(List<VimSoftwareImage> softwareImages) { + this.softwareImages = softwareImages; + } + + /** + * @return the zones to be used for the VNF + */ + public List<ZoneInfo> getZones() { + return zones; + } + + /** + * @param zones the zones to be used for the VNF + */ + public void setZones(List<ZoneInfo> zones) { + this.zones = zones; + } + + /** + * @return the instantiation level of the VNF + */ + public String getInstantiationLevel() { + return instantiationLevel; + } + + /** + * @param instantiationLevel the instantiation level of the VNF + */ + public void setInstantiationLevel(String instantiationLevel) { + this.instantiationLevel = instantiationLevel; + } + + /** + * @return the externally managed internal virtual links + */ + public List<ExtManagedVirtualLinkData> getExtManagedVirtualLinks() { + return extManagedVirtualLinks; + } + + /** + * @param extManagedVirtualLinks the externally managed internal virtual links + */ + public void setExtManagedVirtualLinks(List<ExtManagedVirtualLinkData> extManagedVirtualLinks) { + this.extManagedVirtualLinks = extManagedVirtualLinks; + } + + /** + * @return a binding of the extenal connection points by identifier to it's network addresses to be used + */ + public Map<String, List<NetworkAddress>> getExternalConnectionPointAddresses() { + return externalConnectionPointAddresses; + } + + /** + * @param externalConnectionPointAddresses a binding of the extenal connection points by identifier to it's network addresses to be used + */ + public void setExternalConnectionPointAddresses(Map<String, List<NetworkAddress>> externalConnectionPointAddresses) { + this.externalConnectionPointAddresses = externalConnectionPointAddresses; + } + + public List<ExtVirtualLinkData> getExtVirtualLinks() { + return extVirtualLinks; + } + + public void setExtVirtualLinks(List<ExtVirtualLinkData> extVirtualLinks) { + this.extVirtualLinks = extVirtualLinks; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AdditionalParams that = (AdditionalParams) o; + return vimType == that.vimType && + Objects.equals(domain, that.domain) && + Objects.equals(instantiationLevel, that.instantiationLevel) && + Objects.equals(computeResourceFlavours, that.computeResourceFlavours) && + Objects.equals(zones, that.zones) && + Objects.equals(softwareImages, that.softwareImages) && + Objects.equals(extManagedVirtualLinks, that.extManagedVirtualLinks) && + Objects.equals(externalConnectionPointAddresses, that.externalConnectionPointAddresses) && + Objects.equals(extVirtualLinks, that.extVirtualLinks) && + Objects.equals(additionalParams, that.additionalParams); + } + + @Override + public int hashCode() { + return Objects.hash(vimType, domain, instantiationLevel, computeResourceFlavours, zones, softwareImages, extManagedVirtualLinks, externalConnectionPointAddresses, extVirtualLinks, additionalParams); + } + + @Override + public String toString() { + return "AdditionalParams{" + + "vimType=" + vimType + + ", domain='" + domain + '\'' + + ", instantiationLevel='" + instantiationLevel + '\'' + + ", computeResourceFlavours=" + computeResourceFlavours + + ", zones=" + zones + + ", softwareImages=" + softwareImages + + ", extManagedVirtualLinks=" + extManagedVirtualLinks + + ", externalConnectionPointAddresses=" + externalConnectionPointAddresses + + ", extVirtualLinks=" + extVirtualLinks + + ", additionalParams=" + additionalParams + + '}'; + } +}
\ No newline at end of file diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CatalogManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CatalogManager.java new file mode 100644 index 00000000..414c0134 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CatalogManager.java @@ -0,0 +1,175 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.google.common.io.ByteStreams; +import com.nokia.cbam.catalog.v1.ApiException; +import com.nokia.cbam.catalog.v1.api.DefaultApi; +import com.nokia.cbam.catalog.v1.model.CatalogAdapterVnfpackage; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IPackageProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.vfc.VfcPackageProvider; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static com.google.common.base.Splitter.on; +import static com.google.common.collect.Iterables.filter; +import static java.nio.file.Files.createTempFile; +import static java.nio.file.Files.write; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.slf4j.LoggerFactory.getLogger; + + +/** + * Responsible for handling the CBAM catalog + * - the VNF package is uploaded as part of the instantiation + * - the VNF package is not deleted after VNF deletion + */ +@Component +public class CatalogManager { + private static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta"; + private static final String TOSCA_VNFD_KEY = "Entry-Definitions"; + private static Logger logger = getLogger(CatalogManager.class); + private final CbamRestApiProvider cbamRestApiProvider; + private final IPackageProvider packageProvider; + + @Autowired + CatalogManager(CbamRestApiProvider cbamRestApiProvider, IPackageProvider packageProvider) { + this.cbamRestApiProvider = cbamRestApiProvider; + this.packageProvider = packageProvider; + } + + /** + * @param zip the zip + * @param path the path of the file to be returned + * @return the file in the zip + */ + public static ByteArrayOutputStream getFileInZip(InputStream zip, String path) throws IOException { + ZipInputStream zipInputStream = new ZipInputStream(zip); + ByteArrayOutputStream fileContent = getFileInZip(zipInputStream, path); + zipInputStream.close(); + return fileContent; + } + + /** + * @param stream the CBAM VNF package + * @return the location of the VNFD within the CBAM package + */ + public static String getVnfdLocation(InputStream stream) throws IOException { + String toscaMetadata = new String(getFileInZip(stream, TOSCA_META_PATH).toByteArray()); + String toscaVnfdLine = filter(on("\n").split(toscaMetadata), line -> line.contains(TOSCA_VNFD_KEY)).iterator().next(); + return toscaVnfdLine.replace(TOSCA_VNFD_KEY + ":", "").trim(); + } + + private static ByteArrayOutputStream getFileInZip(ZipInputStream zipInputStream, String path) throws IOException { + ZipEntry zipEntry; + Set<String> items = new HashSet<>(); + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + items.add(zipEntry.getName()); + if (zipEntry.getName().matches(path)) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ByteStreams.copy(zipInputStream, byteArrayOutputStream); + return byteArrayOutputStream; + } + } + logger.error("Unable to find the " + path + " in archive found: " + items); + throw new NoSuchElementException("Unable to find the " + path + " in archive found: " + items); + } + + /** + * Prepare the VNF package in CBAM. If the package is not available in the catalog it is uploaded. + * + * @param vnfmId the identifier of the VNFM + * @param csarId the CSAR identifier of the package in ONAP catalog + * @return the package in CBAM catalog + */ + public CatalogAdapterVnfpackage preparePackageInCbam(String vnfmId, String csarId) { + String cbamVnfdId = packageProvider.getCbamVnfdId(csarId); + DefaultApi cbamCatalogApi = cbamRestApiProvider.getCbamCatalogApi(vnfmId); + if (!isPackageReplicated(cbamVnfdId, cbamCatalogApi)) { + try { + Path tempFile = createTempFile("cbam", "zip"); + ByteArrayOutputStream cbamPackage = getFileInZip(new ByteArrayInputStream(packageProvider.getPackage(csarId)), VfcPackageProvider.CBAM_PACKAGE_NAME_IN_ZIP); + write(tempFile, cbamPackage.toByteArray()); + //FIXME delete file + return cbamCatalogApi.create(tempFile.toFile()); + } catch (Exception e) { + logger.debug("Probably concurrent package uploads", e); + //retest if the VNF package exists in CBAM. It might happen that an other operation + //triggered the replication making this API fail. The replication is considered to be + //successful if the package exist in CBAM even if the current package transfer failed + if (isPackageReplicated(cbamVnfdId, cbamCatalogApi)) { + return queryPackageFromCBAM(cbamVnfdId, cbamCatalogApi); + } else { + throw fatalFailure(logger, "Unable to create VNF with " + csarId + " CSAR identifier in package in CBAM", e); + } + } + } + return queryPackageFromCBAM(cbamVnfdId, cbamCatalogApi); + } + + /** + * Gets the content of the VNFD from the CBAM package uploaded to CBAM + * + * @param vnfmId the identifier of the VNFM + * @param vnfdId the identifier of the VNFD + * @return the content of the CBAM VNFD + */ + public String getCbamVnfdContent(String vnfmId, String vnfdId) { + try { + DefaultApi cbamCatalogApi = cbamRestApiProvider.getCbamCatalogApi(vnfmId); + File content = cbamRestApiProvider.getCbamCatalogApi(vnfmId).content(vnfdId); + String vnfdPath = getVnfdLocation(new FileInputStream(content)); + return new String(getFileInZip(new FileInputStream(content), vnfdPath).toByteArray()); + } catch (Exception e) { + throw fatalFailure(logger, "Unable to get package with (" + vnfdId + ")", e); + } + } + + private boolean isPackageReplicated(String cbamVnfdId, DefaultApi cbamCatalogApi) { + try { + return isPackageReplicatedToCbam(cbamVnfdId, cbamCatalogApi); + } catch (Exception e) { + throw fatalFailure(logger, "Unable to determine if the VNF package has been replicated in CBAM", e); + } + } + + private CatalogAdapterVnfpackage queryPackageFromCBAM(String cbamVnfdId, DefaultApi cbamCatalogApi) { + try { + return cbamCatalogApi.getById(cbamVnfdId); + } catch (ApiException e) { + throw fatalFailure(logger, "Unable to query VNF package with " + cbamVnfdId + " from CBAM", e); + } + } + + private boolean isPackageReplicatedToCbam(String cbamVnfdId, DefaultApi cbamCatalogApi) throws ApiException { + for (CatalogAdapterVnfpackage vnfPackage : cbamCatalogApi.list()) { + if (vnfPackage.getVnfdId().equals(cbamVnfdId)) { + return true; + } + } + return false; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamRestApiProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamRestApiProvider.java new file mode 100644 index 00000000..5fdc919a --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamRestApiProvider.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.google.common.io.BaseEncoding; +import com.nokia.cbam.catalog.v1.api.DefaultApi; +import com.nokia.cbam.lcm.v32.ApiClient; +import com.nokia.cbam.lcm.v32.api.OperationExecutionsApi; +import com.nokia.cbam.lcm.v32.api.VnfsApi; +import com.nokia.cbam.lcn.v32.api.SubscriptionsApi; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VnfmInfoProvider; +import org.onap.vnfmdriver.model.VnfmInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayInputStream; + +/** + * Responsible for providing client to access CBAM REST API + */ +@Component +public class CbamRestApiProvider { + public static final String NOKIA_LCN_API_VERSION = "3.2"; + public static final String NOKIA_LCM_API_VERSION = "3.2"; + private final DriverProperties driverProperties; + private final CbamTokenProvider tokenProvider; + private final VnfmInfoProvider vnfmInfoProvider; + + @Value("${trustedCertificates}") + private String trustedCertificates; + @Value("${skipCertificateVerification}") + private boolean skipCertificateVerification; + + @Autowired + public CbamRestApiProvider(DriverProperties driverProperties, CbamTokenProvider cbamTokenProvider, VnfmInfoProvider vnfmInfoProvider) { + this.driverProperties = driverProperties; + this.tokenProvider = cbamTokenProvider; + this.vnfmInfoProvider = vnfmInfoProvider; + } + + /** + * @param vnfmId the identifier of the VNFM + * @return API to access CBAM LCM API + */ + public VnfsApi getCbamLcmApi(String vnfmId) { + return new VnfsApi(getLcmApiClient(vnfmId)); + } + + /** + * @param vnfmId the identifier of the VNFM + * @return API to access the operation executions + */ + public OperationExecutionsApi getCbamOperationExecutionApi(String vnfmId) { + return new OperationExecutionsApi(getLcmApiClient(vnfmId)); + } + + /** + * @param vnfmId the identifier of the VNFM + * @return API to access CBAM LCN subscription API + */ + public SubscriptionsApi getCbamLcnApi(String vnfmId) { + com.nokia.cbam.lcn.v32.ApiClient apiClient = new com.nokia.cbam.lcn.v32.ApiClient(); + if (!skipCertificateVerification) { + apiClient.setSslCaCert(new ByteArrayInputStream(BaseEncoding.base64().decode(trustedCertificates))); + } else { + apiClient.setVerifyingSsl(false); + } + apiClient.setBasePath(driverProperties.getCbamLcnUrl()); + apiClient.setAccessToken(tokenProvider.getToken(vnfmId)); + return new SubscriptionsApi(apiClient); + } + + /** + * @param vnfmId the identifier of the VNFM + * @return API to access CBAM catalog API + */ + public DefaultApi getCbamCatalogApi(String vnfmId) { + com.nokia.cbam.catalog.v1.ApiClient apiClient = new com.nokia.cbam.catalog.v1.ApiClient(); + if (!skipCertificateVerification) { + apiClient.setSslCaCert(new ByteArrayInputStream(BaseEncoding.base64().decode(trustedCertificates))); + } else { + apiClient.setVerifyingSsl(false); + } + apiClient.setBasePath(driverProperties.getCbamCatalogUrl()); + apiClient.setAccessToken(tokenProvider.getToken(vnfmId)); + return new DefaultApi(apiClient); + } + + private ApiClient getLcmApiClient(String vnfmId) { + VnfmInfo vnfmInfo = vnfmInfoProvider.getVnfmInfo(vnfmId); + ApiClient apiClient = new ApiClient(); + if (!skipCertificateVerification) { + apiClient.setSslCaCert(new ByteArrayInputStream(BaseEncoding.base64().decode(trustedCertificates))); + } else { + apiClient.setVerifyingSsl(false); + } + apiClient.setAccessToken(tokenProvider.getToken(vnfmId)); + apiClient.setBasePath(vnfmInfo.getUrl()); + return apiClient; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamTokenProvider.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamTokenProvider.java new file mode 100644 index 00000000..e49c5720 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/CbamTokenProvider.java @@ -0,0 +1,250 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.io.BaseEncoding; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import okhttp3.*; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VnfmInfoProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.StoreLoader; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions; +import org.onap.vnfmdriver.model.VnfmInfo; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import javax.net.ssl.*; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; +import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; + +/** + * Responsible for providing a token to access CBAM APIs + */ +@Component +public class CbamTokenProvider { + public static final int MAX_RETRY_COUNT = 5; + private static final String CBAM_TOKEN_PATH = "/realms/cbam/protocol/openid-connect/token"; + private static Logger logger = getLogger(CbamTokenProvider.class); + private final VnfmInfoProvider vnfmInfoProvider; + @Value("${cbamKeyCloakBaseUrl}") + private String cbamKeyCloakBaseUrl; + @Value("${cbamUsername}") + private String username; + @Value("${cbamPassword}") + private String password; + @Value("${trustedCertificates}") + private String trustedCertificates; + @Value("${skipCertificateVerification}") + private boolean skipCertificateVerification; + @Value("${skipHostnameVerification}") + private boolean skipHostnameVerification; + private volatile CurrentToken token; + + @Autowired + CbamTokenProvider(VnfmInfoProvider vnfmInfoProvider) { + this.vnfmInfoProvider = vnfmInfoProvider; + } + + /** + * @return the token to access CBAM APIs (ex. 123456) + */ + public String getToken(String vnfmId) { + VnfmInfo vnfmInfo = vnfmInfoProvider.getVnfmInfo(vnfmId); + return getToken(vnfmInfo.getUserName(), vnfmInfo.getPassword()); + } + + private String getToken(String clientId, String clientSecret) { + logger.trace("Requesting token for accessing CBAM API"); + synchronized (this) { + long now = SystemFunctions.systemFunctions().currentTimeMillis(); + if (token == null || token.refreshAfter < now) { + if (token == null) { + logger.debug("No token: getting first token"); + } else { + logger.debug("Token expired " + (now - token.refreshAfter) + " ms ago"); + } + refresh(clientId, clientSecret); + } else { + logger.debug("Token will expire in " + (now - token.refreshAfter) + " ms"); + } + } + return token.token.accessToken; + } + + ; + + private void refresh(String clientId, String clientSecret) { + FormBody body = new FormBody.Builder() + .add("grant_type", "password") + .add("client_id", clientId) + .add("client_secret", clientSecret) + .add("username", username) + .add("password", password).build(); + Request request = new Request.Builder().url(cbamKeyCloakBaseUrl + CBAM_TOKEN_PATH).addHeader(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE).post(body).build(); + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + SSLSocketFactory sslSocketFac = buildSSLSocketFactory(); + HostnameVerifier hostnameVerifier = buildHostnameVerifier(); + OkHttpClient client = builder.sslSocketFactory(sslSocketFac).hostnameVerifier(hostnameVerifier).build(); + Exception lastException = null; + for (int i = 0; i < MAX_RETRY_COUNT; i++) { + try { + Response response = execute(client.newCall(request)); + if (response.isSuccessful()) { + String json = response.body().string(); + TokenResponse tokenResponse = new Gson().fromJson(json, TokenResponse.class); + //token is scheduled to be refreshed in the half time before expiring + token = new CurrentToken(tokenResponse, getTokenRefreshTime(tokenResponse)); + return; + } else { + throw new RuntimeException(); + } + } catch (Exception e) { + lastException = e; + logger.warn("Unable to get token to access CBAM API (" + (i + 1) + "/" + MAX_RETRY_COUNT + ")", e); + } + } + logger.error("Unable to get token to access CBAM API (giving up retries)", lastException); + throw new RuntimeException(lastException); + } + + @VisibleForTesting + Response execute(Call call) throws IOException { + return call.execute(); + } + + /** + * - a new token is requested after the half of the time has expired till which the currently + * used token is valid + * + * @param token the currently held token + * @return the point in time after which a new token must be requested + */ + private long getTokenRefreshTime(TokenResponse token) { + return SystemFunctions.systemFunctions().currentTimeMillis() + token.expiresIn * (1000 / 2); + } + + private HostnameVerifier buildHostnameVerifier() { + if (skipHostnameVerification) { + return new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } else { + return new DefaultHostnameVerifier(); + } + } + + @VisibleForTesting + SSLSocketFactory buildSSLSocketFactory() { + try { + TrustManager[] trustManagers = buildTrustManager(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagers, new SecureRandom()); + return sslContext.getSocketFactory(); + } catch (GeneralSecurityException e) { + logger.error("Unable to create SSL socket factory", e); + throw new RuntimeException(e); + } + } + + @VisibleForTesting + TrustManager[] buildTrustManager() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException { + if (skipCertificateVerification) { + return new TrustManager[]{new AllTrustedTrustManager()}; + } else { + if (StringUtils.isEmpty(trustedCertificates)) { + throw new IllegalArgumentException("If the skipCertificateVerification is set to false (default) the trustedCertificates can not be empty"); + } + Set<String> trustedPems; + try { + trustedPems = StoreLoader.getCertifacates(new String(BaseEncoding.base64().decode(trustedCertificates), StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new RuntimeException("The trustedCertificates must be a base64 encoded collection of PEM certificates", e); + } + KeyStore keyStore = StoreLoader.loadStore(Joiner.on("\n").join(trustedPems), "password", "password"); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keyStore); + return trustManagerFactory.getTrustManagers(); + + } + } + + private static class CurrentToken { + private final TokenResponse token; + private final long refreshAfter; + + CurrentToken(TokenResponse token, long refreshAfter) { + this.refreshAfter = refreshAfter; + this.token = token; + } + } + + static class AllTrustedTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + + /** + * Represents the token received from CBAM + */ + //FIXME use authentication swagger client instead + private static class TokenResponse { + @SerializedName("access_token") + String accessToken; + @SerializedName("expires_in") + int expiresIn; + @SerializedName("id_token") + String tokenId; + @SerializedName("not-before-policy") + int notBeforePolicy; + @SerializedName("refresh_expires_in") + int refreshExpiresIn; + @SerializedName("refresh_token") + String refreshToken; + @SerializedName("session_state") + String sessionState; + @SerializedName("token_type") + String tokenType; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/DriverProperties.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/DriverProperties.java new file mode 100644 index 00000000..dfa674b7 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/DriverProperties.java @@ -0,0 +1,106 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.SelfRegistrationManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * Wraps the properties supplied to the servlet + */ +@Component +public class DriverProperties { + public static final String BASE_SUFFIX = "/" + SelfRegistrationManager.SERVICE_NAME + "/v1"; + public static final String BASE_URL = "/api" + BASE_SUFFIX; + public static final String LCN_PATH = "/lcn"; + + @Value("${cbamCatalogUrl}") + private String cbamCatalogUrl; + @Value("${cbamLcnUrl}") + private String cbamLcnUrl; + @Value("${vnfmId}") + private String vnfmId; + + /** + * @return the URL on which the CBAM catalog API can be accessed (ex. https://1.2.3.4:443/api/catalog/adapter ) + */ + public String getCbamCatalogUrl() { + return cbamCatalogUrl; + } + + /** + * @param cbamCatalogUrl the URL on which the CBAM catalog API can be accessed (ex. https://1.2.3.4:443/api/catalog/adapter ) + */ + public void setCbamCatalogUrl(String cbamCatalogUrl) { + this.cbamCatalogUrl = cbamCatalogUrl; + } + + /** + * @return the URL on which the CBAM LCN subscription API can be accessed (ex. https://1.2.3.4:443/vnfm/lcn/v3 ) + */ + public String getCbamLcnUrl() { + return cbamLcnUrl; + } + + /** + * @param cbamLcnUrl the URL on which the CBAM LCN subscription API can be accessed (ex. https://1.2.3.4:443/vnfm/lcn/v3 ) + */ + public void setCbamLcnUrl(String cbamLcnUrl) { + this.cbamLcnUrl = cbamLcnUrl; + } + + /** + * @return the identifier of the VNFM + */ + public String getVnfmId() { + return vnfmId; + } + + /** + * @param vnfmId the identifier of the VNFM + */ + public void setVnfmId(String vnfmId) { + this.vnfmId = vnfmId; + } + + @Override + public String toString() { + return "DriverProperties{" + + ", cbamCatalogUrl='" + cbamCatalogUrl + '\'' + + ", cbamLcnUrl='" + cbamLcnUrl + '\'' + + ", vnfmId='" + vnfmId + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DriverProperties that = (DriverProperties) o; + return Objects.equals(cbamCatalogUrl, that.cbamCatalogUrl) && + Objects.equals(cbamLcnUrl, that.cbamLcnUrl) && + Objects.equals(vnfmId, that.vnfmId); + } + + @Override + public int hashCode() { + return Objects.hash(cbamCatalogUrl, cbamLcnUrl, vnfmId); + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/ILifecycleChangeNotificationManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/ILifecycleChangeNotificationManager.java new file mode 100644 index 00000000..daf16fc3 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/ILifecycleChangeNotificationManager.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.google.common.collect.Ordering; +import com.nokia.cbam.lcm.v32.model.OperationExecution; +import com.nokia.cbam.lcm.v32.model.VnfLifecycleChangeNotification; + +/** + * Responsible for handling CBAM notifications + */ +public interface ILifecycleChangeNotificationManager { + /** + * The key of the CBAM VNF extension for the identifier of the VNFM in ONAP + */ + String EXTERNAL_VNFM_ID = "externalVnfmId"; + + /** + * Separator for multiple keys concatenated into a single string + */ + String SEPARATOR = "_"; + + /** + * Order the operations by start time (latest first) + */ + Ordering<OperationExecution> NEWEST_OPERATIONS_FIRST = new Ordering<OperationExecution>() { + @Override + public int compare(OperationExecution left, OperationExecution right) { + return right.getStartTime().toLocalDate().compareTo(left.getStartTime().toLocalDate()); + } + }; + + /** + * Transform a CBAM LCN into ONAP LCN + * + * @param receivedNotification the CBAM LCN + */ + void handleLcn(VnfLifecycleChangeNotification receivedNotification); + + /** + * Wait for the termination finish notification to be processed + * + * @param operationExecutionId the identifier of the termination operation + */ + void waitForTerminationToBeProcessed(String operationExecutionId); +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/JobManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/JobManager.java new file mode 100644 index 00000000..d6b1cb20 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/JobManager.java @@ -0,0 +1,308 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.nokia.cbam.lcm.v32.ApiException; +import com.nokia.cbam.lcm.v32.api.OperationExecutionsApi; +import com.nokia.cbam.lcm.v32.api.VnfsApi; +import com.nokia.cbam.lcm.v32.model.OperationExecution; +import com.nokia.cbam.lcm.v32.model.VnfInfo; +import org.apache.http.HttpStatus; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.onap.core.SelfRegistrationManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification.LifecycleChangeNotificationManager; +import org.onap.vnfmdriver.model.JobDetailInfo; +import org.onap.vnfmdriver.model.JobDetailInfoResponseDescriptor; +import org.onap.vnfmdriver.model.JobResponseInfo; +import org.onap.vnfmdriver.model.JobStatus; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletResponse; +import java.util.*; + +import static com.google.common.base.Splitter.on; +import static com.google.common.collect.Iterables.tryFind; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager.SEPARATOR; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.util.StringUtils.isEmpty; + +/** + * Responsible for providing the status of jobs + * The job id is a composite field of the VNF identifier and an UUID. + * The second UUID is passed as mandatory parameter to each executed operation. + * This UUID is used to locate the operation execution from the ONAP job identifier + */ +@Component +public class JobManager { + private static final Ordering<JobResponseInfo> OLDEST_FIRST = new Ordering<JobResponseInfo>() { + @Override + public int compare(JobResponseInfo left, JobResponseInfo right) { + return Long.valueOf(left.getResponseId()).compareTo(Long.valueOf(right.getResponseId())); + } + }; + private static Logger logger = getLogger(JobManager.class); + private final Set<String> ongoingJobs = Sets.newConcurrentHashSet(); + private final CbamRestApiProvider cbamRestApiProvider; + private final SelfRegistrationManager selfRegistrationManager; + private volatile boolean preparingForShutDown = false; + + @Autowired + JobManager(CbamRestApiProvider cbamRestApiProvider, SelfRegistrationManager selfRegistrationManager) { + this.cbamRestApiProvider = cbamRestApiProvider; + this.selfRegistrationManager = selfRegistrationManager; + } + + /** + * @param operationParams the operation execution + * @return the ONAP job identifier of belonging to the operation execution + */ + public static String extractOnapJobId(Object operationParams) { + JsonElement operationParamsAsJson = new Gson().toJsonTree(operationParams); + JsonElement additionalParams = operationParamsAsJson.getAsJsonObject().get("additionalParams"); + if (additionalParams == null) { + throw new NoSuchElementException("The operation result " + operationParamsAsJson + " does not contain the mandatory additionalParams structure"); + } + JsonElement jobId = additionalParams.getAsJsonObject().get("jobId"); + if (jobId == null) { + throw new NoSuchElementException("The operation result " + operationParamsAsJson + " does not contain the mandatory jobId in the additionalParams structure"); + } + return jobId.getAsString(); + } + + /** + * Throws an exception in case the service is not ready to serve requests due to + * not being able to register to MSB or to subscribe to CBAM LCNs + * + * @param vnfId the identifier of the VNF + * @param response the HTTP response of the current sVNFM incomming request + * @return the identifier of the job + */ + public String spawnJob(String vnfId, HttpServletResponse response) { + String jobId = vnfId + SEPARATOR + UUID.randomUUID().toString(); + synchronized (this) { + if (preparingForShutDown) { + response.setStatus(HttpStatus.SC_SERVICE_UNAVAILABLE); + fatalFailure(logger, "The service is preparing to shut down"); + } + if (!selfRegistrationManager.isReady()) { + response.setStatus(HttpStatus.SC_SERVICE_UNAVAILABLE); + fatalFailure(logger, "The service is not yet ready"); + } + } + ongoingJobs.add(jobId); + return jobId; + } + + /** + * Signal that a job has finished + * + * @param jobId the identifier of the job + */ + public void jobFinished(String jobId) { + ongoingJobs.remove(jobId); + } + + /** + * @return the system has any ongoing jobs + */ + public boolean hasOngoingJobs() { + return ongoingJobs.size() != 0; + } + + + /** + * Wait for all jobs to be cleared from the system the refuses to let additional request in + */ + public void prepareForShutdown() { + preparingForShutDown = true; + while (true) { + synchronized (this) { + if (!hasOngoingJobs()) { + return; + } + } + systemFunctions().sleep(500L); + } + } + + /** + * @param vnfmId the identifier of the VNFM + * @param jobId the identifier of the job + * @return detailed information of the job + */ + public JobDetailInfo getJob(String vnfmId, String jobId) { + logger.debug("Retrieving the details for job with " + jobId); + ArrayList<String> jobParts = newArrayList(on(SEPARATOR).split(jobId)); + if (jobParts.size() != 2) { + throw new IllegalArgumentException("The jobId should be in the <vnfId>" + SEPARATOR + "<UUID> format, but was " + jobId); + } + String vnfId = jobParts.get(0); + if (isEmpty(vnfId)) { + throw new IllegalArgumentException("The vnfId in the jobId (" + jobId + ") can not be empty"); + } + String operationExecutionId = jobParts.get(1); + if (isEmpty(operationExecutionId)) { + throw new IllegalArgumentException("The UUID in the jobId (" + jobId + ") can not be empty"); + } + Optional<VnfInfo> vnf = getVnf(vnfmId, vnfId); + if (!vnf.isPresent()) { + return getJobDetailInfoForMissingVnf(jobId); + } else { + return getJobInfoForExistingVnf(vnfmId, jobId, vnfId, vnf); + } + } + + private JobDetailInfo getJobDetailInfoForMissingVnf(String jobId) { + if (ongoingJobs.contains(jobId)) { + return reportOngoing(jobId); + } else { + return reportFinished(jobId); + } + } + + private JobDetailInfo getJobInfoForExistingVnf(String vnfmId, String jobId, String vnfId, Optional<VnfInfo> vnf) { + try { + OperationExecution operation = findOperationByJobId(vnfmId, vnf, jobId); + switch (operation.getStatus()) { + case STARTED: + return reportOngoing(jobId); + case FINISHED: + case OTHER: + switch (operation.getOperationType()) { + case TERMINATE: + //termination includes VNF deletion in ONAP terminology + if (ongoingJobs.contains(jobId)) { + return reportOngoing(jobId); + } else { + //the VNF must be queried again since it could have been deleted since the VNF has been terminated + if (getVnf(vnfmId, vnfId).isPresent()) { + return reportFailed(jobId, "unable to delete VNF"); + } else { + return reportFinished(jobId); + } + } + default: + return reportFinished(jobId); + } + default: //all cases handled + case FAILED: + return reportFailed(jobId, operation.getError().getTitle() + ": " + operation.getError().getDetail()); + } + } catch (NoSuchElementException e) { + if (ongoingJobs.contains(jobId)) { + return reportOngoing(jobId); + } else { + return reportFailed(jobId, "The requested operation was not able to start on CBAM"); + } + } + } + + private JobDetailInfo buildJob(String jobId, JobResponseInfo... history) { + JobDetailInfo job = new JobDetailInfo(); + job.setJobId(jobId); + JobDetailInfoResponseDescriptor jobDetailInfoResponseDescriptor = new JobDetailInfoResponseDescriptor(); + job.setResponseDescriptor(jobDetailInfoResponseDescriptor); + List<JobResponseInfo> oldestFirst = OLDEST_FIRST.sortedCopy(newArrayList(history)); + JobResponseInfo newestJob = oldestFirst.get(oldestFirst.size() - 1); + jobDetailInfoResponseDescriptor.setResponseId(newestJob.getResponseId()); + jobDetailInfoResponseDescriptor.setStatus(JobStatus.valueOf(newestJob.getStatus())); + jobDetailInfoResponseDescriptor.setProgress(newestJob.getProgress()); + jobDetailInfoResponseDescriptor.setStatusDescription(newestJob.getStatusDescription()); + jobDetailInfoResponseDescriptor.setErrorCode(newestJob.getErrorCode()); + jobDetailInfoResponseDescriptor.setResponseHistoryList(oldestFirst); + return job; + } + + private JobResponseInfo buildJobPart(String description, JobStatus status, Integer progress, Integer responseId) { + JobResponseInfo currentJob = new JobResponseInfo(); + currentJob.setProgress(progress.toString()); + currentJob.setResponseId(responseId.toString()); + currentJob.setStatus(status.name()); + currentJob.setStatusDescription(description); + return currentJob; + } + + private JobDetailInfo reportOngoing(String jobId) { + return buildJob(jobId, buildJobPart("Operation started", JobStatus.STARTED, 50, 1)); + } + + private JobDetailInfo reportFailed(String jobId, String reason) { + return buildJob(jobId, + buildJobPart("Operation started", JobStatus.STARTED, 50, 1), + buildJobPart("Operation failed due to " + reason, JobStatus.ERROR, 100, 2) + ); + } + + private JobDetailInfo reportFinished(String jobId) { + return buildJob(jobId, + buildJobPart("Operation started", JobStatus.STARTED, 50, 1), + buildJobPart("Operation finished", JobStatus.FINISHED, 100, 2) + ); + } + + private OperationExecution findOperationByJobId(String vnfmId, Optional<VnfInfo> vnf, String jobId) { + OperationExecutionsApi cbamOperationExecutionApi = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId); + //the operations are sorted so that the newest operations are queried first + //performance optimization that usually the core system is interested in the operations executed last + if (vnf.get().getOperationExecutions() != null) { + for (OperationExecution operationExecution : LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST.sortedCopy(vnf.get().getOperationExecutions())) { + try { + Object operationParams = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operationExecution.getId(), NOKIA_LCM_API_VERSION); + if (extractOnapJobId(operationParams).equals(jobId)) { + return operationExecution; + } + } catch (ApiException e) { + logger.error("Unable to retrieve operation parameters", e); + throw new RuntimeException(e); + } + } + } + throw new NoSuchElementException(); + } + + private Optional<VnfInfo> getVnf(String vnfmId, String vnfId) { + try { + //test if the VNF exists (required to be able to distingush between failed request ) + VnfsApi cbamLcmApi = cbamRestApiProvider.getCbamLcmApi(vnfmId); + logger.debug("Listing VNFs"); + List<VnfInfo> vnfs = cbamLcmApi.vnfsGet(NOKIA_LCM_API_VERSION); + com.google.common.base.Optional<VnfInfo> vnf = tryFind(vnfs, vnfInfo -> vnfId.equals(vnfInfo.getId())); + if (!vnf.isPresent()) { + logger.debug("VNF with " + vnfId + " is missing"); + return empty(); + } else { + logger.debug("VNF with " + vnfId + " still exists"); + //query the VNF again to get operation execution result + return of(cbamLcmApi.vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION)); + } + } catch (ApiException e) { + logger.error("Unable to retrieve VNF", e); + throw new RuntimeException(e); + } + } + +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/LifecycleManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/LifecycleManager.java new file mode 100644 index 00000000..e835e35c --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/LifecycleManager.java @@ -0,0 +1,537 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; + + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.nokia.cbam.catalog.v1.model.CatalogAdapterVnfpackage; +import com.nokia.cbam.lcm.v32.ApiException; +import com.nokia.cbam.lcm.v32.model.*; +import com.nokia.cbam.lcm.v32.model.ScaleDirection; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.IGrantManager; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.VimInfoProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.StoreLoader; +import org.onap.vnfmdriver.model.ExtVirtualLinkInfo; +import org.onap.vnfmdriver.model.*; +import org.onap.vnfmdriver.model.VimInfo; +import org.onap.vnfmdriver.model.VnfInfo; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.yaml.snakeyaml.Yaml; + +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static com.google.common.base.Splitter.on; +import static com.google.common.collect.Iterables.find; +import static com.google.common.collect.Lists.newArrayList; +import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE; +import static com.nokia.cbam.lcm.v32.model.VimInfo.VimInfoTypeEnum.*; +import static java.lang.Integer.parseInt; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.*; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions.systemFunctions; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager.*; +import static org.slf4j.LoggerFactory.getLogger; +import static org.springframework.util.StringUtils.isEmpty; + +/** + * Responsible for executing lifecycle operation on the VNF + */ +@Component +public class LifecycleManager { + public static final String ONAP_CSAR_ID = "onapCsarId"; + public static final long OPERATION_STATUS_POLLING_INTERVAL_IN_MS = 5000L; + private static Logger logger = getLogger(LifecycleManager.class); + private final CatalogManager catalogManager; + private final IGrantManager grantManager; + private final JobManager jobManager; + private final ILifecycleChangeNotificationManager notificationManager; + private final CbamRestApiProvider cbamRestApiProvider; + private final VimInfoProvider vimInfoProvider; + + /** + * Runs asynchronous operations in the background + */ + private ExecutorService executorService = Executors.newCachedThreadPool(); + + @Autowired + LifecycleManager(CatalogManager catalogManager, IGrantManager grantManager, CbamRestApiProvider restApiProvider, VimInfoProvider vimInfoProvider, JobManager jobManager, ILifecycleChangeNotificationManager notificationManager) { + this.vimInfoProvider = vimInfoProvider; + this.grantManager = grantManager; + this.cbamRestApiProvider = restApiProvider; + this.jobManager = jobManager; + this.notificationManager = notificationManager; + this.catalogManager = catalogManager; + } + + public static String getRegionName(String vimId) { + return newArrayList(on(SEPARATOR).split(vimId)).get(1); + } + + public static String getCloudOwner(String vimId) { + return newArrayList(on(SEPARATOR).split(vimId)).get(0); + } + + private static OperationExecution findLastInstantiation(List<OperationExecution> operationExecutions) { + return find(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), op -> INSTANTIATE.equals(op.getOperationType())); + } + + /** + * Instantiate (VF-C terminology) the VNF. It consists of the following steps + * <ul> + * <li>upload the VNF package to CBAM package (if not already there)</li> + * <li>create the VNF on CBAM</li> + * <li>modify attributes of the VNF (add onapCsarId field)</li> + * <li>asynchronously</li> + * <li>request grant from VF-C</li> + * <li>instantiate VNF on CBAM</li> + * <li>return VNF & job id (after create VNF on CBAM)</li> + * <li></li> + * </ul> + * The rollback of the failed operation is not implemented + * <ul> + * <li>delete the VNF if error occurs before instantiation</li> + * <li>terminate & delete VNf if error occurs after instantiation</li> + * </ul> + * + * @param vnfmId the identifier of the VNFM + * @param request the instantiation request + * @param httpResponse the HTTP response + * @return the instantiation response + */ + public VnfInstantiateResponse instantiate(String vnfmId, VnfInstantiateRequest request, HttpServletResponse httpResponse) { + logger.info("Additional parameters for instantiation: " + new Gson().toJson(request.getAdditionalParam())); + AdditionalParams additionalParams = convertInstantiationAdditionalParams(request.getVnfPackageId(), request.getAdditionalParam()); + validateVimType(additionalParams); + CatalogAdapterVnfpackage cbamPackage = catalogManager.preparePackageInCbam(vnfmId, request.getVnfPackageId()); + try { + CreateVnfRequest vnfCreateRequest = new CreateVnfRequest(); + vnfCreateRequest.setVnfdId(cbamPackage.getId()); + vnfCreateRequest.setName(request.getVnfInstanceName()); + vnfCreateRequest.setDescription(request.getVnfInstanceDescription()); + com.nokia.cbam.lcm.v32.model.VnfInfo vnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsPost(vnfCreateRequest, NOKIA_LCM_API_VERSION); + VnfInstantiateResponse response = new VnfInstantiateResponse(); + response.setVnfInstanceId(vnfInfo.getId()); + //FIXME the vimId should be send during grant response (VFC-604) + String vimId = getVimId(request.getAdditionalParam()); + addVnfdIdToVnfModifyableAttributeExtensions(vnfmId, vnfInfo.getId(), request.getVnfPackageId()); + JobInfo spawnJob = scheduleExecution(vnfInfo.getId(), httpResponse, "instantiate", (jobInfo) -> { + String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamPackage.getId()); + GrantVNFResponseVim vim = grantManager.requestGrantForInstantiate(vnfmId, vnfInfo.getId(), vimId, request.getVnfPackageId(), additionalParams.getInstantiationLevel(), vnfdContent, jobInfo.getJobId()); + if (vim.getVimId() == null) { + fatalFailure(logger, "VF-C did not send VIM identifier in grant response"); + } + VimInfo vimInfo = vimInfoProvider.getVimInfo(vim.getVimId()); + InstantiateVnfRequest instantiationRequest = new InstantiateVnfRequest(); + addExernalLinksToRequest(request.getExtVirtualLink(), additionalParams, instantiationRequest, vimId); + switch (additionalParams.getVimType()) { + case OPENSTACK_V2_INFO: + instantiationRequest.getVims().add(buildOpenStackV2INFO(vimId, vim, vimInfo)); + break; + case OPENSTACK_V3_INFO: + instantiationRequest.getVims().add(buildOpenStackV3INFO(vimId, additionalParams, vim, vimInfo)); + break; + case VMWARE_VCLOUD_INFO: + instantiationRequest.getVims().add(buildVcloudInfo(vimId, vim, vimInfo)); + break; + } + instantiationRequest.setFlavourId(getFlavorId(vnfdContent)); + instantiationRequest.setComputeResourceFlavours(additionalParams.getComputeResourceFlavours()); + instantiationRequest.setGrantlessMode(true); + instantiationRequest.setInstantiationLevelId(additionalParams.getInstantiationLevel()); + instantiationRequest.setSoftwareImages(additionalParams.getSoftwareImages()); + instantiationRequest.setZones(additionalParams.getZones()); + instantiationRequest.setExtManagedVirtualLinks(additionalParams.getExtManagedVirtualLinks()); + for (ExtVirtualLinkData extVirtualLinkData : additionalParams.getExtVirtualLinks()) { + instantiationRequest.addExtVirtualLinksItem(extVirtualLinkData); + } + JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject(); + if (additionalParams.getAdditionalParams() != null && !isEmpty(additionalParams.getAdditionalParams().toString())) { + for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(additionalParams.getAdditionalParams()).getAsJsonObject().entrySet()) { + root.add(item.getKey(), item.getValue()); + } + } + instantiationRequest.setAdditionalParams(root); + OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdInstantiatePost(vnfInfo.getId(), instantiationRequest, NOKIA_LCM_API_VERSION); + waitForOperationToFinish(vnfmId, vnfInfo.getId(), operationExecution.getId(), jobInfo.getJobId()); + }); + response.setJobId(spawnJob.getJobId()); + return response; + } catch (Exception e) { + throw fatalFailure(logger, "Unable to create the VNF", e); + } + } + + private void validateVimType(AdditionalParams additionalParams) { + switch (additionalParams.getVimType()) { + case OPENSTACK_V2_INFO: + case OPENSTACK_V3_INFO: + case VMWARE_VCLOUD_INFO: + break; + default: + throw fatalFailure(logger, "Only " + OPENSTACK_V2_INFO + ", " + OPENSTACK_V3_INFO + " and " + VMWARE_VCLOUD_INFO + " is the supported VIM types"); + } + } + + private String getVimId(Object additionalParams) { + return childElement(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "vimId").getAsString(); + } + + private AdditionalParams convertInstantiationAdditionalParams(String csarId, Object additionalParams) { + JsonObject vnfParameters = child(child(new Gson().toJsonTree(additionalParams).getAsJsonObject(), "inputs"), "vnfs"); + if (!vnfParameters.has(csarId)) { + throw fatalFailure(logger, "The additional parameter section does not contain setting for VNF with " + csarId + " CSAR id"); + } + JsonElement additionalParamsForVnf = vnfParameters.get(csarId); + return new Gson().fromJson(additionalParamsForVnf, AdditionalParams.class); + } + + private String getFlavorId(String vnfdContent) { + JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject(); + JsonObject capabilities = child(child(child(root, "topology_template"), "substitution_mappings"), "capabilities"); + JsonObject deploymentFlavorProperties = child(child(capabilities, "deployment_flavour"), "properties"); + return childElement(deploymentFlavorProperties, "flavour_id").getAsString(); + } + + private Set<String> getAcceptableOperationParameters(String vnfdContent, String categroryOfOperation, String operationName) { + JsonObject root = new Gson().toJsonTree(new Yaml().load(vnfdContent)).getAsJsonObject(); + JsonObject interfaces = child(child(child(root, "topology_template"), "substitution_mappings"), "interfaces"); + JsonObject additionalParameters = child(child(child(child(interfaces, categroryOfOperation), operationName), "inputs"), "additional_parameters"); + return additionalParameters.keySet(); + } + + private void addExernalLinksToRequest(List<ExtVirtualLinkInfo> extVirtualLinks, AdditionalParams additionalParams, InstantiateVnfRequest instantiationRequest, String vimId) { + for (ExtVirtualLinkInfo extVirtualLink : extVirtualLinks) { + ExtVirtualLinkData cbamExternalVirtualLink = new ExtVirtualLinkData(); + cbamExternalVirtualLink.setVimId(vimId); + cbamExternalVirtualLink.setResourceId(extVirtualLink.getResourceId()); + VnfExtCpData ecp = new VnfExtCpData(); + cbamExternalVirtualLink.setExtVirtualLinkId(extVirtualLink.getVlInstanceId()); + cbamExternalVirtualLink.getExtCps().add(ecp); + ecp.setCpdId(extVirtualLink.getCpdId()); + List<NetworkAddress> addresses = additionalParams.getExternalConnectionPointAddresses().get(extVirtualLink.getCpdId()); + ecp.setAddresses(addresses); + instantiationRequest.addExtVirtualLinksItem(cbamExternalVirtualLink); + } + } + + private void addVnfdIdToVnfModifyableAttributeExtensions(String vnfmId, String vnfId, String onapCsarId) { + ModifyVnfInfoRequest request = new ModifyVnfInfoRequest(); + VnfProperty onapCsarIdProperty = new VnfProperty(); + onapCsarIdProperty.setName(ONAP_CSAR_ID); + onapCsarIdProperty.setValue(onapCsarId); + request.setExtensions(new ArrayList<>()); + request.getExtensions().add(onapCsarIdProperty); + VnfProperty externalVnfmIdProperty = new VnfProperty(); + externalVnfmIdProperty.setName(EXTERNAL_VNFM_ID); + externalVnfmIdProperty.setValue(vnfmId); + request.getExtensions().add(externalVnfmIdProperty); + request.setVnfConfigurableProperties(null); + try { + OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdPatch(vnfId, request, NOKIA_LCM_API_VERSION); + waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), NOKIA_LCM_API_VERSION); + } catch (ApiException e) { + throw fatalFailure(logger, "Unable to set the " + ONAP_CSAR_ID + " property on the VNF", e); + } + } + + private OPENSTACKV3INFO buildOpenStackV3INFO(String vimId, AdditionalParams additionalParams, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) { + OPENSTACKV3INFO openstackv3INFO = new OPENSTACKV3INFO(); + openstackv3INFO.setVimInfoType(OPENSTACK_V3_INFO); + OpenStackAccessInfoV3 accessInfov3 = new OpenStackAccessInfoV3(); + openstackv3INFO.accessInfo(accessInfov3); + accessInfov3.setPassword(vimInfo.getPassword()); + accessInfov3.setDomain(additionalParams.getDomain()); + accessInfov3.setProject(vim.getAccessInfo().getTenant()); + accessInfov3.setRegion(getRegionName(vimId)); + accessInfov3.setUsername(vimInfo.getUserName()); + EndpointInfo interfaceInfoV3 = new EndpointInfo(); + interfaceInfoV3.setEndpoint(vimInfo.getUrl()); + if (!isEmpty(vimInfo.getSslInsecure())) { + interfaceInfoV3.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure())); + interfaceInfoV3.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure())); + } + if (!interfaceInfoV3.isSkipCertificateVerification()) { + interfaceInfoV3.setTrustedCertificates(new ArrayList<>()); + for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) { + interfaceInfoV3.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8)); + } + } + openstackv3INFO.setInterfaceInfo(interfaceInfoV3); + openstackv3INFO.setId(vimId); + return openstackv3INFO; + } + + private OPENSTACKV2INFO buildOpenStackV2INFO(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) { + OPENSTACKV2INFO openstackv2INFO = new OPENSTACKV2INFO(); + openstackv2INFO.setVimInfoType(OPENSTACK_V2_INFO); + OpenStackAccessInfoV2 accessInfo = new OpenStackAccessInfoV2(); + openstackv2INFO.setAccessInfo(accessInfo); + accessInfo.setPassword(vimInfo.getPassword()); + accessInfo.setTenant(vim.getAccessInfo().getTenant()); + accessInfo.setUsername(vimInfo.getUserName()); + accessInfo.setRegion(getRegionName(vimId)); + EndpointInfo interfaceEndpoint = new EndpointInfo(); + if (!isEmpty(vimInfo.getSslInsecure())) { + interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure())); + interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure())); + } + interfaceEndpoint.setEndpoint(vimInfo.getUrl()); + if (!interfaceEndpoint.isSkipCertificateVerification()) { + interfaceEndpoint.setTrustedCertificates(new ArrayList<>()); + for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) { + interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8)); + } + } + openstackv2INFO.setInterfaceInfo(interfaceEndpoint); + openstackv2INFO.setId(vimId); + return openstackv2INFO; + } + + private VMWAREVCLOUDINFO buildVcloudInfo(String vimId, GrantVNFResponseVim vim, org.onap.vnfmdriver.model.VimInfo vimInfo) { + VMWAREVCLOUDINFO vcloudInfo = new VMWAREVCLOUDINFO(); + vcloudInfo.setVimInfoType(VMWARE_VCLOUD_INFO); + VCloudAccessInfo accessInfo = new VCloudAccessInfo(); + vcloudInfo.setAccessInfo(accessInfo); + accessInfo.setPassword(vimInfo.getPassword()); + accessInfo.setUsername(vimInfo.getUserName()); + accessInfo.setOrganization(getRegionName(vimId)); + EndpointInfo interfaceEndpoint = new EndpointInfo(); + if (!isEmpty(vimInfo.getSslInsecure())) { + interfaceEndpoint.setSkipCertificateHostnameCheck(Boolean.parseBoolean(vimInfo.getSslInsecure())); + interfaceEndpoint.setSkipCertificateVerification(Boolean.parseBoolean(vimInfo.getSslInsecure())); + } + interfaceEndpoint.setEndpoint(vimInfo.getUrl()); + if (!interfaceEndpoint.isSkipCertificateVerification()) { + interfaceEndpoint.setTrustedCertificates(new ArrayList<>()); + for (String trustedCertificate : StoreLoader.getCertifacates(vimInfo.getSslCacert())) { + interfaceEndpoint.getTrustedCertificates().add(trustedCertificate.getBytes(UTF_8)); + } + } + vcloudInfo.setInterfaceInfo(interfaceEndpoint); + vcloudInfo.setId(vimId); + return vcloudInfo; + } + + /** + * Terminates and deletes the VNF + * <ul> + * <li>fails if the VNF does not exist</li> + * <li>terminates if instantiated</li> + * <li>deletes the VNF</li> + * </ul> + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param request the termination request + * @param httpResponse the HTTP response + * @return the job for polling the progress of the termination + */ + public JobInfo terminateVnf(String vnfmId, String vnfId, VnfTerminateRequest request, HttpServletResponse httpResponse) { + return scheduleExecution(vnfId, httpResponse, "terminate", (jobInfo) -> { + TerminateVnfRequest cbamRequest = new TerminateVnfRequest(); + cbamRequest.setAdditionalParams(jobInfo); + if (request.getTerminationType() == null) { + cbamRequest.setTerminationType(TerminationType.FORCEFUL); + } else { + if (request.getTerminationType().equals(VnfTerminationType.GRACEFUL)) { + cbamRequest.setTerminationType(TerminationType.GRACEFUL); + cbamRequest.setGracefulTerminationTimeout(parseInt(request.getGracefulTerminationTimeout())); + } else { + cbamRequest.setTerminationType(TerminationType.FORCEFUL); + } + } + com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + switch (vnf.getInstantiationState()) { + case INSTANTIATED: + String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf); + grantManager.requestGrantForTerminate(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), vnf, jobInfo.getJobId()); + OperationExecution terminationOperation = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdTerminatePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION); + OperationExecution finishedOperation = waitForOperationToFinish(vnfmId, vnfId, terminationOperation.getId(), jobInfo.getJobId()); + switch (finishedOperation.getStatus()) { + case FINISHED: + notificationManager.waitForTerminationToBeProcessed(finishedOperation.getId()); + logger.info("Deleting VNF with " + vnfId); + cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION); + logger.info("VNF with " + vnfId + " has been deleted"); + break; + default: + logger.error("Unable to terminate VNF the operation did not finish with success"); + } + break; + case NOT_INSTANTIATED: + cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdDelete(vnfId, NOKIA_LCM_API_VERSION); + break; + } + }); + } + + private String getVimIdFromInstantiationRequest(String vnfmId, com.nokia.cbam.lcm.v32.model.VnfInfo vnf) throws ApiException { + OperationExecution lastInstantiation = findLastInstantiation(vnf.getOperationExecutions()); + Object operationParameters = cbamRestApiProvider.getCbamOperationExecutionApi(vnfmId).operationExecutionsOperationExecutionIdOperationParamsGet(lastInstantiation.getId(), NOKIA_LCM_API_VERSION); + JsonObject root = new Gson().toJsonTree(operationParameters).getAsJsonObject(); + return childElement(childElement(root, "vims").getAsJsonArray().get(0).getAsJsonObject(), "id").getAsString(); + } + + private String getVnfdIdFromModifyableAttributes(com.nokia.cbam.lcm.v32.model.VnfInfo vnf) { + return find(vnf.getExtensions(), p -> p.getName().equals(ONAP_CSAR_ID)).getValue().toString(); + } + + /** + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @return the current state of the VNF + */ + public VnfInfo queryVnf(String vnfmId, String vnfId) { + try { + com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + VnfInfo vnfInfo = new VnfInfo(); + vnfInfo.setVersion(cbamVnfInfo.getVnfSoftwareVersion()); + vnfInfo.setVnfInstanceId(vnfId); + String onapCsarId = getVnfdIdFromModifyableAttributes(cbamVnfInfo); + vnfInfo.setVnfdId(onapCsarId); + vnfInfo.setVnfPackageId(onapCsarId); + vnfInfo.setVnfInstanceDescription(cbamVnfInfo.getDescription()); + vnfInfo.setVnfInstanceName(cbamVnfInfo.getName()); + vnfInfo.setVnfProvider(cbamVnfInfo.getVnfProvider()); + vnfInfo.setVnfStatus("ACTIVE"); + vnfInfo.setVnfType("Kuku"); + return vnfInfo; + } catch (ApiException e) { + throw fatalFailure(logger, "Unable to query VNF (" + vnfId + ")", e); + } + } + + private ScaleDirection convert(org.onap.vnfmdriver.model.ScaleDirection direction) { + if (org.onap.vnfmdriver.model.ScaleDirection.IN.equals(direction)) { + return ScaleDirection.IN; + } else { + return ScaleDirection.OUT; + } + } + + /** + * Scale the VNF + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param request the scale request + * @param httpResponse the HTTP response + * @return the job for tracking the scale + */ + public JobInfo scaleVnf(String vnfmId, String vnfId, VnfScaleRequest request, HttpServletResponse httpResponse) { + logger.info("Scale VNF " + vnfId + " " + new Gson().toJson(request)); + return scheduleExecution(vnfId, httpResponse, "scale", (jobInfo) -> { + ScaleVnfRequest cbamRequest = new ScaleVnfRequest(); + cbamRequest.setAspectId(request.getAspectId()); + cbamRequest.setNumberOfSteps(Integer.valueOf(request.getNumberOfSteps())); + cbamRequest.setType(convert(request.getType())); + com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + JsonObject root = new Gson().toJsonTree(jobInfo).getAsJsonObject(); + com.nokia.cbam.lcm.v32.model.VnfInfo cbamVnfInfo = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + String vnfdContent = catalogManager.getCbamVnfdContent(vnfmId, cbamVnfInfo.getVnfdId()); + Set<String> acceptableOperationParameters = getAcceptableOperationParameters(vnfdContent, "Basic", "scale"); + if (request.getAdditionalParam() != null) { + for (Map.Entry<String, JsonElement> item : new Gson().toJsonTree(request.getAdditionalParam()).getAsJsonObject().entrySet()) { + if (acceptableOperationParameters.contains(item.getKey())) { + root.add(item.getKey(), item.getValue()); + } + } + } + cbamRequest.setAdditionalParams(root); + grantManager.requestGrantForScale(vnfmId, vnfId, getVimIdFromInstantiationRequest(vnfmId, vnf), getVnfdIdFromModifyableAttributes(vnf), request, jobInfo.getJobId()); + OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdScalePost(vnfId, cbamRequest, NOKIA_LCM_API_VERSION); + waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), jobInfo.getJobId()); + }); + } + + /** + * Heal the VNF + * + * @param vnfmId the identifier of the VNFM + * @param vnfId the identifier of the VNF + * @param request the heal request + * @param httpResponse the HTTP response + * @return the job for tracking the heal + */ + public JobInfo healVnf(String vnfmId, String vnfId, VnfHealRequest request, HttpServletResponse httpResponse) { + return scheduleExecution(vnfId, httpResponse, "heal", (job) -> { + HealVnfRequest cbamHealRequest = new HealVnfRequest(); + Map<String, String> additionalParams = new HashMap<>(); + additionalParams.put("vmName", request.getAffectedvm().getVmname()); + additionalParams.put("action", request.getAction()); + additionalParams.put("jobId", job.getJobId()); + cbamHealRequest.setAdditionalParams(additionalParams); + com.nokia.cbam.lcm.v32.model.VnfInfo vnf = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdGet(vnfId, NOKIA_LCM_API_VERSION); + String vimId = getVimIdFromInstantiationRequest(vnfmId, vnf); + grantManager.requestGrantForHeal(vnfmId, vnfId, vimId, getVnfdIdFromModifyableAttributes(vnf), request, job.getJobId()); + OperationExecution operationExecution = cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdHealPost(vnfId, cbamHealRequest, NOKIA_LCM_API_VERSION); + waitForOperationToFinish(vnfmId, vnfId, operationExecution.getId(), job.getJobId()); + }); + } + + private JobInfo scheduleExecution(String vnfId, HttpServletResponse httpResponse, String operation, AsynchronousExecution asynchronExecution) { + JobInfo jobInfo = new JobInfo(); + jobInfo.setJobId(jobManager.spawnJob(vnfId, httpResponse)); + executorService.submit(() -> { + try { + asynchronExecution.execute(jobInfo); + } catch (RuntimeException e) { + logger.error("Unable to " + operation + " VNF with " + vnfId + " identifier", e); + jobManager.jobFinished(jobInfo.getJobId()); + throw e; + } catch (Exception e) { + String msg = "Unable to " + operation + " VNF with " + vnfId + " identifier"; + logger.error(msg, e); + //the job can only be signaled to be finished after the error is logged + jobManager.jobFinished(jobInfo.getJobId()); + throw new RuntimeException(msg, e); + } + jobManager.jobFinished(jobInfo.getJobId()); + }); + return jobInfo; + } + + private OperationExecution waitForOperationToFinish(String vnfmId, String vnfId, String operationExecutionId, String jobId) { + while (true) { + try { + OperationExecution operationExecution = find(cbamRestApiProvider.getCbamLcmApi(vnfmId).vnfsVnfInstanceIdOperationExecutionsGet(vnfId, NOKIA_LCM_API_VERSION), opEx -> operationExecutionId.equals(opEx.getId())); + switch (operationExecution.getStatus()) { + case FINISHED: + case FAILED: + logger.debug("Operation finished with " + operationExecution.getId()); + return operationExecution; + } + } catch (Exception e) { + //swallow exception and retry + logger.warn("Unable to retrieve operations details", e); + } + systemFunctions().sleep(OPERATION_STATUS_POLLING_INTERVAL_IN_MS); + } + } + + private interface AsynchronousExecution { + void execute(JobInfo job) throws Exception; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java new file mode 100644 index 00000000..142072d5 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/LifecycleChangeNotificationManager.java @@ -0,0 +1,211 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.nokia.cbam.lcm.v32.ApiException; +import com.nokia.cbam.lcm.v32.api.OperationExecutionsApi; +import com.nokia.cbam.lcm.v32.api.VnfsApi; +import com.nokia.cbam.lcm.v32.model.*; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.api.INotificationSender; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.SystemFunctions; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.DriverProperties; +import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.ILifecycleChangeNotificationManager; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.tryFind; +import static com.google.common.collect.Sets.newConcurrentHashSet; +import static com.nokia.cbam.lcm.v32.model.OperationType.INSTANTIATE; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.childElement; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.fatalFailure; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION; +import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCN_API_VERSION; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Responsible for handling lifecycle change notifications from CBAM. + * The received LCNs are transformed into ONAP LCNs. + * The following CBAM LCNs are processed: + * - HEAL + * - INSTANTIATE + * - SCALE + * - TERMINATE + * The current limitations + * - if a LCN can not be be processed due to VNF having been deleted the problem is logged and CBAM is notified that + * the LCN has been processed (even if not in reality) because the signaling of failed LCN delivery blocks the delivery + * on all LCN deliveries. The consequence of this is that the information known by VF-C / A&AI may be inconsistent with + * reality (VNF having been deleted) + */ +@Component +public class LifecycleChangeNotificationManager implements ILifecycleChangeNotificationManager { + + public static final String PROBLEM = "All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure"; + /** + * < Separates the VNF id and the resource id within a VNF + */ + private static final Set<OperationStatus> terminalStatus = Sets.newHashSet(OperationStatus.FINISHED, OperationStatus.FAILED); + private static Logger logger = getLogger(LifecycleChangeNotificationManager.class); + + private final CbamRestApiProvider restApiProvider; + private final DriverProperties driverProperties; + private final INotificationSender notificationSender; + private Set<ProcessedNotification> processedNotifications = newConcurrentHashSet(); + + @Autowired + LifecycleChangeNotificationManager(CbamRestApiProvider restApiProvider, DriverProperties driverProperties, INotificationSender notificationSender) { + this.notificationSender = notificationSender; + this.driverProperties = driverProperties; + this.restApiProvider = restApiProvider; + } + + @VisibleForTesting + static OperationExecution findLastInstantiationBefore(List<OperationExecution> operationExecutions, OperationExecution currentOperation) { + for (OperationExecution opExs : filter(NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions), (OperationExecution opex2) -> !opex2.getStartTime().isAfter(currentOperation.getStartTime()))) { + if (INSTANTIATE.equals(opExs.getOperationType()) && + (opExs.getStartTime().toLocalDate().isBefore(currentOperation.getStartTime().toLocalDate()) || + opExs.getStartTime().toLocalDate().isEqual(currentOperation.getStartTime().toLocalDate()) + )) { + return opExs; + } + } + throw new NoSuchElementException(); + } + + @Override + public void handleLcn(VnfLifecycleChangeNotification recievedNotification) { + logger.info("Received LCN: " + new Gson().toJson(recievedNotification)); + VnfsApi cbamLcmApi = restApiProvider.getCbamLcmApi(driverProperties.getVnfmId()); + try { + List<VnfInfo> vnfs = cbamLcmApi.vnfsGet(NOKIA_LCM_API_VERSION); + com.google.common.base.Optional<VnfInfo> currentVnf = tryFind(vnfs, vnf -> vnf.getId().equals(recievedNotification.getVnfInstanceId())); + if (!currentVnf.isPresent()) { + logger.warn("The VNF with " + recievedNotification.getVnfInstanceId() + " disappeared before being able to process the LCN"); + //swallow LCN + return; + } else { + VnfInfo vnf = cbamLcmApi.vnfsVnfInstanceIdGet(recievedNotification.getVnfInstanceId(), NOKIA_LCN_API_VERSION); + com.google.common.base.Optional<VnfProperty> externalVnfmId = tryFind(vnf.getExtensions(), prop -> prop.getName().equals(EXTERNAL_VNFM_ID)); + if (!externalVnfmId.isPresent()) { + logger.warn("The VNF with " + vnf.getId() + " identifier is not a managed VNF"); + return; + } + if (!externalVnfmId.get().getValue().equals(driverProperties.getVnfmId())) { + logger.warn("The VNF with " + vnf.getId() + " identifier is not a managed by the VNFM with id " + externalVnfmId.get().getValue()); + return; + } + } + } catch (Exception e) { + logger.error("Unable to list VNFs / query VNF", e); + throw new RuntimeException("Unable to list VNFs / query VNF", e); + } + OperationExecutionsApi cbamOperationExecutionApi = restApiProvider.getCbamOperationExecutionApi(driverProperties.getVnfmId()); + try { + List<OperationExecution> operationExecutions = cbamLcmApi.vnfsVnfInstanceIdOperationExecutionsGet(recievedNotification.getVnfInstanceId(), NOKIA_LCM_API_VERSION); + OperationExecution operationExecution = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdGet(recievedNotification.getLifecycleOperationOccurrenceId(), NOKIA_LCM_API_VERSION); + OperationExecution closestInstantiationToOperation = findLastInstantiationBefore(operationExecutions, operationExecution); + String vimId; + try { + Object operationParams = cbamOperationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(closestInstantiationToOperation.getId(), NOKIA_LCM_API_VERSION); + vimId = getVimId(operationParams); + } catch (Exception e) { + logger.error("Unable to detect last instantiation operation", e); + throw new RuntimeException("Unable to detect last instantiation operation", e); + } + notificationSender.processNotification(recievedNotification, operationExecution, buildAffectedCps(operationExecution), vimId); + if (OperationType.TERMINATE.equals(recievedNotification.getOperation()) && terminalStatus.contains(recievedNotification.getStatus())) { + processedNotifications.add(new ProcessedNotification(recievedNotification.getLifecycleOperationOccurrenceId(), recievedNotification.getStatus())); + } + } catch (ApiException e) { + logger.error("Unable to retrieve the current VNF " + recievedNotification.getVnfInstanceId(), e); + throw new RuntimeException("Unable to retrieve the current VNF " + recievedNotification.getVnfInstanceId(), e); + } + } + + @Override + public void waitForTerminationToBeProcessed(String operationExecutionId) { + while (true) { + com.google.common.base.Optional<ProcessedNotification> notification = Iterables.tryFind(processedNotifications, processedNotification -> processedNotification.getOperationExecutionId().equals(operationExecutionId)); + if (notification.isPresent()) { + processedNotifications.remove(notification.get()); + return; + } + SystemFunctions.systemFunctions().sleep(500); + } + } + + private String getVimId(Object instantiationParameters) { + InstantiateVnfRequest request = new Gson().fromJson(new Gson().toJson(instantiationParameters), InstantiateVnfRequest.class); + return request.getVims().get(0).getId(); + } + + private ReportedAffectedConnectionPoints buildAffectedCps(OperationExecution operationExecution) { + switch (operationExecution.getOperationType()) { + case TERMINATE: + String terminationType = childElement(new Gson().toJsonTree(operationExecution.getOperationParams()).getAsJsonObject(), "terminationType").getAsString(); + if (TerminationType.FORCEFUL.name().equals(terminationType)) { + //in case of force full termination the Ansible is not executed, so the connection points can not be + //calculated from operation execution result + logger.warn("Unable to send information related to affected connection points during forceful termination"); + return null; + } + } + try { + JsonElement root = new Gson().toJsonTree(operationExecution.getAdditionalData()); + if (root.getAsJsonObject().has("operationResult")) { + JsonObject operationResult = root.getAsJsonObject().get("operationResult").getAsJsonObject(); + if (!isPresent(operationResult, "cbam_pre") || !isPresent(operationResult, "cbam_post")) { + handleFailure(operationExecution, null); + } + return new Gson().fromJson(operationResult, ReportedAffectedConnectionPoints.class); + } + } catch (Exception e) { + handleFailure(operationExecution, e); + } + return new ReportedAffectedConnectionPoints(); + } + + private boolean isPresent(JsonObject operationResult, String key) { + return operationResult.has(key) && operationResult.get(key).isJsonArray(); + } + + private void handleFailure(OperationExecution operationExecution, Exception e) { + switch (operationExecution.getStatus()) { + case FAILED: + case OTHER: + logger.warn("The operation failed and the affected connection points were not reported"); + break; + case STARTED: //can not happen (the changed resources are only executed for terminal state + case FINISHED: + if (e != null) { + fatalFailure(logger, PROBLEM, e); + } + fatalFailure(logger, PROBLEM); + } + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ProcessedNotification.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ProcessedNotification.java new file mode 100644 index 00000000..044e1ca2 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ProcessedNotification.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification; + +import com.nokia.cbam.lcm.v32.model.OperationStatus; + +import java.util.Objects; + +/** + * Represents a notification successfully processed by the {@link LifecycleChangeNotificationManager} + */ +class ProcessedNotification { + private String operationExecutionId; + //do not remove field the {@link LifecycleChangeNotificationManager} uses the equals + // method to compare notifications + private OperationStatus status; + + ProcessedNotification(String operationExecutionId, OperationStatus status) { + this.operationExecutionId = operationExecutionId; + this.status = status; + } + + /** + * @return the identifier of the operation + */ + public String getOperationExecutionId() { + return operationExecutionId; + } + + /** + * @param operationExecutionId the identifier of the operation + */ + public void setOperationExecutionId(String operationExecutionId) { + this.operationExecutionId = operationExecutionId; + } + + /** + * @return the status of the operation + */ + public OperationStatus getStatus() { + return status; + } + + /** + * @param status the status of the operation + */ + public void setStatus(OperationStatus status) { + this.status = status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ProcessedNotification that = (ProcessedNotification) o; + return Objects.equals(operationExecutionId, that.operationExecutionId) && + status == that.status; + } + + @Override + public int hashCode() { + return Objects.hash(operationExecutionId, status); + } + + @Override + public String toString() { + return "ProcessedNotification{" + + "operationExecutionId=" + operationExecutionId + '"' + + ", status=" + status + + '}'; + } +} diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedConnectionPoints.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedConnectionPoints.java new file mode 100644 index 00000000..9bf629e3 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedConnectionPoints.java @@ -0,0 +1,83 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification; + +import com.google.gson.annotations.SerializedName; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * The connection points reported by each operation + */ +public class ReportedAffectedConnectionPoints { + @SerializedName("cbam_pre") + private Set<ReportedAffectedCp> pre = new HashSet<>(); + @SerializedName("cbam_post") + private Set<ReportedAffectedCp> post = new HashSet<>(); + + /** + * @return the connection points that were present after the operation has finished + */ + public Set<ReportedAffectedCp> getPost() { + return post; + } + + /** + * @param post the connection points that were present after the operation has finished + */ + public void setPost(Set<ReportedAffectedCp> post) { + this.post = post; + } + + /** + * @return the connection points that were present before the operation was started + */ + public Set<ReportedAffectedCp> getPre() { + return pre; + } + + /** + * @param pre the connection points that were present before the operation was started + */ + public void setPre(Set<ReportedAffectedCp> pre) { + this.pre = pre; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ReportedAffectedConnectionPoints that = (ReportedAffectedConnectionPoints) o; + return Objects.equals(pre, that.pre) && + Objects.equals(post, that.post); + } + + @Override + public int hashCode() { + + return Objects.hash(pre, post); + } + + @Override + public String toString() { + return "ReportedAffectedConnectionPoints{" + + "pre=" + pre + + ", post=" + post + + '}'; + } +}
\ No newline at end of file diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedCp.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedCp.java new file mode 100644 index 00000000..ab9d6314 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/notification/ReportedAffectedCp.java @@ -0,0 +1,231 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification; + +import com.nokia.cbam.lcm.v32.model.ChangeType; + +import java.util.Objects; + +/** + * Represent a single port change in the VNF + */ +public class ReportedAffectedCp { + private String providerId; + private String cpdId; + private String ecpdId; + private String cpId; //the location of the resource in the Heat stack + private String tenantId; + private String ipAddress; + private String macAddress; + private String serverProviderId; + private String name; + private String networkProviderId; + private ChangeType changeType; + + /** + * @return the provider id of the port + */ + public String getProviderId() { + return providerId; + } + + /** + * @param providerId the provider id of the port + */ + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + /** + * @return the identifier of the connection point of the port (may be null) + */ + public String getCpdId() { + return cpdId; + } + + /** + * @param cpdId the identifier of the connection point of the port (may be null) + */ + public void setCpdId(String cpdId) { + this.cpdId = cpdId; + } + + /** + * @return the identifier of the external connection point of the port (may be null) + */ + public String getEcpdId() { + return ecpdId; + } + + /** + * @param ecpdId the identifier of the connection point of the port (may be null) + */ + public void setEcpdId(String ecpdId) { + this.ecpdId = ecpdId; + } + + /** + * @return the identifier of the connection point instance of the port + */ + public String getCpId() { + return cpId; + } + + /** + * @param cpId the identifier of the connection point instance of the port + */ + public void setCpId(String cpId) { + this.cpId = cpId; + } + + /** + * @return the identifier of the tenant owning the port + */ + public String getTenantId() { + return tenantId; + } + + /** + * @param tenantId the identifier of the tenant owning the port + */ + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + /** + * @return the IP address of the port (may be null) + */ + public String getIpAddress() { + return ipAddress; + } + + /** + * @param ipAddress the IP address of the port (may be null) + */ + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + /** + * @return the MAC address of the port + */ + public String getMacAddress() { + return macAddress; + } + + /** + * @param macAddress the MAC address of the port + */ + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + /** + * @return the provider id of the server to which the port is attached to (may be null) + */ + public String getServerProviderId() { + return serverProviderId; + } + + /** + * @param serverProviderId the provider id of the server to which the port is attached to (may be null) + */ + public void setServerProviderId(String serverProviderId) { + this.serverProviderId = serverProviderId; + } + + /** + * @return the name of the port (may be null) + */ + public String getName() { + return name; + } + + /** + * @param name the name of the port + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the provider id of the network of the port + */ + public String getNetworkProviderId() { + return networkProviderId; + } + + /** + * @param networkProviderId the provider id of the network of the port + */ + public void setNetworkProviderId(String networkProviderId) { + this.networkProviderId = networkProviderId; + } + + /** + * @return the state of the port in the current operation + */ + public ChangeType getChangeType() { + return changeType; + } + + /** + * @param changeType the state of the port in the current operation + */ + public void setChangeType(ChangeType changeType) { + this.changeType = changeType; + } + + @Override + public String toString() { + return "ReportedAffectedCp{" + + "providerId='" + providerId + '\'' + + ", cpdId='" + cpdId + '\'' + + ", ecpdId='" + ecpdId + '\'' + + ", cpId='" + cpId + '\'' + + ", tenantId='" + tenantId + '\'' + + ", ipAddress='" + ipAddress + '\'' + + ", macAddress='" + macAddress + '\'' + + ", serverProviderId='" + serverProviderId + '\'' + + ", name='" + name + '\'' + + ", networkProviderId='" + networkProviderId + '\'' + + ", changeType=" + changeType + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ReportedAffectedCp that = (ReportedAffectedCp) o; + return Objects.equals(providerId, that.providerId) && + Objects.equals(cpdId, that.cpdId) && + Objects.equals(ecpdId, that.ecpdId) && + Objects.equals(cpId, that.cpId) && + Objects.equals(tenantId, that.tenantId) && + Objects.equals(ipAddress, that.ipAddress) && + Objects.equals(macAddress, that.macAddress) && + Objects.equals(serverProviderId, that.serverProviderId) && + Objects.equals(name, that.name) && + Objects.equals(networkProviderId, that.networkProviderId) && + changeType == that.changeType; + } + + @Override + public int hashCode() { + return Objects.hash(providerId, cpdId, ecpdId, cpId, tenantId, ipAddress, macAddress, serverProviderId, name, networkProviderId, changeType); + } +}
\ No newline at end of file diff --git a/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/package-info.java b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/package-info.java new file mode 100644 index 00000000..eb4ca242 --- /dev/null +++ b/nokiav2/driver/src/main/java/org/onap/vfc/nfvo/driver/vnfm/svnfm/nokia/vnfm/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2017, Nokia Corporation + * + * 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. + */ + +/** + * Handles the interactions with the VNFM. + */ +package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm; |