summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pom.xml15
-rw-r--r--src/main/resources/clds/camel/rest/clamp-api-v2.xml18
-rw-r--r--src/main/resources/clds/camel/routes/dcae-flows.xml8
-rw-r--r--src/test/java/org/onap/clamp/clds/it/RobotItCase.java114
-rw-r--r--src/test/java/org/onap/clamp/loop/DeployFlowTestItCase.java3
-rw-r--r--src/test/resources/robotframework/Dockerfile9
-rw-r--r--src/test/resources/robotframework/requirements.txt6
-rw-r--r--src/test/resources/robotframework/robotframework-test.properties173
-rw-r--r--src/test/resources/robotframework/tests/01_healthcheck.robot19
-rw-r--r--ui-react/src/LoopUI.js155
-rw-r--r--ui-react/src/__snapshots__/LoopUI.test.js.snap4
-rw-r--r--ui-react/src/__snapshots__/OnapClamp.test.js.snap4
-rw-r--r--ui-react/src/components/dialogs/PerformActions.js52
-rw-r--r--ui-react/src/components/dialogs/PerformActions.test.js8
-rw-r--r--ui-react/src/components/loop_viewer/svg/SvgGenerator.js16
15 files changed, 548 insertions, 56 deletions
diff --git a/pom.xml b/pom.xml
index e8b4ba00..245bae02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -547,6 +547,18 @@
<version>2.0.4</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.github.docker-java</groupId>
+ <artifactId>docker-java-core</artifactId>
+ <version>3.2.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.github.docker-java</groupId>
+ <artifactId>docker-java</artifactId>
+ <version>3.2.1</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -850,6 +862,7 @@
<portName>docker.mariadb.port.host</portName>
<portName>docker.http-cache.port.host</portName>
<portName>clamp.it.tests.http-redirected</portName>
+ <portName>clamp.it.tests.robotframework.http</portName>
<portName>clamp.it.tests.https</portName>
<portName>clamp.it.tests.http</portName>
</portNames>
@@ -902,7 +915,7 @@
<include>**/*ItCase.java</include>
</includes>
<forkCount>1C</forkCount>
- <reuseForks>true</reuseForks>
+ <reuseForks>false</reuseForks>
<useSystemClassLoader>false</useSystemClassLoader>
<argLine>${failsafeArgLine}</argLine>
</configuration>
diff --git a/src/main/resources/clds/camel/rest/clamp-api-v2.xml b/src/main/resources/clds/camel/rest/clamp-api-v2.xml
index 99e92f5b..505ea30d 100644
--- a/src/main/resources/clds/camel/rest/clamp-api-v2.xml
+++ b/src/main/resources/clds/camel/rest/clamp-api-v2.xml
@@ -200,6 +200,9 @@
<doTry>
<log loggingLevel="INFO"
message="DCAE DEPLOY request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*, 'DCAE DEPLOY request')" />
<to
@@ -330,6 +333,9 @@
<doTry>
<log loggingLevel="INFO"
message="DCAE UNDEPLOY request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*, 'DCAE UNDEPLOY request')" />
<to
@@ -373,6 +379,9 @@
<doTry>
<log loggingLevel="INFO"
message="STOP request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*,'STOP request')" />
<to
@@ -416,6 +425,9 @@
<doTry>
<log loggingLevel="INFO"
message="RESTART request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*,'RESTART request')" />
<to
@@ -460,6 +472,9 @@
<doTry>
<log loggingLevel="INFO"
message="POLICY SUBMIT request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*, 'POLICY SUBMIT request')" />
<to
@@ -544,6 +559,9 @@
<doTry>
<log loggingLevel="INFO"
message="DELETE request for loop: ${header.loopName}" />
+ <setProperty propertyName="raiseHttpExceptionFlag">
+ <simple resultType="java.lang.Boolean">true</simple>
+ </setProperty>
<to
uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*,'DELETE request')" />
<to
diff --git a/src/main/resources/clds/camel/routes/dcae-flows.xml b/src/main/resources/clds/camel/routes/dcae-flows.xml
index e36f28ab..d71db176 100644
--- a/src/main/resources/clds/camel/routes/dcae-flows.xml
+++ b/src/main/resources/clds/camel/routes/dcae-flows.xml
@@ -239,7 +239,7 @@
method="getStatusUrl(${exchangeProperty[dcaeResponse]})" />
</setProperty>
<to
- uri="bean:org.onap.clamp.policy.microservice.MicroServicePolicyService?method=updateDcaeDeploymentFields(${exchangeProperty[microServicePolicy]},${exchangeProperty[microServicePolicy].getDcaeDeploymentId()},${exchangeProperty[dcaeStatusUrl]})" />
+ uri="bean:org.onap.clamp.policy.microservice.MicroServicePolicyService?method=updateDcaeDeploymentFields(${exchangeProperty[microServicePolicy]},null,${exchangeProperty[dcaeStatusUrl]})" />
<to
uri="bean:org.onap.clamp.loop.log.LoopLogService?method=addLogForComponent('Undeploy for the micro service: ${exchangeProperty[microServicePolicy].getName()} - ${header.CamelHttpResponseCode} : ${header.CamelHttpResponseText}','INFO','DCAE',${exchangeProperty[loopObject]})" />
</when>
@@ -322,7 +322,7 @@
method="getStatusUrl(${exchangeProperty[dcaeResponse]})" />
</setProperty>
<to
- uri="bean:org.onap.clamp.loop.LoopService?method=updateDcaeDeploymentFields(${exchangeProperty[loopObject]},${exchangeProperty[loopObject].getDcaeDeploymentId()},${exchangeProperty[dcaeStatusUrl]})" />
+ uri="bean:org.onap.clamp.loop.LoopService?method=updateDcaeDeploymentFields(${exchangeProperty[loopObject]},null,${exchangeProperty[dcaeStatusUrl]})" />
<doFinally>
<to uri="direct:reset-raise-http-exception-flag" />
<to
@@ -378,9 +378,9 @@
</handled>
<log loggingLevel="ERROR"
- message="GET policy request FAILED for loop: ${header.loopName}, ${exception.stacktrace}" />
+ message="GET DCAE deployment request FAILED for loop: ${header.loopName}, ${exception.stacktrace}" />
<to
- uri="bean:org.onap.clamp.loop.log.LoopLogService?method=addLog('GET policy request failed, Error reported: ${exception.message}','ERROR',${exchangeProperty[loopObject]})" />
+ uri="bean:org.onap.clamp.loop.log.LoopLogService?method=addLog('GET DCAE deployment request failed, Error reported: ${exception.message}','ERROR',${exchangeProperty[loopObject]})" />
</doCatch>
<doFinally>
<to uri="direct:reset-raise-http-exception-flag" />
diff --git a/src/test/java/org/onap/clamp/clds/it/RobotItCase.java b/src/test/java/org/onap/clamp/clds/it/RobotItCase.java
new file mode 100644
index 00000000..b386d9bb
--- /dev/null
+++ b/src/test/java/org/onap/clamp/clds/it/RobotItCase.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.clds.it;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.github.dockerjava.api.DockerClient;
+import com.github.dockerjava.api.command.BuildImageResultCallback;
+import com.github.dockerjava.api.command.CreateContainerResponse;
+import com.github.dockerjava.api.command.InspectContainerResponse;
+import com.github.dockerjava.api.command.LogContainerCmd;
+import com.github.dockerjava.api.model.AccessMode;
+import com.github.dockerjava.api.model.Bind;
+import com.github.dockerjava.api.model.BuildResponseItem;
+import com.github.dockerjava.api.model.Frame;
+import com.github.dockerjava.api.model.Volume;
+import com.github.dockerjava.core.DockerClientBuilder;
+import com.github.dockerjava.core.command.LogContainerResultCallback;
+import com.github.dockerjava.netty.NettyDockerCmdExecFactory;
+import java.io.File;
+import java.util.Objects;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@TestPropertySource(locations = "classpath:robotframework/robotframework-test.properties")
+public class RobotItCase {
+
+ @Value("${server.port}")
+ private String httpPort;
+ private static final int TIMEOUT_S = 150;
+ protected static final EELFLogger logger = EELFManager.getInstance().getLogger(RobotItCase.class);
+
+ @Test
+ public void robotTests() throws Exception {
+ File robotFolder = new File(getClass().getClassLoader().getResource("robotframework").getFile());
+ Volume testsVolume = new Volume("/opt/robotframework/tests");
+ DockerClient client = DockerClientBuilder
+ .getInstance()
+ .withDockerCmdExecFactory(new NettyDockerCmdExecFactory())
+ .build();
+
+
+ BuildImageResultCallback callback = new BuildImageResultCallback() {
+ @Override
+ public void onNext(BuildResponseItem item) {
+ System.out.println("XXX ITEM " + item);
+ super.onNext(item);
+ }
+ };
+
+ String imageId = client.buildImageCmd(robotFolder).exec(callback).awaitImageId();
+ CreateContainerResponse createContainerResponse = client.createContainerCmd(imageId)
+ .withVolumes(testsVolume)
+ .withBinds(
+ new Bind(robotFolder.getAbsolutePath() + "/tests/", testsVolume, AccessMode.rw))
+ .withEnv("CLAMP_PORT=" + httpPort)
+ .withStopTimeout(TIMEOUT_S)
+ .withNetworkMode("host")
+ .exec();
+ String id = createContainerResponse.getId();
+ client.startContainerCmd(id).exec();
+ InspectContainerResponse exec;
+
+ int tries = 0;
+ do {
+ Thread.sleep(1000);
+ exec = client.inspectContainerCmd(id).exec();
+ tries++;
+ } while (exec.getState().getRunning() && tries < TIMEOUT_S);
+ Assert.assertEquals(exec.getState().getError(), 0L,
+ Objects.requireNonNull(exec.getState().getExitCodeLong()).longValue());
+ LogContainerCmd logContainerCmd = client.logContainerCmd(id);
+ logContainerCmd.withStdOut(true).withStdErr(true);
+ try {
+ logContainerCmd.exec(new LogContainerResultCallback() {
+ @Override
+ public void onNext(Frame item) {
+ logger.info(item.toString());
+ }
+ }).awaitCompletion();
+ } catch (InterruptedException e) {
+ throw new Exception("Failed to retrieve logs of container " + id, e);
+ }
+ client.stopContainerCmd(id);
+ }
+}
diff --git a/src/test/java/org/onap/clamp/loop/DeployFlowTestItCase.java b/src/test/java/org/onap/clamp/loop/DeployFlowTestItCase.java
index 07e7c4d7..169db9db 100644
--- a/src/test/java/org/onap/clamp/loop/DeployFlowTestItCase.java
+++ b/src/test/java/org/onap/clamp/loop/DeployFlowTestItCase.java
@@ -160,6 +160,7 @@ public class DeployFlowTestItCase {
Loop loopAfterTest = loopService.getLoop("ControlLoopTest");
assertThat(loopAfterTest.getDcaeDeploymentStatusUrl().contains("/uninstall")).isTrue();
+ assertThat(loopAfterTest.getDcaeDeploymentId()).isNull();
}
/**
@@ -196,6 +197,8 @@ public class DeployFlowTestItCase {
Set<MicroServicePolicy> policyList = loopAfterTest.getMicroServicePolicies();
for (MicroServicePolicy policy : policyList) {
assertThat(policy.getDcaeDeploymentStatusUrl().contains("/uninstall")).isTrue();
+ assertThat(policy.getDcaeDeploymentId()).isNull();
+
}
assertThat(loopAfterTest.getDcaeDeploymentStatusUrl()).isNull();
assertThat(loopAfterTest.getDcaeDeploymentId()).isNull();
diff --git a/src/test/resources/robotframework/Dockerfile b/src/test/resources/robotframework/Dockerfile
new file mode 100644
index 00000000..4ae08208
--- /dev/null
+++ b/src/test/resources/robotframework/Dockerfile
@@ -0,0 +1,9 @@
+#FROM robotframework/rfdocker
+#
+#### Uncomment following two lines if having external test libraries:
+##COPY --chown=robot:robot requirements.txt .
+#RUN pip3 install --no-cache-dir -r requirements.txt
+#COPY *.robot /home/robot/atest
+FROM ppodgorsek/robot-framework:3.0.3
+COPY requirements.txt .
+RUN pip install -r requirements.txt \ No newline at end of file
diff --git a/src/test/resources/robotframework/requirements.txt b/src/test/resources/robotframework/requirements.txt
new file mode 100644
index 00000000..2ae8f450
--- /dev/null
+++ b/src/test/resources/robotframework/requirements.txt
@@ -0,0 +1,6 @@
+certifi
+chardet
+idna
+requests
+urllib3
+robotframework-extendedrequestslibrary
diff --git a/src/test/resources/robotframework/robotframework-test.properties b/src/test/resources/robotframework/robotframework-test.properties
new file mode 100644
index 00000000..4ec65735
--- /dev/null
+++ b/src/test/resources/robotframework/robotframework-test.properties
@@ -0,0 +1,173 @@
+###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 2017-2018 AT&T Intellectual Property. All rights
+# reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+# ===================================================================
+#
+###
+
+### Set the port for HTTP or HTTPS protocol (Controlled by Spring framework, only one at a time).
+### (See below for the parameter 'server.http.port' if you want to have both enabled)
+### To have only HTTP, keep the lines server.ssl.* commented
+### To have only HTTPS enabled, uncomment the server.ssl.* lines and specify a right keystore location
+server.port=${clamp.it.tests.robotframework.http}
+### Settings for HTTPS (this automatically enables the HTTPS on the port 'server.port')
+#server.ssl.key-store=file:/tmp/mykey.jks
+#server.ssl.key-store-password=pass
+#server.ssl.key-password=pass
+
+### In order to be user friendly when HTTPS is enabled,
+### you can add another HTTP port that will be automatically redirected to HTTPS
+### by enabling this parameter (server.http.port) and set it to another port (80 or 8080, 8090, etc ...)
+#server.http-to-https-redirection.port=8090
+
+### HTTP Example:
+###--------------
+### server.port=8080
+
+### HTTPS Example:
+### --------------
+### server.port=8443
+### server.ssl.key-store=file:/tmp/mykey.jks
+### server.ssl.key-store-password=mypass
+### server.ssl.key-password=mypass
+
+### HTTP (Redirected to HTTPS) and HTTPS Example:
+### --------------------------------------------
+### server.port=8443 <-- The HTTPS port
+### server.ssl.key-store=file:/tmp/mykey.jks
+### server.ssl.key-store-password=mypass
+### server.ssl.key-password=mypass
+### server.http-to-https-redirection.port=8090 <-- The HTTP port
+
+server.servlet.context-path=/
+#Modified engine-rest applicationpath
+spring.profiles.active=clamp-default,clamp-default-user
+spring.http.converters.preferred-json-mapper=gson
+
+#The max number of active threads in this pool
+server.tomcat.max-threads=200
+#The minimum number of threads always kept alive
+server.tomcat.min-Spare-Threads=25
+#The number of milliseconds before an idle thread shutsdown, unless the number of active threads are less or equal to minSpareThreads
+server.tomcat.max-idle-time=60000
+
+#Servlet context parameters
+server.context_parameters.p-name=value #context parameter with p-name as key and value as value.
+
+camel.springboot.consumer-template-cache-size=1000
+camel.springboot.producer-template-cache-size=1000
+# JMX enabled to have Camel Swagger runtime working
+camel.springboot.jmx-enabled=true
+camel.defaultthreadpool.poolsize=10
+camel.defaultthreadpool.maxpoolsize=20
+camel.defaultthreadpool.maxqueuesize=1000
+camel.defaultthreadpool.keepaliveTime=60
+camel.defaultthreadpool.rejectpolicy=CallerRuns
+#camel.springboot.xmlRoutes = false
+camel.springboot.xmlRoutes=classpath:/clds/camel/routes/*.xml
+camel.springboot.xmlRests=classpath:/clds/camel/rest/*.xml
+#camel.springboot.typeConversion = false
+
+#clds datasource connection details
+spring.datasource.driverClassName=org.mariadb.jdbc.Driver
+spring.datasource.url=jdbc:mariadb:sequential://localhost:3306,localhost:${docker.mariadb.port.host}/cldsdb4?autoReconnect=true&connectTimeout=10000&socketTimeout=10000&retriesAllDown=3
+spring.datasource.username=clds
+spring.datasource.password=sidnnd83K
+spring.datasource.validationQuery=SELECT 1
+spring.datasource.validationQueryTimeout=20000
+spring.datasource.validationInterval=30000
+spring.datasource.testWhileIdle = true
+spring.datasource.minIdle = 0
+spring.datasource.initialSize=0
+# Automatically test whether a connection provided is good or not
+spring.datasource.testOnBorrow=true
+spring.datasource.ignoreExceptionOnPreLoad=true
+
+spring.jpa.properties.javax.persistence.schema-generation.database.action=none
+#spring.jpa.properties.javax.persistence.schema-generation.create-source=metadata
+#spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
+#spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql
+# disable Hibernate DDL generation because the schema will be generated from a sql script
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
+spring.jpa.properties.hibernate.ddl-auto=validate
+spring.jpa.properties.hibernate.hbm2ddl.delimiter=;
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.use-new-id-generator-mappings=true
+
+# Whether to enable logging of SQL statements.
+#spring.jpa.show-sql=true
+
+#Async Executor default Parameters
+async.core.pool.size=10
+async.max.pool.size=20
+async.queue.capacity=500
+
+#For EELF logback file
+#com.att.eelf.logging.path=
+clamp.config.logback.filename=logback-default.xml
+#The log folder that will be used in logback.xml file
+clamp.config.log.path=log
+clamp.config.files.systemProperties=classpath:/system.properties
+clamp.config.files.cldsUsers=classpath:/clds/clds-users.json
+clamp.config.files.globalProperties=classpath:/clds/templates/globalProperties.json
+clamp.config.files.sdcController=classpath:/clds/sdc-controllers-config.json
+
+#
+# Configuration Settings for Policy Engine Components
+clamp.config.policy.api.url=http4://localhost:${docker.http-cache.port.host}
+clamp.config.policy.api.userName=healthcheck
+clamp.config.policy.api.password=zb!XztG34
+clamp.config.policy.pap.url=http4://localhost:${docker.http-cache.port.host}
+clamp.config.policy.pap.userName=healthcheck
+clamp.config.policy.pap.password=zb!XztG34
+
+# Sdc service properties
+#
+clamp.config.sdc.csarFolder = ${project.build.directory}/sdc-tests
+
+#DCAE Inventory Url Properties
+clamp.config.dcae.inventory.url=http4://localhost:${docker.http-cache.port.host}
+clamp.config.dcae.intentory.retry.interval=100
+clamp.config.dcae.intentory.retry.limit=1
+
+#DCAE Deployment Url Properties
+clamp.config.dcae.deployment.url=http4://localhost:${docker.http-cache.port.host}
+clamp.config.dcae.deployment.userName=test
+clamp.config.dcae.deployment.password=test
+
+#Define user permission related parameters, the permission type can be changed but MUST be redefined in clds-users.properties in that case !
+clamp.config.security.permission.type.cl=permission-type-cl
+clamp.config.security.permission.type.cl.manage=permission-type-cl-manage
+clamp.config.security.permission.type.cl.event=permission-type-cl-event
+clamp.config.security.permission.type.filter.vf=permission-type-filter-vf
+clamp.config.security.permission.type.template=permission-type-template
+clamp.config.security.permission.type.tosca=permission-type-tosca
+#This one indicates the type of instances (dev|prod|perf...), this must be set accordingly in clds-users.properties
+clamp.config.security.permission.instance=dev
+clamp.config.security.authentication.class=org.onap.aaf.cadi.principal.X509Principal
+
+# Configuration settings for CDS
+clamp.config.cds.url=http4://localhost:${docker.http-cache.port.host}
+clamp.config.cds.userName=ccsdkapps
+clamp.config.cds.password=ccsdkapps
+
+## Tosca converter
+clamp.config.tosca.converter.json.schema.templates=classpath:/clds/tosca-converter/templates.json
+clamp.config.tosca.converter.default.datatypes=classpath:/clds/tosca-converter/default-tosca-types.yaml
+clamp.config.tosca.converter.dictionary.support.enabled=true \ No newline at end of file
diff --git a/src/test/resources/robotframework/tests/01_healthcheck.robot b/src/test/resources/robotframework/tests/01_healthcheck.robot
new file mode 100644
index 00000000..f1926678
--- /dev/null
+++ b/src/test/resources/robotframework/tests/01_healthcheck.robot
@@ -0,0 +1,19 @@
+*** Settings ***
+Library Collections
+Library RequestsLibrary
+Library OperatingSystem
+Library json
+Library OperatingSystem
+*** Variables ***
+${login} admin
+${passw} password
+*** Keywords ***
+Create the sessions
+*** Test Cases ***
+Get Requests health check ok
+ ${port} = Get Environment Variable CLAMP_PORT
+ ${auth}= Create List ${login} ${passw}
+ Create Session clamp http://localhost:${port} auth=${auth} disable_warnings=1
+ Set Global Variable ${clamp_session} clamp
+ ${resp}= Get Request ${clamp_session} /restservices/clds/v1/healthcheck
+ Should Be Equal As Strings ${resp.status_code} 200 \ No newline at end of file
diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js
index 8624726b..0ee6e6e2 100644
--- a/ui-react/src/LoopUI.js
+++ b/ui-react/src/LoopUI.js
@@ -52,6 +52,7 @@ import PerformAction from './components/dialogs/PerformActions';
import RefreshStatus from './components/dialogs/RefreshStatus';
import DeployLoopModal from './components/dialogs/Loop/DeployLoopModal';
import Alert from 'react-bootstrap/Alert';
+import Spinner from 'react-bootstrap/Spinner';
import { Link } from 'react-router-dom';
@@ -59,6 +60,11 @@ const StyledMainDiv = styled.div`
background-color: ${props => props.theme.backgroundColor};
`
+const StyledSpinnerDiv = styled.div`
+ justify-content: center !important;
+ display: flex !important;
+`;
+
const ProjectNameStyled = styled.a`
vertical-align: middle;
padding-left: 30px;
@@ -108,7 +114,8 @@ export default class LoopUI extends React.Component {
loopName: OnapConstants.defaultLoopName,
loopCache: new LoopCache({}),
showSucAlert: false,
- showFailAlert: false
+ showFailAlert: false,
+ busyLoadingCount: 0
};
constructor() {
@@ -120,6 +127,9 @@ export default class LoopUI extends React.Component {
this.showSucAlert = this.showSucAlert.bind(this);
this.showFailAlert = this.showFailAlert.bind(this);
this.disableAlert = this.disableAlert.bind(this);
+ this.setBusyLoading = this.setBusyLoading.bind(this);
+ this.clearBusyLoading = this.clearBusyLoading.bind(this);
+ this.isBusyLoading = this.isBusyLoading.bind(this);
}
componentWillMount() {
@@ -191,7 +201,7 @@ export default class LoopUI extends React.Component {
renderLoopViewBody() {
return (
<LoopViewBodyDivStyled>
- <SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE}/>
+ <SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE} isBusyLoading={this.isBusyLoading}/>
<LoopStatus loopCache={this.state.loopCache}/>
<LoopLogs loopCache={this.state.loopCache} />
</LoopViewBodyDivStyled>
@@ -225,53 +235,160 @@ export default class LoopUI extends React.Component {
showFailAlert(message) {
this.setState ({ showFailAlert: true, showMessage:message });
}
-
+
disableAlert() {
this.setState ({ showSucAlert: false, showFailAlert: false });
}
loadLoop(loopName) {
+ this.setBusyLoading();
LoopService.getLoop(loopName).then(loop => {
console.debug("Updating loopCache");
LoopActionService.refreshStatus(loopName).then(data => {
this.updateLoopCache(data);
+ this.clearBusyLoading();
this.props.history.push('/');
})
.catch(error => {
this.updateLoopCache(loop);
+ this.clearBusyLoading();
this.props.history.push('/');
});
});
}
+ setBusyLoading() {
+ this.setState((state,props) => ({ busyLoadingCount: ++state.busyLoadingCount }));
+ }
+
+ clearBusyLoading() {
+ this.setState((state,props) => ({ busyLoadingCount: --state.busyLoadingCount }));
+ }
+
+ isBusyLoading() {
+ if (this.state.busyLoadingCount === 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
closeLoop() {
this.setState({ loopCache: new LoopCache({}), loopName: OnapConstants.defaultLoopName });
this.props.history.push('/');
}
- render() {
- return (
- <StyledMainDiv id="main_div">
+ renderRoutes() {
+ return(
+ <React.Fragment>
<Route path="/uploadToscaPolicyModal" render={(routeProps) => (<UploadToscaPolicyModal {...routeProps} />)} />
<Route path="/viewToscaPolicyModal" render={(routeProps) => (<ViewToscaPolicyModal {...routeProps} />)} />
<Route path="/ViewLoopTemplatesModal" render={(routeProps) => (<ViewLoopTemplatesModal {...routeProps} />)} />
<Route path="/ManageDictionaries" render={(routeProps) => (<ManageDictionaries {...routeProps} />)} />
- <Route path="/policyModal/:policyInstanceType/:policyName" render={(routeProps) => (<PolicyModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
- <Route path="/createLoop" render={(routeProps) => (<CreateLoopModal {...routeProps} loadLoopFunction={this.loadLoop} />)} />
- <Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps} loadLoopFunction={this.loadLoop} />)} />
- <Route path="/loopProperties" render={(routeProps) => (<LoopPropertiesModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
- <Route path="/modifyLoop" render={(routeProps) => (<ModifyLoopModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
+
+ <Route path="/policyModal/:policyInstanceType/:policyName" render={(routeProps) => (<PolicyModal {...routeProps}
+ loopCache={this.getLoopCache()}
+ loadLoopFunction={this.loadLoop}/>)}
+ />
+ <Route path="/createLoop" render={(routeProps) => (<CreateLoopModal {...routeProps}
+ loadLoopFunction={this.loadLoop} />)}
+ />
+ <Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps}
+ loadLoopFunction={this.loadLoop} />)}
+ />
+ <Route path="/loopProperties" render={(routeProps) => (<LoopPropertiesModal {...routeProps}
+ loopCache={this.getLoopCache()}
+ loadLoopFunction={this.loadLoop}/>)}
+ />
+ <Route path="/modifyLoop" render={(routeProps) => (<ModifyLoopModal {...routeProps}
+ loopCache={this.getLoopCache()}
+ loadLoopFunction={this.loadLoop}/>)}
+ />
<Route path="/userInfo" render={(routeProps) => (<UserInfoModal {...routeProps} />)} />
<Route path="/closeLoop" render={this.closeLoop} />
- <Route path="/submit" render={(routeProps) => (<PerformAction {...routeProps} loopAction="submit" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/stop" render={(routeProps) => (<PerformAction {...routeProps} loopAction="stop" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/restart" render={(routeProps) => (<PerformAction {...routeProps} loopAction="restart" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/delete" render={(routeProps) => (<PerformAction {...routeProps} loopAction="delete" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/undeploy" render={(routeProps) => (<PerformAction {...routeProps} loopAction="undeploy" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/deploy" render={(routeProps) => (<DeployLoopModal {...routeProps} loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <Route path="/refreshStatus" render={(routeProps) => (<RefreshStatus {...routeProps} loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} />
- <GlobalClampStyle />
+
+ <Route path="/submit" render={(routeProps) => (<PerformAction {...routeProps}
+ loopAction="submit"
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}
+ setBusyLoading={this.setBusyLoading}
+ clearBusyLoading={this.clearBusyLoading}/>)}
+ />
+ <Route path="/stop" render={(routeProps) => (<PerformAction {...routeProps}
+ loopAction="stop"
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}
+ setBusyLoading={this.setBusyLoading}
+ clearBusyLoading={this.clearBusyLoading}/>)}
+ />
+ <Route path="/restart" render={(routeProps) => (<PerformAction {...routeProps}
+ loopAction="restart"
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}
+ setBusyLoading={this.setBusyLoading}
+ clearBusyLoading={this.clearBusyLoading}/>)}
+ />
+ <Route path="/delete" render={(routeProps) => (<PerformAction {...routeProps}
+ loopAction="delete"
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}
+ setBusyLoading={this.setBusyLoading}
+ clearBusyLoading={this.clearBusyLoading}/>)}
+ />
+ <Route path="/undeploy" render={(routeProps) => (<PerformAction {...routeProps}
+ loopAction="undeploy"
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}
+ setBusyLoading={this.setBusyLoading}
+ clearBusyLoading={this.clearBusyLoading}/>)}
+ />
+ <Route path="/deploy" render={(routeProps) => (<DeployLoopModal {...routeProps}
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}/>)}
+ />
+ <Route path="/refreshStatus" render={(routeProps) => (<RefreshStatus {...routeProps}
+ loopCache={this.getLoopCache()}
+ updateLoopFunction={this.updateLoopCache}
+ showSucAlert={this.showSucAlert}
+ showFailAlert={this.showFailAlert}/>)}
+ />
+ </React.Fragment>
+ );
+ }
+
+ renderSpinner() {
+ if (this.isBusyLoading()) {
+ return (
+ <StyledSpinnerDiv>
+ <Spinner animation="border" role="status">
+ <span className="sr-only">Loading...</span>
+ </Spinner>
+ </StyledSpinnerDiv>
+ );
+ } else {
+ return (<div></div>);
+ }
+ }
+
+ render() {
+ return (
+ <StyledMainDiv id="main_div">
+ <GlobalClampStyle />
+ {this.renderRoutes()}
+ {this.renderSpinner()}
{this.renderAlertBar()}
{this.renderNavBar()}
{this.renderLoopViewer()}
diff --git a/ui-react/src/__snapshots__/LoopUI.test.js.snap b/ui-react/src/__snapshots__/LoopUI.test.js.snap
index 2dfa4809..cae9182f 100644
--- a/ui-react/src/__snapshots__/LoopUI.test.js.snap
+++ b/ui-react/src/__snapshots__/LoopUI.test.js.snap
@@ -4,6 +4,7 @@ exports[`Verify LoopUI Test the render method 1`] = `
<styled.div
id="main_div"
>
+ <GlobalStyleComponent />
<Route
path="/uploadToscaPolicyModal"
render={[Function]}
@@ -76,7 +77,7 @@ exports[`Verify LoopUI Test the render method 1`] = `
path="/refreshStatus"
render={[Function]}
/>
- <GlobalStyleComponent />
+ <div />
<div>
<Alert
closeLabel="Close alert"
@@ -166,6 +167,7 @@ exports[`Verify LoopUI Test the render method 1`] = `
<withRouter(SvgGenerator)
clickable={true}
generatedFrom="INSTANCE"
+ isBusyLoading={[Function]}
loopCache={
LoopCache {
"loopJsonCache": Object {},
diff --git a/ui-react/src/__snapshots__/OnapClamp.test.js.snap b/ui-react/src/__snapshots__/OnapClamp.test.js.snap
index 56d022fc..d4573b3d 100644
--- a/ui-react/src/__snapshots__/OnapClamp.test.js.snap
+++ b/ui-react/src/__snapshots__/OnapClamp.test.js.snap
@@ -31,6 +31,7 @@ exports[`Verify OnapClamp Test the render method 1`] = `
<styled.div
id="main_div"
>
+ <GlobalStyleComponent />
<Route
path="/uploadToscaPolicyModal"
render={[Function]}
@@ -103,7 +104,7 @@ exports[`Verify OnapClamp Test the render method 1`] = `
path="/refreshStatus"
render={[Function]}
/>
- <GlobalStyleComponent />
+ <div />
<div>
<Alert
closeLabel="Close alert"
@@ -191,6 +192,7 @@ exports[`Verify OnapClamp Test the render method 1`] = `
<withRouter(SvgGenerator)
clickable={true}
generatedFrom="INSTANCE"
+ isBusyLoading={[Function]}
loopCache={
LoopCache {
"loopJsonCache": Object {},
diff --git a/ui-react/src/components/dialogs/PerformActions.js b/ui-react/src/components/dialogs/PerformActions.js
index cf5a3c20..f6001e21 100644
--- a/ui-react/src/components/dialogs/PerformActions.js
+++ b/ui-react/src/components/dialogs/PerformActions.js
@@ -22,24 +22,19 @@
*/
import React from 'react';
import LoopActionService from '../../api/LoopActionService';
-import Spinner from 'react-bootstrap/Spinner'
-import styled from 'styled-components';
-const StyledSpinnerDiv = styled.div`
- justify-content: center !important;
- display: flex !important;
-`;
export default class PerformActions extends React.Component {
state = {
loopName: this.props.loopCache.getLoopName(),
loopAction: this.props.loopAction
};
+
constructor(props, context) {
super(props, context);
-
this.refreshStatus = this.refreshStatus.bind(this);
}
+
componentWillReceiveProps(newProps) {
this.setState({
loopName: newProps.loopCache.getLoopName(),
@@ -51,35 +46,50 @@ export default class PerformActions extends React.Component {
const action = this.state.loopAction;
const loopName = this.state.loopName;
- LoopActionService.performAction(loopName, action).then(pars => {
+ if (action === 'delete') {
+ if (window.confirm('You are about to remove Control Loop Model "' + loopName +
+ '". Select OK to continue with deletion or Cancel to keep the model.') === false) {
+ return;
+ }
+ }
+
+ this.props.setBusyLoading(); // Alert top level to start block user clicks
+
+ LoopActionService.performAction(loopName, action)
+ .then(pars => {
this.props.showSucAlert("Action " + action + " successfully performed");
- // refresh status and update loop logs
- this.refreshStatus(loopName);
+ if (action === 'delete') {
+ this.props.updateLoopFunction(null);
+ this.props.history.push('/');
+ } else {
+ // refresh status and update loop logs
+ this.refreshStatus(loopName);
+ }
})
.catch(error => {
this.props.showFailAlert("Action " + action + " failed");
// refresh status and update loop logs
this.refreshStatus(loopName);
- });
-
+ })
+ .finally(() => this.props.clearBusyLoading());
}
refreshStatus(loopName) {
- LoopActionService.refreshStatus(loopName).then(data => {
+
+ this.props.setBusyLoading();
+
+ LoopActionService.refreshStatus(loopName)
+ .then(data => {
this.props.updateLoopFunction(data);
this.props.history.push('/');
})
- .catch(error => {
+ .catch(error => {
this.props.history.push('/');
- });
+ })
+ .finally(() => this.props.clearBusyLoading());
}
render() {
- return (
- <StyledSpinnerDiv>
- <Spinner animation="border" role="status">
- </Spinner>
- </StyledSpinnerDiv>
- );
+ return null;
}
}
diff --git a/ui-react/src/components/dialogs/PerformActions.test.js b/ui-react/src/components/dialogs/PerformActions.test.js
index b833a929..c91c2f67 100644
--- a/ui-react/src/components/dialogs/PerformActions.test.js
+++ b/ui-react/src/components/dialogs/PerformActions.test.js
@@ -38,6 +38,8 @@ describe('Verify PerformActions', () => {
const updateLoopFunction = jest.fn();
const showSucAlert = jest.fn();
const showFailAlert = jest.fn();
+ const setBusyLoading = jest.fn();
+ const clearBusyLoading = jest.fn();
LoopActionService.refreshStatus = jest.fn().mockImplementation(() => {
return Promise.resolve({
@@ -47,7 +49,7 @@ describe('Verify PerformActions', () => {
});
});
const component = shallow(<PerformActions loopCache={loopCache}
- loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} />)
+ loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} setBusyLoading={setBusyLoading} clearBusyLoading={clearBusyLoading}/>)
await flushPromises();
component.update();
@@ -60,6 +62,8 @@ describe('Verify PerformActions', () => {
const updateLoopFunction = jest.fn();
const showSucAlert = jest.fn();
const showFailAlert = jest.fn();
+ const setBusyLoading = jest.fn();
+ const clearBusyLoading = jest.fn();
LoopActionService.performAction = jest.fn().mockImplementation(() => {
return Promise.resolve({
@@ -76,7 +80,7 @@ describe('Verify PerformActions', () => {
});
});
const component = shallow(<PerformActions loopCache={loopCache}
- loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} />)
+ loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} setBusyLoading={setBusyLoading} clearBusyLoading={clearBusyLoading}/>)
await flushPromises();
component.update();
diff --git a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
index d718c2e4..7070455e 100644
--- a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
+++ b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js
@@ -70,13 +70,15 @@ class SvgGenerator extends React.Component {
}
handleSvgClick(event) {
- if (this.state.clickable) {
- console.debug("svg click event received");
- var elementName = event.target.parentNode.getAttribute('policyId');
- console.info("SVG element clicked", elementName);
- if (elementName !== null) {
- this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName);
- }
+ console.debug("svg click event received");
+ if (this.state.clickable) {
+ var elementName = event.target.parentNode.getAttribute('policyId');
+ console.info("SVG element clicked", elementName);
+ // Only allow movement to policy editing IF there busyLoadingCOunt is 0,
+ // meaning we are not waiting for refreshStatus to complete, for example
+ if (elementName !== null && !this.props.isBusyLoading()) {
+ this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName);
+ }
}
}