aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--docs/release-notes.rst52
-rw-r--r--docs/user-guide.rst26
-rw-r--r--extra/docker/elk/docker-compose.yml4
-rw-r--r--pom.xml52
-rw-r--r--releases/4.1.0.yaml5
-rw-r--r--src/main/docker/Dockerfile34
-rw-r--r--src/main/docker/backend/Dockerfile57
-rw-r--r--src/main/docker/backend/backend-files.xml (renamed from src/main/docker/assembly/clamp-files.xml)17
-rw-r--r--src/main/docker/elasticsearch/Dockerfile20
-rw-r--r--src/main/docker/elasticsearch/bin/init_sg.sh7
-rw-r--r--src/main/docker/elasticsearch/config/elasticsearch.yml33
-rw-r--r--src/main/docker/elasticsearch/config/sg/kirk-keystore.jksbin0 -> 4525 bytes
-rw-r--r--src/main/docker/elasticsearch/config/sg/node-0-keystore.jksbin0 -> 4593 bytes
-rw-r--r--src/main/docker/elasticsearch/config/sg/sg_action_groups.yml153
-rw-r--r--src/main/docker/elasticsearch/config/sg/sg_config.yml238
-rw-r--r--src/main/docker/elasticsearch/config/sg/sg_internal_users.yml45
-rw-r--r--src/main/docker/elasticsearch/config/sg/sg_roles.yml304
-rw-r--r--src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml34
-rw-r--r--src/main/docker/elasticsearch/config/sg/truststore.jksbin0 -> 1096 bytes
-rw-r--r--src/main/docker/elasticsearch/my-entrypoint.sh8
-rw-r--r--src/main/docker/frontend/Dockerfile52
-rw-r--r--src/main/docker/frontend/frontend-files.xml43
-rw-r--r--src/main/docker/kibana/Dockerfile (renamed from src/main/docker/kibana/Dockerfile.kibana)5
-rwxr-xr-xsrc/main/docker/kibana/backup.py4
-rw-r--r--src/main/docker/kibana/conf/kibana.yml10
-rwxr-xr-xsrc/main/docker/kibana/restore.py4
-rw-r--r--src/main/docker/logstash/Dockerfile (renamed from src/main/docker/logstash/Dockerfile.logstash)16
-rw-r--r--src/main/docker/logstash/pipeline/logstash.conf6
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsDictionary.java43
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java63
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsInfo.java29
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsModel.java43
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsServiceData.java6
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsTemplate.java69
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java22
-rw-r--r--src/main/java/org/onap/clamp/clds/model/CldsVfcData.java24
-rw-r--r--src/main/java/org/onap/clamp/clds/model/DcaeEvent.java31
-rw-r--r--src/main/java/org/onap/clamp/clds/model/ValueItem.java26
-rw-r--r--src/main/java/org/onap/clamp/loop/LoopController.java5
-rw-r--r--src/main/resources/boot-message.txt19
-rw-r--r--ui-react/nginx/nginx.conf17
-rw-r--r--ui-react/package.json36
-rw-r--r--ui-react/public/index.html40
-rw-r--r--ui-react/public/manifest.json15
-rw-r--r--ui-react/public/onap.icobin0 -> 18046 bytes
-rw-r--r--ui-react/src/LoopUI.js205
-rw-r--r--ui-react/src/NotFound.js36
-rw-r--r--ui-react/src/OnapClamp.js39
-rw-r--r--ui-react/src/React-Spinner.jpgbin0 -> 5140 bytes
-rw-r--r--ui-react/src/__test__/LoopCache.test.js217
-rw-r--r--ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json117
-rw-r--r--ui-react/src/__test__/OpenLoopModal.test.js35
-rw-r--r--ui-react/src/api/LoopActionService.js74
-rw-r--r--ui-react/src/api/LoopCache.js116
-rw-r--r--ui-react/src/api/LoopService.js131
-rw-r--r--ui-react/src/api/UserService.js72
-rw-r--r--ui-react/src/api/example.json417
-rw-r--r--ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js126
-rw-r--r--ui-react/src/components/dialogs/LoopProperties.js119
-rw-r--r--ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js111
-rw-r--r--ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicy.css73
-rw-r--r--ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js552
-rw-r--r--ui-react/src/components/dialogs/OperationalPolicy/template.json52
-rw-r--r--ui-react/src/components/dialogs/UserInfo.js161
-rw-r--r--ui-react/src/components/loop_viewer/logs/LoopLogs.js97
-rw-r--r--ui-react/src/components/loop_viewer/status/LoopStatus.js105
-rw-r--r--ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js18
-rw-r--r--ui-react/src/components/loop_viewer/svg/LoopSvg.js101
-rw-r--r--ui-react/src/components/loop_viewer/svg/example.svg1
-rw-r--r--ui-react/src/components/menu/MenuBar.js87
-rw-r--r--ui-react/src/components/menu/PerformActions.js85
-rw-r--r--ui-react/src/components/menu/RefreshStatus.js66
-rw-r--r--ui-react/src/index.js38
-rw-r--r--ui-react/src/logo.pngbin0 -> 21360 bytes
-rw-r--r--ui-react/src/setupTests.js4
-rw-r--r--ui-react/src/theme/globalStyle.js91
77 files changed, 4857 insertions, 210 deletions
diff --git a/.gitignore b/.gitignore
index f70506ad0..fbdcf4af5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,9 @@ target
.buildpath
.idea
*.iml
-**/logs/
+ui-react/node_modules
+ui-react/build
+**/package-lock.json
**/.evosuite/
**/debug-logs/
*.log
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index 9dce5a8ec..2776df572 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -5,10 +5,58 @@
Release Notes
=============
-Version: 4.0.3
+Version: 4.1.0
--------------
-:Release Date: 2019-05-06
+:Release Date: 2019-08-19
+
+**New Features**
+
+The El Alto-Early Drop release is the fifth release of the Control Loop Automation Management Platform (CLAMP).
+
+The main goal of the El Alto-Early Drop release was to:
+
+ - _.Fix a maximum a security issues, especially the angular related issues by moving to React.
+
+**Bug Fixes**
+
+ - The full list of implemented user stories and epics is available on `CLAMP R5 - Early Drop RELEASE <https://wiki.onap.org/display/DW/CLAMP+R5+-+Early+Drop>`_
+ This includes the list of bugs that were fixed during the course of this release.
+
+**Known Issues**
+
+ - `CLAMP-384 <https://jira.onap.org/browse/CLAMP-384>`_ Loop State in UI is not reflecting the current state
+
+**Security Notes**
+
+*Fixed Security Issues*
+
+ - `OJSI-166 <https://jira.onap.org/browse/OJSI-166>`_ Port 30290 exposes unprotected service outside of cluster.
+
+*Known Security Issues*
+
+*Known Vulnerabilities in Used Modules*
+
+CLAMP code has been formally scanned during build time using NexusIQ and all Critical vulnerabilities have been addressed, items that remain open have been assessed for risk and actions to be taken in future release.
+The CLAMP open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=68540334>`_.
+
+Quick Links:
+ - `CLAMP project page <https://wiki.onap.org/display/DW/CLAMP+Project>`_
+
+ - `Passing Badge information for CLAMP <https://bestpractices.coreinfrastructure.org/en/projects/1197>`_
+
+ - `Project Vulnerability Review Table for CLAMP <https://wiki.onap.org/pages/viewpage.action?pageId=68540334>`_
+
+**Upgrade Notes**
+
+ New Docker Containers are available.
+
+
+
+Version: 4.0.5
+--------------
+
+:Release Date: 2019-06-06
**New Features**
diff --git a/docs/user-guide.rst b/docs/user-guide.rst
index ac45ebc42..0d7d4e619 100644
--- a/docs/user-guide.rst
+++ b/docs/user-guide.rst
@@ -74,13 +74,35 @@ Once clicked, it's possible to configure operational policy. Policy can have chi
3. Specifies whether policy is abated
4. Unique id for Control Loop.
5. Button for creating child/parent policies
+ Child/parent policies are policies that depend on one another under certain circumstances (check point 12.)
6. Unique id of Policy. (Clamp internal)
7. Recipe/Operation triggered on controller/orchestrator
-8. Maximum count of retries
+ This recipe will be used by policy drools PDP when sending request to controller/orchestrator.
+ E.g. in case of *Health-Check* is selected here and *APPC* as actor PDP will trigger APPC LCM API triggering health-check operation.
+
+ List of options is predefined in Clamp code and can't be modified.
+ Possible options:
+ * Restart
+ * Rebuild
+ * Migrate
+ * Health-Check
+ * ModifyConfig
+ * VF Module Create
+ * VF Module Delete
+ * Reroute
+8. Maximum amount of retries that policy takes when triggering action
9. Timeout for this operational policy
10. Actor used to perform action. (Orchestrator/Controller)
+ Actor that will be used by drools PDP to perform action.
+ Possible options:
+ * APPC
+ * SO
+ * VFC
+ * SDNC
+ * SDNR
11. Payload required by actor to perform an action
-12. Set of fields with policies called under certain conditions. E.g. when health-check receives timeout failure restart could be called.
+12. Set of fields describing child/parend policies dependency.
+ E.g. when health-check receives timeout failure restart could be called.
13. Set of fields specifying resource. On this resource Operational Policy should perform an action
14. Checkbox enabling/disabling guard policy for this operational policy
15. Guard Policy type (frequency limited or min max)
diff --git a/extra/docker/elk/docker-compose.yml b/extra/docker/elk/docker-compose.yml
index cb39b660e..886563812 100644
--- a/extra/docker/elk/docker-compose.yml
+++ b/extra/docker/elk/docker-compose.yml
@@ -2,7 +2,7 @@ version: '3.1'
services:
elasticsearch:
- image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.6.2
+ image: onap/clamp-dashboard-elasticsearch:latest
ports:
- 9200:9200
networks:
@@ -36,6 +36,8 @@ services:
- notification_topic=POLICY-CL-MGT
- request_topic=APPC-CL
- elasticsearch_base_url=elasticsearch
+ - "LOGSTASH_USR=logstash"
+ - "LOGSTASH_PWD=logstash"
kibana:
image: onap/clamp-dashboard-kibana:latest
diff --git a/pom.xml b/pom.xml
index ec1033b05..841917b3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1025,8 +1025,8 @@
</run>
</image>
<image>
- <name>onap/clamp</name>
- <alias>onap-clamp</alias>
+ <name>onap/clamp-backend</name>
+ <alias>onap-clamp-backend</alias>
<run>
<skip>true</skip>
</run>
@@ -1039,14 +1039,52 @@
</tags>
<!-- A relative path is looked up in ${project.basedir}/src/main/docker
by default -->
- <dockerFile>Dockerfile</dockerFile>
+ <dockerFile>backend/Dockerfile</dockerFile>
<assembly>
- <descriptor>assembly/clamp-files.xml</descriptor>
- <name>onap-clamp</name>
+ <descriptor>backend/backend-files.xml</descriptor>
+ <name>onap-clamp-backend</name>
</assembly>
</build>
</image>
<image>
+ <name>onap/clamp-frontend</name>
+ <alias>onap-clamp-frontend</alias>
+ <run>
+ <skip>true</skip>
+ </run>
+ <build>
+ <cleanup>true</cleanup>
+ <tags>
+ <tag>latest</tag>
+ <tag>${project.docker.latesttagtimestamp.version}</tag>
+ <tag>${project.docker.latesttag.version}</tag>
+ </tags>
+ <!-- A relative path is looked up in ${project.basedir}/src/main/docker
+ by default -->
+ <dockerFile>frontend/Dockerfile</dockerFile>
+ <assembly>
+ <descriptor>frontend/frontend-files.xml</descriptor>
+ <name>onap-clamp-frontend</name>
+ </assembly>
+ </build>
+ </image>
+ <image>
+ <name>onap/clamp-dashboard-elasticsearch</name>
+ <alias>onap-clamp-dashboard-elasticsearch</alias>
+ <run>
+ <skip>true</skip>
+ </run>
+ <build>
+ <cleanup>true</cleanup>
+ <tags>
+ <tag>latest</tag>
+ <tag>${project.docker.latesttagtimestamp.version}</tag>
+ <tag>${project.docker.latesttag.version}</tag>
+ </tags>
+ <dockerFile>elasticsearch/Dockerfile</dockerFile>
+ </build>
+ </image>
+ <image>
<name>onap/clamp-dashboard-logstash</name>
<alias>onap-clamp-dashboard-logstash</alias>
<run>
@@ -1059,7 +1097,7 @@
<tag>${project.docker.latesttagtimestamp.version}</tag>
<tag>${project.docker.latesttag.version}</tag>
</tags>
- <dockerFile>logstash/Dockerfile.logstash</dockerFile>
+ <dockerFile>logstash/Dockerfile</dockerFile>
</build>
</image>
<image>
@@ -1075,7 +1113,7 @@
<tag>${project.docker.latesttagtimestamp.version}</tag>
<tag>${project.docker.latesttag.version}</tag>
</tags>
- <dockerFile>kibana/Dockerfile.kibana</dockerFile>
+ <dockerFile>kibana/Dockerfile</dockerFile>
</build>
</image>
</images>
diff --git a/releases/4.1.0.yaml b/releases/4.1.0.yaml
new file mode 100644
index 000000000..f71e3f935
--- /dev/null
+++ b/releases/4.1.0.yaml
@@ -0,0 +1,5 @@
+---
+distribution_type: 'maven'
+version: '4.1.0'
+project: 'clamp'
+log_dir: 'clamp-maven-stage-master/81/' \ No newline at end of file
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile
deleted file mode 100644
index c47d6229e..000000000
--- a/src/main/docker/Dockerfile
+++ /dev/null
@@ -1,34 +0,0 @@
-FROM openjdk:8u191-jdk-alpine3.9
-
-MAINTAINER "The Onap Team"
-LABEL Description="This immage contains alpine, openjdk 11 and clamp"
-
-ARG http_proxy
-ARG https_proxy
-ENV HTTP_PROXY=$http_proxy
-ENV HTTPS_PROXY=$https_proxy
-ENV http_proxy=$HTTP_PROXY
-ENV https_proxy=$HTTPS_PROXY
-
-RUN addgroup onap && adduser -D -G onap clamp
-VOLUME /opt/clamp/config
-RUN mkdir /var/log/onap
-RUN chmod a+rwx /var/log/onap
-
-COPY onap-clamp/clamp.jar /opt/clamp/app.jar
-RUN chmod 700 /opt/clamp/app.jar
-
-RUN chown -R clamp:onap /opt/clamp
-
-RUN apk add fontconfig
-RUN apk add ttf-dejavu
-
-RUN ln -s /usr/lib/libfontconfig.so.1 /usr/lib/libfontconfig.so && \
- ln -s /lib/libuuid.so.1 /usr/lib/libuuid.so.1 && \
- ln -s /lib/libc.musl-x86_64.so.1 /usr/lib/libc.musl-x86_64.so.1
-
-ENV LD_LIBRARY_PATH /usr/lib
-
-USER clamp
-WORKDIR /opt/clamp/
-ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms256m", "-Xmx1g", "-jar" ,"./app.jar"]
diff --git a/src/main/docker/backend/Dockerfile b/src/main/docker/backend/Dockerfile
new file mode 100644
index 000000000..9e5c8d8b1
--- /dev/null
+++ b/src/main/docker/backend/Dockerfile
@@ -0,0 +1,57 @@
+###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 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============================================
+# ===================================================================
+#
+###
+
+FROM openjdk:8u191-jdk-alpine3.9
+
+MAINTAINER "The Onap Team"
+LABEL Description="This immage contains alpine, openjdk 11 and clamp"
+
+ARG http_proxy
+ARG https_proxy
+ENV HTTP_PROXY=$http_proxy
+ENV HTTPS_PROXY=$https_proxy
+ENV http_proxy=$HTTP_PROXY
+ENV https_proxy=$HTTPS_PROXY
+
+RUN addgroup onap && adduser -D -G onap clamp
+VOLUME /opt/clamp/config
+RUN mkdir /var/log/onap
+RUN chmod a+rwx /var/log/onap
+
+COPY onap-clamp-backend/clamp.jar /opt/clamp/app.jar
+RUN chmod 700 /opt/clamp/app.jar
+
+RUN chown -R clamp:onap /opt/clamp
+
+RUN apk add fontconfig
+RUN apk add ttf-dejavu
+
+RUN ln -s /usr/lib/libfontconfig.so.1 /usr/lib/libfontconfig.so && \
+ ln -s /lib/libuuid.so.1 /usr/lib/libuuid.so.1 && \
+ ln -s /lib/libc.musl-x86_64.so.1 /usr/lib/libc.musl-x86_64.so.1
+
+ENV LD_LIBRARY_PATH /usr/lib
+
+USER clamp
+WORKDIR /opt/clamp/
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms256m", "-Xmx1g", "-jar" ,"./app.jar"]
diff --git a/src/main/docker/assembly/clamp-files.xml b/src/main/docker/backend/backend-files.xml
index e4e9875c5..7a9c6f530 100644
--- a/src/main/docker/assembly/clamp-files.xml
+++ b/src/main/docker/backend/backend-files.xml
@@ -1,6 +1,6 @@
<!--
============LICENSE_START=======================================================
- ECOMP MSO
+ ECOMP CLAMP
================================================================================
Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
================================================================================
@@ -29,7 +29,6 @@
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
-
<fileSets>
<!-- include config files -->
<fileSet>
@@ -39,20 +38,6 @@
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
- <fileSet>
- <includes>
- <include>>etc/config/**</include>
- </includes>
- <directory>${project.build.directory}</directory>
- <outputDirectory>/</outputDirectory>
- </fileSet>
- <fileSet>
- <includes>
- <include>etc/keystore/**</include>
- </includes>
- <directory>${project.build.directory}</directory>
- <outputDirectory>/</outputDirectory>
- </fileSet>
</fileSets>
</assembly>
diff --git a/src/main/docker/elasticsearch/Dockerfile b/src/main/docker/elasticsearch/Dockerfile
new file mode 100644
index 000000000..2c932c8e9
--- /dev/null
+++ b/src/main/docker/elasticsearch/Dockerfile
@@ -0,0 +1,20 @@
+# https://github.com/elastic/elasticsearch-docker
+FROM docker.elastic.co/elasticsearch/elasticsearch:6.6.2
+
+COPY config/sg/ config/sg/
+COPY config/ config/
+COPY bin/ bin/
+#RUN chmod +x bin/init_sg.sh
+COPY my-entrypoint.sh /usr/local/bin/my-entrypoint.sh
+# Search Guard plugin
+RUN elasticsearch-plugin install --batch com.floragunn:search-guard-6:6.6.2-25.1 \
+ && chmod +x plugins/search-guard-6/tools/*.sh \
+ && chown -R elasticsearch config/sg/ \
+ && chmod -R go= config/sg/
+
+# Add your elasticsearch plugins setup here
+# Example: RUN elasticsearch-plugin install analysis-icu
+
+# This custom entrypoint script is used instead of
+# the original's /usr/local/bin/docker-entrypoint.sh
+ENTRYPOINT ["bash","-c","my-entrypoint.sh"] \ No newline at end of file
diff --git a/src/main/docker/elasticsearch/bin/init_sg.sh b/src/main/docker/elasticsearch/bin/init_sg.sh
new file mode 100644
index 000000000..1c4e607c6
--- /dev/null
+++ b/src/main/docker/elasticsearch/bin/init_sg.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+plugins/search-guard-6/tools/sgadmin.sh \
+ -cd config/sg/ \
+ -ts config/sg/truststore.jks \
+ -ks config/sg/kirk-keystore.jks \
+ -nhnv \
+ -icl \ No newline at end of file
diff --git a/src/main/docker/elasticsearch/config/elasticsearch.yml b/src/main/docker/elasticsearch/config/elasticsearch.yml
new file mode 100644
index 000000000..72ce137ab
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/elasticsearch.yml
@@ -0,0 +1,33 @@
+---
+## Default Elasticsearch configuration from elasticsearch-docker.
+## from https://github.com/elastic/elasticsearch-docker/blob/master/.tedi/template/elasticsearch.yml
+#
+cluster.name: "docker-cluster"
+network.host: 0.0.0.0
+
+# minimum_master_nodes need to be explicitly set when bound on a public IP
+# set to 1 to allow single node clusters
+# Details: https://github.com/elastic/elasticsearch/pull/17288
+discovery.zen.minimum_master_nodes: 1
+
+## Use single node discovery in order to disable production mode and avoid bootstrap checks
+## see https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
+#
+discovery.type: single-node
+
+## X-Pack settings
+## see https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-xpack.html
+#
+xpack.license.self_generated.type: basic
+xpack.security.enabled: false
+
+## Search Guard
+#
+searchguard.enterprise_modules_enabled: false
+
+searchguard.ssl.transport.keystore_filepath: sg/node-0-keystore.jks
+searchguard.ssl.transport.truststore_filepath: sg/truststore.jks
+searchguard.ssl.transport.enforce_hostname_verification: false
+
+searchguard.authcz.admin_dn:
+ - "CN=kirk,OU=client,O=client,l=tEst,C=De"
diff --git a/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks b/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks
new file mode 100644
index 000000000..dd7562ef8
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks
Binary files differ
diff --git a/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks b/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks
new file mode 100644
index 000000000..5693b7bf8
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks
Binary files differ
diff --git a/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml b/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml
new file mode 100644
index 000000000..38ffef024
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml
@@ -0,0 +1,153 @@
+UNLIMITED:
+ readonly: true
+ permissions:
+ - "*"
+
+###### INDEX LEVEL ######
+
+INDICES_ALL:
+ readonly: true
+ permissions:
+ - "indices:*"
+
+# for backward compatibility
+ALL:
+ readonly: true
+ permissions:
+ - INDICES_ALL
+
+MANAGE:
+ readonly: true
+ permissions:
+ - "indices:monitor/*"
+ - "indices:admin/*"
+
+CREATE_INDEX:
+ readonly: true
+ permissions:
+ - "indices:admin/create"
+ - "indices:admin/mapping/put"
+
+MANAGE_ALIASES:
+ readonly: true
+ permissions:
+ - "indices:admin/aliases*"
+
+INDEX_OWNER:
+ - CREATE_INDEX
+ - CRUD
+
+# for backward compatibility
+MONITOR:
+ readonly: true
+ permissions:
+ - INDICES_MONITOR
+
+INDICES_MONITOR:
+ readonly: true
+ permissions:
+ - "indices:monitor/*"
+
+DATA_ACCESS:
+ readonly: true
+ permissions:
+ - "indices:data/*"
+ - CRUD
+
+WRITE:
+ readonly: true
+ permissions:
+ - "indices:data/write*"
+ - "indices:admin/mapping/put"
+
+READ:
+ readonly: true
+ permissions:
+ - "indices:data/read*"
+ - "indices:admin/mappings/fields/get*"
+
+DELETE:
+ readonly: true
+ permissions:
+ - "indices:data/write/delete*"
+
+CRUD:
+ readonly: true
+ permissions:
+ - READ
+ - WRITE
+
+SEARCH:
+ readonly: true
+ permissions:
+ - "indices:data/read/search*"
+ - "indices:data/read/msearch*"
+ - SUGGEST
+
+SUGGEST:
+ readonly: true
+ permissions:
+ - "indices:data/read/suggest*"
+
+INDEX:
+ readonly: true
+ permissions:
+ - "indices:data/write/index*"
+ - "indices:data/write/update*"
+ - "indices:admin/mapping/put"
+ - "indices:data/write/bulk*"
+
+GET:
+ readonly: true
+ permissions:
+ - "indices:data/read/get*"
+ - "indices:data/read/mget*"
+
+###### CLUSTER LEVEL ######
+
+CLUSTER_ALL:
+ readonly: true
+ permissions:
+ - "cluster:*"
+
+CLUSTER_MONITOR:
+ readonly: true
+ permissions:
+ - "cluster:monitor/*"
+
+CLUSTER_COMPOSITE_OPS_RO:
+ readonly: true
+ permissions:
+ - "indices:data/read/mget"
+ - "indices:data/read/msearch"
+ - "indices:data/read/mtv"
+ - "indices:admin/aliases/exists*"
+ - "indices:admin/aliases/get*"
+ - "indices:data/read/scroll"
+
+CLUSTER_COMPOSITE_OPS:
+ readonly: true
+ permissions:
+ - "indices:data/write/bulk"
+ - "indices:admin/aliases*"
+ - "indices:data/write/reindex"
+ - CLUSTER_COMPOSITE_OPS_RO
+
+MANAGE_SNAPSHOTS:
+ readonly: true
+ permissions:
+ - "cluster:admin/snapshot/*"
+ - "cluster:admin/repository/*"
+
+# CLAMPDASHBOARD ELASTICSEARCH ENTRYPOINT
+GET_TEMPLATE:
+ - "indices:admin/template/get"
+PUT_TEMPLATE:
+ - "indices:admin/template/put"
+TEMPLATE_OWNER:
+ - GET_TEMPLATE
+ - PUT_TEMPLATE
+ES_INPUT:
+ - TEMPLATE_OWNER
+ - WRITE
+ - MONITOR
diff --git a/src/main/docker/elasticsearch/config/sg/sg_config.yml b/src/main/docker/elasticsearch/config/sg/sg_config.yml
new file mode 100644
index 000000000..7d3a933fa
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/sg_config.yml
@@ -0,0 +1,238 @@
+# This is the main Search Guard configuration file where authentication
+# and authorization is defined.
+#
+# You need to configure at least one authentication domain in the authc of this file.
+# An authentication domain is responsible for extracting the user credentials from
+# the request and for validating them against an authentication backend like Active Directory for example.
+#
+# If more than one authentication domain is configured the first one which succeeds wins.
+# If all authentication domains fail then the request is unauthenticated.
+# In this case an exception is thrown and/or the HTTP status is set to 401.
+#
+# After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect
+# the roles from a given backend for the authenticated user.
+#
+# Both, authc and auth can be enabled/disabled separately for REST and TRANSPORT layer. Default is true for both.
+# http_enabled: true
+# transport_enabled: true
+#
+# 5.x Migration: "enabled: true/false" will also be respected currently but only to provide backward compatibility.
+#
+# For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to
+# find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated.
+# If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "sg_anonymous"
+# and one role named "sg_anonymous_backendrole".
+# If you enable anonymous authentication all HTTP authenticators will not challenge.
+#
+#
+# Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert"
+# first and the challenging one last.
+# Because it's not possible to challenge a client with two different authentication methods (for example
+# Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation
+# by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request.
+#
+# Default value of the challenge flag is true.
+#
+#
+# HTTP
+# basic (challenging)
+# proxy (not challenging, needs xff)
+# kerberos (challenging) NOT FREE FOR COMMERCIAL
+# clientcert (not challenging, needs https)
+# jwt (not challenging) NOT FREE FOR COMMERCIAL
+# host (not challenging) #DEPRECATED, will be removed in a future version.
+# host based authentication is configurable in sg_roles_mapping
+
+# Authc
+# internal
+# noop
+# ldap NOT FREE FOR COMMERCIAL USE
+
+# Authz
+# ldap NOT FREE FOR COMMERCIAL USE
+# noop
+
+searchguard:
+ dynamic:
+ # Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index
+ # Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default)
+ # Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently
+ #filtered_alias_mode: warn
+ #kibana:
+ # Kibana multitenancy - NOT FREE FOR COMMERCIAL USE
+ # see https://github.com/floragunncom/search-guard-docs/blob/master/multitenancy.md
+ # To make this work you need to install https://github.com/floragunncom/search-guard-module-kibana-multitenancy/wiki
+ #multitenancy_enabled: true
+ #server_username: kibanaserver
+ #index: '.kibana'
+ #do_not_fail_on_forbidden: false
+ http:
+ anonymous_auth_enabled: false
+ xff:
+ enabled: false
+ internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern
+ #internalProxies: '.*' # trust all internal proxies, regex pattern
+ remoteIpHeader: 'x-forwarded-for'
+ proxiesHeader: 'x-forwarded-by'
+ #trustedProxies: '.*' # trust all external proxies, regex pattern
+ ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help
+ ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For
+ ###### and here https://tools.ietf.org/html/rfc7239
+ ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve
+ authc:
+ kerberos_auth_domain:
+ http_enabled: false
+ transport_enabled: false
+ order: 6
+ http_authenticator:
+ type: kerberos # NOT FREE FOR COMMERCIAL USE
+ challenge: true
+ config:
+ # If true a lot of kerberos/security related debugging output will be logged to standard out
+ krb_debug: false
+ # If true then the realm will be stripped from the user name
+ strip_realm_from_principal: true
+ authentication_backend:
+ type: noop
+ basic_internal_auth_domain:
+ http_enabled: true
+ transport_enabled: true
+ order: 4
+ http_authenticator:
+ type: basic
+ challenge: true
+ authentication_backend:
+ type: intern
+ proxy_auth_domain:
+ http_enabled: false
+ transport_enabled: false
+ order: 3
+ http_authenticator:
+ type: proxy
+ challenge: false
+ config:
+ user_header: "x-proxy-user"
+ roles_header: "x-proxy-roles"
+ authentication_backend:
+ type: noop
+ jwt_auth_domain:
+ http_enabled: false
+ transport_enabled: false
+ order: 0
+ http_authenticator:
+ type: jwt
+ challenge: false
+ config:
+ signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
+ jwt_header: "Authorization"
+ jwt_url_parameter: null
+ roles_key: null
+ subject_key: null
+ authentication_backend:
+ type: noop
+ clientcert_auth_domain:
+ http_enabled: false
+ transport_enabled: false
+ order: 2
+ http_authenticator:
+ type: clientcert
+ config:
+ username_attribute: cn #optional, if omitted DN becomes username
+ challenge: false
+ authentication_backend:
+ type: noop
+ ldap:
+ http_enabled: false
+ transport_enabled: false
+ order: 5
+ http_authenticator:
+ type: basic
+ challenge: false
+ authentication_backend:
+ # LDAP authentication backend (authenticate users against a LDAP or Active Directory)
+ type: ldap # NOT FREE FOR COMMERCIAL USE
+ config:
+ # enable ldaps
+ enable_ssl: false
+ # enable start tls, enable_ssl should be false
+ enable_start_tls: false
+ # send client certificate
+ enable_ssl_client_auth: false
+ # verify ldap hostname
+ verify_hostnames: true
+ hosts:
+ - localhost:8389
+ bind_dn: null
+ password: null
+ userbase: 'ou=people,dc=example,dc=com'
+ # Filter to search for users (currently in the whole subtree beneath userbase)
+ # {0} is substituted with the username
+ usersearch: '(sAMAccountName={0})'
+ # Use this attribute from the user as username (if not set then DN is used)
+ username_attribute: null
+ authz:
+ roles_from_myldap:
+ http_enabled: false
+ transport_enabled: false
+ authorization_backend:
+ # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too)
+ type: ldap # NOT FREE FOR COMMERCIAL USE
+ config:
+ # enable ldaps
+ enable_ssl: false
+ # enable start tls, enable_ssl should be false
+ enable_start_tls: false
+ # send client certificate
+ enable_ssl_client_auth: false
+ # verify ldap hostname
+ verify_hostnames: true
+ hosts:
+ - localhost:8389
+ bind_dn: null
+ password: null
+ rolebase: 'ou=groups,dc=example,dc=com'
+ # Filter to search for roles (currently in the whole subtree beneath rolebase)
+ # {0} is substituted with the DN of the user
+ # {1} is substituted with the username
+ # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute
+ rolesearch: '(member={0})'
+ # Specify the name of the attribute which value should be substituted with {2} above
+ userroleattribute: null
+ # Roles as an attribute of the user entry
+ userrolename: disabled
+ #userrolename: memberOf
+ # The attribute in a role entry containing the name of that role, Default is "name".
+ # Can also be "dn" to use the full DN as rolename.
+ rolename: cn
+ # Resolve nested roles transitive (roles which are members of other roles and so on ...)
+ resolve_nested_roles: true
+ userbase: 'ou=people,dc=example,dc=com'
+ # Filter to search for users (currently in the whole subtree beneath userbase)
+ # {0} is substituted with the username
+ usersearch: '(uid={0})'
+ # Skip users matching a user name, a wildcard or a regex pattern
+ #skip_users:
+ # - 'cn=Michael Jackson,ou*people,o=TEST'
+ # - '/\S*/'
+ roles_from_another_ldap:
+ enabled: false
+ authorization_backend:
+ type: ldap # NOT FREE FOR COMMERCIAL USE
+ #config goes here ...
+# auth_failure_listeners:
+# ip_rate_limiting:
+# type: ip
+# allowed_tries: 10
+# time_window_seconds: 3600
+# block_expiry_seconds: 600
+# max_blocked_clients: 100000
+# max_tracked_clients: 100000
+# internal_authentication_backend_limiting:
+# type: username
+# authentication_backend: intern
+# allowed_tries: 10
+# time_window_seconds: 3600
+# block_expiry_seconds: 600
+# max_blocked_clients: 100000
+# max_tracked_clients: 100000
+ \ No newline at end of file
diff --git a/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml b/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml
new file mode 100644
index 000000000..1712d3792
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml
@@ -0,0 +1,45 @@
+# This is the internal user database
+# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh
+
+#password is: admin
+admin:
+ readonly: true
+ hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG
+ roles:
+ - admin
+ attributes:
+ #no dots allowed in attribute names
+ attribute1: value1
+ attribute2: value2
+ attribute3: value3
+
+#password is: logstash
+logstash:
+ hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2
+ roles:
+ - logstash
+
+#password is: kibanaserver
+kibanaserver:
+ readonly: true
+ hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H.
+
+#password is: kibanaro
+kibanaro:
+ hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC
+ roles:
+ - kibanauser
+ - readall
+
+#password is: readall
+readall:
+ hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2
+ #password is: readall
+ roles:
+ - readall
+
+#password is: snapshotrestore
+snapshotrestore:
+ hash: $2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W
+ roles:
+ - snapshotrestore \ No newline at end of file
diff --git a/src/main/docker/elasticsearch/config/sg/sg_roles.yml b/src/main/docker/elasticsearch/config/sg/sg_roles.yml
new file mode 100644
index 000000000..6902fba2c
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/sg_roles.yml
@@ -0,0 +1,304 @@
+#<sg_role_name>:
+# cluster:
+# - '<permission>'
+# indices:
+# '<indexname or alias>':
+# '<type>':
+# - '<permission>'
+# _dls_: '<dls query>'
+# _fls_:
+# - '<field>'
+# - '<field>'
+
+# When a user make a request to Elasticsearch then the following roles will be evaluated to see if the user has
+# permissions for the request. A request is always associated with an action and is executed against and index (or alias)
+# and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed.
+# Every role a user has will be examined if it allows the action against an index (or type). At least one role must match
+# for the request to be successful. If no role match then the request will be denied. Currently a match must happen within
+# one single role - that means that permissions can not span multiple roles.
+
+# For <permission>, <indexname or alias> and <type> simple wildcards and regular expressions are possible.
+# A asterix (*) will match any character sequence (or an empty sequence)
+# A question mark (?) will match any single character (but NOT empty character)
+# Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1'
+# Example: '?kibana' will match '.kibana' but not 'kibana'
+
+# To use a full blown regex you have to pre- and apend a '/' to use regex instead of simple wildcards
+# '/<java regex>/'
+# Example: '/\S*/' will match any non whitespace characters
+
+# Important:
+# Index, alias or type names can not contain dots (.) in the <indexname or alias> or <type> expression.
+# Reason is that we currently parse the config file into a elasticsearch settings object which cannot cope with dots in keys.
+# Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index'
+# This limitation will likely removed with Search Guard 6
+
+# DLS (Document level security) - NOT FREE FOR COMMERCIAL
+# http://docs.search-guard.com/v6/document-level-security
+
+# FLS (Field level security) - NOT FREE FOR COMMERCIAL
+# http://docs.search-guard.com/v6/field-level-security
+
+# Kibana multitenancy - NOT FREE FOR COMMERCIAL
+# http://docs.search-guard.com/v6/kibana-multi-tenancy
+
+# Allows everything, but no changes to searchguard configuration index
+sg_all_access:
+ readonly: true
+ cluster:
+ - UNLIMITED
+ indices:
+ '*':
+ '*':
+ - UNLIMITED
+ tenants:
+ admin_tenant: RW
+
+# Read all, but no write permissions
+sg_readall:
+ readonly: true
+ cluster:
+ - CLUSTER_COMPOSITE_OPS_RO
+ indices:
+ '*':
+ '*':
+ - READ
+
+# Read all and monitor, but no write permissions
+sg_readall_and_monitor:
+ cluster:
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS_RO
+ indices:
+ '*':
+ '*':
+ - READ
+
+# For users which use kibana, access to indices must be granted separately
+sg_kibana_user:
+ readonly: true
+ cluster:
+ - INDICES_MONITOR
+ - CLUSTER_COMPOSITE_OPS
+ indices:
+ '?kibana':
+ '*':
+ - MANAGE
+ - INDEX
+ - READ
+ - DELETE
+ '?kibana-6':
+ '*':
+ - MANAGE
+ - INDEX
+ - READ
+ - DELETE
+ '?kibana_*':
+ '*':
+ - MANAGE
+ - INDEX
+ - READ
+ - DELETE
+ '?tasks':
+ '*':
+ - INDICES_ALL
+ '?management-beats':
+ '*':
+ - INDICES_ALL
+ '*':
+ '*':
+ - indices:data/read/field_caps*
+ - indices:data/read/xpack/rollup*
+ - indices:admin/mappings/get*
+ - indices:admin/get
+
+# For the kibana server
+sg_kibana_server:
+ readonly: true
+ cluster:
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS
+ - cluster:admin/xpack/monitoring*
+ - indices:admin/template*
+ - indices:data/read/scroll*
+ indices:
+ '?kibana':
+ '*':
+ - INDICES_ALL
+ '?kibana-6':
+ '*':
+ - INDICES_ALL
+ '?kibana_*':
+ '*':
+ - INDICES_ALL
+ '?reporting*':
+ '*':
+ - INDICES_ALL
+ '?monitoring*':
+ '*':
+ - INDICES_ALL
+ '?tasks':
+ '*':
+ - INDICES_ALL
+ '?management-beats*':
+ '*':
+ - INDICES_ALL
+ '*':
+ '*':
+ - "indices:admin/aliases*"
+
+# For logstash and beats
+sg_logstash:
+ cluster:
+ - ES_INPUT
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS
+ - indices:admin/template/get
+ - indices:admin/template/put
+ indices:
+ 'logstash-*':
+ '*':
+ - INDEX_OWNER
+ '*beat*':
+ '*':
+ - INDEX_OWNER
+ 'dmaap*':
+ '*':
+ - INDEX_OWNER
+ 'events*':
+ '*':
+ - INDEX_OWNER
+ 'errors*':
+ '*':
+ - INDEX_OWNER
+
+# Allows adding and modifying repositories and creating and restoring snapshots
+sg_manage_snapshots:
+ cluster:
+ - MANAGE_SNAPSHOTS
+ indices:
+ '*':
+ '*':
+ - "indices:data/write/index"
+ - "indices:admin/create"
+
+# Allows each user to access own named index
+sg_own_index:
+ cluster:
+ - CLUSTER_COMPOSITE_OPS
+ indices:
+ '${user_name}':
+ '*':
+ - INDICES_ALL
+
+### X-Pack COMPATIBILITY
+sg_xp_monitoring:
+ readonly: true
+ cluster:
+ - cluster:monitor/xpack/info
+ - cluster:monitor/main
+ - cluster:admin/xpack/monitoring/bulk
+ indices:
+ '?monitor*':
+ '*':
+ - INDICES_ALL
+
+sg_xp_alerting:
+ readonly: true
+ cluster:
+ - indices:data/read/scroll
+ - cluster:admin/xpack/watcher*
+ - cluster:monitor/xpack/watcher*
+ indices:
+ '?watches*':
+ '*':
+ - INDICES_ALL
+ '?watcher-history-*':
+ '*':
+ - INDICES_ALL
+ '?triggered_watches':
+ '*':
+ - INDICES_ALL
+ '*':
+ '*':
+ - READ
+ - indices:admin/aliases/get
+
+sg_xp_machine_learning:
+ readonly: true
+ cluster:
+ - cluster:admin/persistent*
+ - cluster:internal/xpack/ml*
+ - indices:data/read/scroll*
+ - cluster:admin/xpack/ml*
+ - cluster:monitor/xpack/ml*
+ indices:
+ '*':
+ '*':
+ - READ
+ - indices:admin/get*
+ '?ml-*':
+ '*':
+ - "*"
+
+
+### LEGACY ROLES, FOR COMPATIBILITY ONLY
+### WILL BE REMOVED IN SG7, DO NOT USE ANYMORE
+
+sg_readonly_and_monitor:
+ cluster:
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS_RO
+ indices:
+ '*':
+ '*':
+ - READ
+
+# Make xpack monitoring work
+sg_monitor:
+ cluster:
+ - cluster:admin/xpack/monitoring/*
+ - cluster:admin/ingest/pipeline/put
+ - cluster:admin/ingest/pipeline/get
+ - indices:admin/template/get
+ - indices:admin/template/put
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS
+ indices:
+ '?monitor*':
+ '*':
+ - INDICES_ALL
+ '?marvel*':
+ '*':
+ - INDICES_ALL
+ '?kibana*':
+ '*':
+ - READ
+ '*':
+ '*':
+ - indices:data/read/field_caps
+
+# Make xpack alerting work
+sg_alerting:
+ cluster:
+ - indices:data/read/scroll
+ - cluster:admin/xpack/watcher/watch/put
+ - cluster:admin/xpack/watcher*
+ - CLUSTER_MONITOR
+ - CLUSTER_COMPOSITE_OPS
+ indices:
+ '?kibana*':
+ '*':
+ - READ
+ '?watches*':
+ '*':
+ - INDICES_ALL
+ '?watcher-history-*':
+ '*':
+ - INDICES_ALL
+ '?triggered_watches':
+ '*':
+ - INDICES_ALL
+ '*':
+ '*':
+ - READ
diff --git a/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml b/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml
new file mode 100644
index 000000000..45bb77556
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml
@@ -0,0 +1,34 @@
+# In this file users, backendroles and hosts can be mapped to Search Guard roles.
+# Permissions for Search Guard roles are configured in sg_roles.yml
+
+sg_all_access:
+ readonly: true
+ backendroles:
+ - admin
+
+sg_logstash:
+ backendroles:
+ - logstash
+
+sg_kibana_server:
+ readonly: true
+ users:
+ - kibanaserver
+
+sg_kibana_user:
+ backendroles:
+ - kibanauser
+
+sg_readall:
+ readonly: true
+ backendroles:
+ - readall
+
+sg_manage_snapshots:
+ readonly: true
+ backendroles:
+ - snapshotrestore
+
+sg_own_index:
+ users:
+ - '*' \ No newline at end of file
diff --git a/src/main/docker/elasticsearch/config/sg/truststore.jks b/src/main/docker/elasticsearch/config/sg/truststore.jks
new file mode 100644
index 000000000..7a1b59a8d
--- /dev/null
+++ b/src/main/docker/elasticsearch/config/sg/truststore.jks
Binary files differ
diff --git a/src/main/docker/elasticsearch/my-entrypoint.sh b/src/main/docker/elasticsearch/my-entrypoint.sh
new file mode 100644
index 000000000..9eee64e0c
--- /dev/null
+++ b/src/main/docker/elasticsearch/my-entrypoint.sh
@@ -0,0 +1,8 @@
+source init_sg.sh
+
+while [ $? -ne 0 ]; do
+ sleep 10
+ source init_sg.sh
+done &
+
+/bin/bash -c "source /usr/local/bin/docker-entrypoint.sh;"
diff --git a/src/main/docker/frontend/Dockerfile b/src/main/docker/frontend/Dockerfile
new file mode 100644
index 000000000..8c755b039
--- /dev/null
+++ b/src/main/docker/frontend/Dockerfile
@@ -0,0 +1,52 @@
+###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 2019 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============================================
+# ===================================================================
+#
+###
+
+# build environment
+FROM node:12.4-alpine as build
+WORKDIR /app
+#ENV PATH /app/node_modules/.bin:$PATH
+COPY onap-clamp-frontend/ /app/
+RUN npm install --silent
+RUN npm run build
+
+FROM nginx:1.17.0-alpine
+
+MAINTAINER "The Onap Team"
+LABEL Description="This image contains Clamp frontend"
+
+ARG http_proxy
+ARG https_proxy
+ENV HTTP_PROXY=$http_proxy
+ENV HTTPS_PROXY=$https_proxy
+ENV http_proxy=$HTTP_PROXY
+ENV https_proxy=$HTTPS_PROXY
+
+RUN addgroup onap && adduser -D -G onap clamp
+RUN mkdir /var/log/onap
+RUN chmod a+rwx /var/log/onap
+
+COPY --from=build /app/build /usr/share/nginx/html
+RUN rm /etc/nginx/conf.d/default.conf
+COPY onap-clamp-frontend/nginx/nginx.conf /etc/nginx/conf.d
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file
diff --git a/src/main/docker/frontend/frontend-files.xml b/src/main/docker/frontend/frontend-files.xml
new file mode 100644
index 000000000..2610e828e
--- /dev/null
+++ b/src/main/docker/frontend/frontend-files.xml
@@ -0,0 +1,43 @@
+<!--
+ ============LICENSE_START=======================================================
+ ECOMP CLAMP
+ ================================================================================
+ Copyright (C) 2019 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=========================================================
+ -->
+
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+ <id>clamp-files</id>
+
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <!-- include config files -->
+ <fileSet>
+ <excludes>
+ <exclude>node_modules</exclude>
+ </excludes>
+ <directory>${project.basedir}/ui-react</directory>
+ <outputDirectory>/</outputDirectory>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/src/main/docker/kibana/Dockerfile.kibana b/src/main/docker/kibana/Dockerfile
index 9962fe31a..21e892525 100644
--- a/src/main/docker/kibana/Dockerfile.kibana
+++ b/src/main/docker/kibana/Dockerfile
@@ -23,8 +23,11 @@
FROM docker.elastic.co/kibana/kibana-oss:6.6.2
+# Search Guard plugin
+RUN kibana-plugin install https://repo1.maven.org/maven2/com/floragunn/search-guard-kibana-plugin/6.6.2-18.4/search-guard-kibana-plugin-6.6.2-18.4.zip
+
MAINTAINER "The Onap Team"
-LABEL Description="Kibana image with saved objects loading"
+LABEL Description="Kibana image with saved objects loading and Search Guard support"
USER root
RUN yum install -y python-requests && yum clean all
diff --git a/src/main/docker/kibana/backup.py b/src/main/docker/kibana/backup.py
index a2dab2222..b1e45181b 100755
--- a/src/main/docker/kibana/backup.py
+++ b/src/main/docker/kibana/backup.py
@@ -48,7 +48,7 @@ def parse_args(args):
' matching a configuration item that should be written.'
' Files already in the folder that do not match are'
' left as-is.'))
- parser.add_argument('-H', '--kibana-host', default='http://localhost:5601',
+ parser.add_argument('-H', '--kibana-host', default='https://localhost:5601',
help='Kibana endpoint.')
return parser.parse_args(args)
@@ -68,7 +68,7 @@ def main():
# get list of the set of objects we update
url = "%s/api/saved_objects/_find" % (args.kibana_host.rstrip("/"),)
- saved_objects_req = requests.get(url,
+ saved_objects_req = requests.get(url, auth=('admin', 'admin'), verify=False,
params={'per_page': PER_PAGE,'type':['config','search','dashboard','visualization','index-pattern']})
saved_objects = saved_objects_req.json()['saved_objects']
diff --git a/src/main/docker/kibana/conf/kibana.yml b/src/main/docker/kibana/conf/kibana.yml
index 0c4eda9a1..2edcb803f 100644
--- a/src/main/docker/kibana/conf/kibana.yml
+++ b/src/main/docker/kibana/conf/kibana.yml
@@ -6,4 +6,12 @@ server.host: "0"
elasticsearch.hosts: http://elasticsearch:9200
server.ssl.enabled: true
server.ssl.key: /usr/share/kibana/config/keystore/org.onap.clamp.key.pem
-server.ssl.certificate: /usr/share/kibana/config/keystore/org.onap.clamp.crt.pem \ No newline at end of file
+server.ssl.certificate: /usr/share/kibana/config/keystore/org.onap.clamp.crt.pem
+
+## Search Guard
+#
+xpack.security.enabled: false
+elasticsearch.username: kibanaserver
+elasticsearch.password: kibanaserver
+
+searchguard.cookie.password: 123567818187654rwrwfsfshdhdhtegdhfzftdhncn \ No newline at end of file
diff --git a/src/main/docker/kibana/restore.py b/src/main/docker/kibana/restore.py
index 70f430c57..eee2e6b76 100755
--- a/src/main/docker/kibana/restore.py
+++ b/src/main/docker/kibana/restore.py
@@ -53,7 +53,7 @@ def parse_args(args):
(','.join(OBJECT_TYPES[:-1]), OBJECT_TYPES[-1])
)
)
- parser.add_argument('-H', '--kibana-host', default='http://localhost:5601',
+ parser.add_argument('-H', '--kibana-host', default='https://localhost:5601',
help='Kibana endpoint.')
parser.add_argument('-f', '--force', action='store_const',
const=True, default=False,
@@ -98,7 +98,7 @@ def main():
logger.info('Restoring %s id:%s (overwrite:%s)', obj_type, obj_id, args.force)
url = "%s/api/saved_objects/%s/%s" % (args.kibana_host.rstrip("/"), obj_type, obj_id)
params = {'overwrite': True} if args.force else {}
- post_object_req = requests.post(url,
+ post_object_req = requests.post(url, auth=('admin', 'admin'), verify=False,
headers={'content-type': 'application/json',
'kbn-xsrf': 'True'},
params=params,
diff --git a/src/main/docker/logstash/Dockerfile.logstash b/src/main/docker/logstash/Dockerfile
index f3075099c..73988dc79 100644
--- a/src/main/docker/logstash/Dockerfile.logstash
+++ b/src/main/docker/logstash/Dockerfile
@@ -1,17 +1,25 @@
-# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
-#
+###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 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
+# 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============================================
+# ===================================================================
+#
+###
FROM docker.elastic.co/logstash/logstash-oss:6.6.2
MAINTAINER "The Onap Team"
diff --git a/src/main/docker/logstash/pipeline/logstash.conf b/src/main/docker/logstash/pipeline/logstash.conf
index 6fe9d9691..5c1d47d18 100644
--- a/src/main/docker/logstash/pipeline/logstash.conf
+++ b/src/main/docker/logstash/pipeline/logstash.conf
@@ -238,6 +238,8 @@ output {
elasticsearch {
codec => "json"
hosts => ["${elasticsearch_base_url}"]
+ user => "${LOGSTASH_USR}"
+ password => "${LOGSTASH_PWD}"
index => "errors-%{+YYYY.MM.DD}"
doc_as_upsert => true
}
@@ -246,6 +248,8 @@ output {
elasticsearch {
codec => "json"
hosts => ["${elasticsearch_base_url}"]
+ user => "${LOGSTASH_USR}"
+ password => "${LOGSTASH_PWD}"
document_id => "%{requestID}"
index => "events-cl-%{+YYYY.MM.DD}" # creates daily indexes for control loop
doc_as_upsert => true
@@ -256,6 +260,8 @@ output {
elasticsearch {
codec => "json"
hosts => ["${elasticsearch_base_url}"]
+ user => "${LOGSTASH_USR}"
+ password => "${LOGSTASH_PWD}"
index => "events-raw-%{+YYYY.MM.DD}" # creates daily indexes
doc_as_upsert => true
}
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java b/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java
index 40e547a9c..27a430c7c 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java
@@ -28,25 +28,34 @@ import java.util.List;
import org.onap.clamp.clds.dao.CldsDao;
+import com.google.gson.annotations.Expose;
+
/**
* Represents a CLDS Dictionary.
*/
public class CldsDictionary {
+ @Expose
private String dictionaryId;
+ @Expose
private String dictionaryName;
+
+ @Expose
private String createdBy;
+ @Expose
private String updatedBy;
+ @Expose
private String lastUpdatedDate;
+ @Expose
private List<CldsDictionaryItem> cldsDictionaryItems = new ArrayList<>();
/**
* Creates or updates dictionary item for a dictionary in DB.
*
* @param dictionaryName The dictionary name
- * @param cldsDao The CldsDao
- * @param userId The user ID
+ * @param cldsDao The CldsDao
+ * @param userId The user ID
*/
public void save(String dictionaryName, CldsDao cldsDao, String userId) {
List<CldsDictionary> list = cldsDao.getDictionary(this.getDictionaryId(), dictionaryName);
@@ -70,6 +79,7 @@ public class CldsDictionary {
/**
* Get the dictionary ID.
+ *
* @return the dictionaryId
*/
public String getDictionaryId() {
@@ -78,8 +88,8 @@ public class CldsDictionary {
/**
* Set the dictionary Id.
- * @param dictionaryId
- * the dictionaryId to set
+ *
+ * @param dictionaryId the dictionaryId to set
*/
public void setDictionaryId(String dictionaryId) {
this.dictionaryId = dictionaryId;
@@ -87,6 +97,7 @@ public class CldsDictionary {
/**
* Get the dictionary name.
+ *
* @return the dictionaryName
*/
public String getDictionaryName() {
@@ -95,8 +106,8 @@ public class CldsDictionary {
/**
* Set the dictionary name.
- * @param dictionaryName
- * the dictionaryName to set
+ *
+ * @param dictionaryName the dictionaryName to set
*/
public void setDictionaryName(String dictionaryName) {
this.dictionaryName = dictionaryName;
@@ -104,6 +115,7 @@ public class CldsDictionary {
/**
* Get the createdBy info.
+ *
* @return the createdBy
*/
public String getCreatedBy() {
@@ -112,8 +124,8 @@ public class CldsDictionary {
/**
* Set the createdBy info.
- * @param createdBy
- * the createdBy to set
+ *
+ * @param createdBy the createdBy to set
*/
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
@@ -121,6 +133,7 @@ public class CldsDictionary {
/**
* Get the updatedBy info.
+ *
* @return the updatedBy
*/
public String getUpdatedBy() {
@@ -129,8 +142,8 @@ public class CldsDictionary {
/**
* Set the updatedBy info.
- * @param updatedby
- * the updatedBy to set
+ *
+ * @param updatedby the updatedBy to set
*/
public void setUpdatedBy(String updatedby) {
updatedBy = updatedby;
@@ -138,6 +151,7 @@ public class CldsDictionary {
/**
* Get the last updated date.
+ *
* @return the lastUpdatedDate
*/
public String getLastUpdatedDate() {
@@ -146,8 +160,8 @@ public class CldsDictionary {
/**
* Set the last updated date.
- * @param lastUpdatedDate
- * the lastUpdatedDate to set
+ *
+ * @param lastUpdatedDate the lastUpdatedDate to set
*/
public void setLastUpdatedDate(String lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
@@ -155,6 +169,7 @@ public class CldsDictionary {
/**
* Get all the dictionary items.
+ *
* @return the cldsDictionaryItems
*/
public List<CldsDictionaryItem> getCldsDictionaryItems() {
@@ -163,8 +178,8 @@ public class CldsDictionary {
/**
* Set the whole dictionary items.
- * @param cldsDictionaryItems
- * the cldsDictionaryItems to set
+ *
+ * @param cldsDictionaryItems the cldsDictionaryItems to set
*/
public void setCldsDictionaryItems(List<CldsDictionaryItem> cldsDictionaryItems) {
this.cldsDictionaryItems = cldsDictionaryItems;
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java b/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java
index 871fe9037..14a68502e 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java
@@ -23,6 +23,8 @@
package org.onap.clamp.clds.model;
+import com.google.gson.annotations.Expose;
+
import java.util.List;
import org.onap.clamp.clds.dao.CldsDao;
@@ -32,21 +34,31 @@ import org.onap.clamp.clds.dao.CldsDao;
*/
public class CldsDictionaryItem {
+ @Expose
private String dictElementId;
+ @Expose
private String dictionaryId;
+ @Expose
private String dictElementName;
+ @Expose
private String dictElementShortName;
+ @Expose
private String dictElementDesc;
+ @Expose
private String dictElementType;
+ @Expose
private String createdBy;
+ @Expose
private String updatedBy;
+ @Expose
private String lastUpdatedDate;
/**
* Save the dictionary item.
+ *
* @param dictionaryName The name of the dictionary
- * @param cldsDao The cldsDao
- * @param userId The user id
+ * @param cldsDao The cldsDao
+ * @param userId The user id
*/
public void save(String dictionaryName, CldsDao cldsDao, String userId) {
// Check if dictionary exists.
@@ -55,7 +67,7 @@ public class CldsDictionaryItem {
// Dictionary found. We can add or update the dictionary element
CldsDictionary cldsDictionary = list.stream().findFirst().get();
List<CldsDictionaryItem> dictionaryItems = cldsDao.getDictionaryElements(dictionaryName,
- cldsDictionary.getDictionaryId(), this.getDictElementShortName());
+ cldsDictionary.getDictionaryId(), this.getDictElementShortName());
if (dictionaryItems != null && !dictionaryItems.isEmpty()) {
CldsDictionaryItem item = dictionaryItems.stream().findFirst().get();
cldsDao.updateDictionaryElements(item.getDictElementId(), this, userId);
@@ -71,6 +83,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary element id.
+ *
* @return the dictElementId
*/
public String getDictElementId() {
@@ -79,8 +92,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary element id.
- * @param dictElementId
- * the dictElementId to set
+ *
+ * @param dictElementId the dictElementId to set
*/
public void setDictElementId(String dictElementId) {
this.dictElementId = dictElementId;
@@ -88,6 +101,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary id.
+ *
* @return the dictionaryId
*/
public String getDictionaryId() {
@@ -96,8 +110,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary id.
- * @param dictionaryId
- * the dictionaryId to set
+ *
+ * @param dictionaryId the dictionaryId to set
*/
public void setDictionaryId(String dictionaryId) {
this.dictionaryId = dictionaryId;
@@ -105,6 +119,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary name.
+ *
* @return the dictElementName
*/
public String getDictElementName() {
@@ -113,8 +128,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary name.
- * @param dictElementName
- * the dictElementName to set
+ *
+ * @param dictElementName the dictElementName to set
*/
public void setDictElementName(String dictElementName) {
this.dictElementName = dictElementName;
@@ -122,6 +137,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary element short name.
+ *
* @return the dictElementShortName
*/
public String getDictElementShortName() {
@@ -130,8 +146,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary element short name.
- * @param dictElementShortName
- * the dictElementShortName to set
+ *
+ * @param dictElementShortName the dictElementShortName to set
*/
public void setDictElementShortName(String dictElementShortName) {
this.dictElementShortName = dictElementShortName;
@@ -139,6 +155,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary element description.
+ *
* @return the dictElementDesc
*/
public String getDictElementDesc() {
@@ -147,8 +164,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary element description.
- * @param dictElementDesc
- * the dictElementDesc to set
+ *
+ * @param dictElementDesc the dictElementDesc to set
*/
public void setDictElementDesc(String dictElementDesc) {
this.dictElementDesc = dictElementDesc;
@@ -156,6 +173,7 @@ public class CldsDictionaryItem {
/**
* Get the dictionary element type.
+ *
* @return the dictElementType
*/
public String getDictElementType() {
@@ -164,8 +182,8 @@ public class CldsDictionaryItem {
/**
* Set the dictionary element type.
- * @param dictElementType
- * the dictElementType to set
+ *
+ * @param dictElementType the dictElementType to set
*/
public void setDictElementType(String dictElementType) {
this.dictElementType = dictElementType;
@@ -173,6 +191,7 @@ public class CldsDictionaryItem {
/**
* Get the createdBy info.
+ *
* @return the createdBy
*/
public String getCreatedBy() {
@@ -181,8 +200,8 @@ public class CldsDictionaryItem {
/**
* Set the createdBy info.
- * @param createdBy
- * the createdBy to set
+ *
+ * @param createdBy the createdBy to set
*/
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
@@ -190,6 +209,7 @@ public class CldsDictionaryItem {
/**
* Get the updatedBy info.
+ *
* @return the updatedBy
*/
public String getUpdatedBy() {
@@ -198,8 +218,8 @@ public class CldsDictionaryItem {
/**
* Set the updatedBy info.
- * @param updatedby
- * the updatedBy to set
+ *
+ * @param updatedby the updatedBy to set
*/
public void setUpdatedBy(String updatedby) {
updatedBy = updatedby;
@@ -207,6 +227,7 @@ public class CldsDictionaryItem {
/**
* Get the last updated date.
+ *
* @return the lastUpdatedDate
*/
public String getLastUpdatedDate() {
@@ -215,8 +236,8 @@ public class CldsDictionaryItem {
/**
* Set the last updated date.
- * @param lastUpdatedDate
- * the lastUpdatedDate to set
+ *
+ * @param lastUpdatedDate the lastUpdatedDate to set
*/
public void setLastUpdatedDate(String lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsInfo.java b/src/main/java/org/onap/clamp/clds/model/CldsInfo.java
index a24885f73..f3cf6ed19 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsInfo.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsInfo.java
@@ -5,33 +5,42 @@
* Copyright (C) 2017 AT&T Intellectual Property. All rights
* reserved.
* ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
+ * 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
+ *
+ * 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.model;
-public class CldsInfo {
+import com.google.gson.annotations.Expose;
+public class CldsInfo {
+ @Expose
private String userName;
+ @Expose
private String cldsVersion;
+ @Expose
private boolean permissionReadCl;
+ @Expose
private boolean permissionUpdateCl;
+ @Expose
private boolean permissionReadTemplate;
+ @Expose
private boolean permissionUpdateTemplate;
+ @Expose
private boolean permissionReadTosca;
+ @Expose
private boolean permissionUpdateTosca;
public String getUserName() {
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsModel.java b/src/main/java/org/onap/clamp/clds/model/CldsModel.java
index 223d38929..4d2ca89a4 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsModel.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsModel.java
@@ -23,8 +23,7 @@
package org.onap.clamp.clds.model;
-import com.att.eelf.configuration.EELFLogger;
-import com.att.eelf.configuration.EELFManager;
+import com.google.gson.annotations.Expose;
import java.util.ArrayList;
import java.util.List;
@@ -43,7 +42,6 @@ import org.onap.clamp.clds.model.status.StatusHandlerImpl;
*/
public class CldsModel {
- private static final EELFLogger logger = EELFManager.getInstance().getLogger(CldsModel.class);
private static final int UUID_LENGTH = 36;
/**
* The constant STATUS_DESIGN.
@@ -73,29 +71,50 @@ public class CldsModel {
* The constant STATUS_UNKNOWN.
*/
public static final String STATUS_UNKNOWN = "UNKNOWN";
+
+ @Expose
private String id;
+ @Expose
private String templateId;
+ @Expose
private String templateName;
+ @Expose
private String name;
+ @Expose
private String controlNamePrefix;
+ @Expose
private String controlNameUuid;
+ @Expose
private String bpmnText;
+ @Expose
private String propText;
+ @Expose
private String imageText;
+ @Expose
private String docText;
+ @Expose
private String blueprintText;
+ @Expose
private CldsEvent event;
+ @Expose
private String status;
+ @Expose
private List<String> permittedActionCd;
+ @Expose
private List<CldsModelInstance> cldsModelInstanceList;
// This is a transient value used to return the failure message to UI
+ @Expose
private String errorMessageForUi;
/**
* The service type Id received from DCAE by querying it.
*/
+ @Expose
private String typeId;
+ @Expose
private String typeName;
+ @Expose
private String deploymentId;
+ @Expose
private String deploymentStatusUrl;
// Set default handlers but this can be changed if needed.
@@ -155,7 +174,7 @@ public class CldsModel {
boolean canCall = false;
/* Below checks the clds event is submit/resubmit/distribute */
if (event.isActionCd(CldsEvent.ACTION_SUBMIT) || event.isActionCd(CldsEvent.ACTION_RESUBMIT)
- || event.isActionCd(CldsEvent.ACTION_DISTRIBUTE) || event.isActionCd(CldsEvent.ACTION_SUBMITDCAE)) {
+ || event.isActionCd(CldsEvent.ACTION_DISTRIBUTE) || event.isActionCd(CldsEvent.ACTION_SUBMITDCAE)) {
canCall = true;
}
return canCall;
@@ -202,9 +221,9 @@ public class CldsModel {
public void validateAction(String requestedActionCd) {
determinePermittedActionCd();
if (!permittedActionCd.contains(requestedActionCd)) {
- throw new IllegalArgumentException(
- "Invalid requestedActionCd: " + requestedActionCd + ". Given current actionCd: "
- + actionsHandler.getCurrentActionCd(event) + ", the permittedActionCd: " + permittedActionCd);
+ throw new IllegalArgumentException("Invalid requestedActionCd: " + requestedActionCd
+ + ". Given current actionCd: " + actionsHandler.getCurrentActionCd(event)
+ + ", the permittedActionCd: " + permittedActionCd);
}
}
@@ -220,8 +239,8 @@ public class CldsModel {
public static CldsModel createUsingControlName(String fullControlName) {
if (fullControlName == null || fullControlName.length() < UUID_LENGTH) {
throw new BadRequestException(
- "closed loop id / control name length, " + (fullControlName != null ? fullControlName.length() : 0)
- + ", less than the minimum of: " + UUID_LENGTH);
+ "closed loop id / control name length, " + (fullControlName != null ? fullControlName.length() : 0)
+ + ", less than the minimum of: " + UUID_LENGTH);
}
CldsModel model = new CldsModel();
model.setControlNamePrefix(fullControlName.substring(0, fullControlName.length() - UUID_LENGTH));
@@ -251,11 +270,11 @@ public class CldsModel {
CldsModel cldsModel = createUsingControlName(controlName);
cldsModel = cldsDao.getModelByUuid(cldsModel.getControlNameUuid());
cldsModel.determineStatus();
- if (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_UNDEPLOY)
- || (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_DEPLOY)
+ if (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_UNDEPLOY) || (dcaeEvent.getCldsActionCd()
+ .equals(CldsEvent.ACTION_DEPLOY)
&& (cldsModel.getStatus().equals(STATUS_DISTRIBUTED) || cldsModel.getStatus().equals(STATUS_DESIGN)))) {
CldsEvent.insEvent(cldsDao, dcaeEvent.getControlName(), userid, dcaeEvent.getCldsActionCd(),
- CldsEvent.ACTION_STATE_RECEIVED, null);
+ CldsEvent.ACTION_STATE_RECEIVED, null);
}
cldsDao.insModelInstance(cldsModel, dcaeEvent.getInstances());
return cldsModel;
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java b/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java
index 45194deca..8777ab5c9 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java
@@ -18,7 +18,7 @@
* limitations under the License.
* ============LICENSE_END============================================
* ===================================================================
- *
+ *
*/
package org.onap.clamp.clds.model;
@@ -77,8 +77,8 @@ public class CldsServiceData implements Serializable {
}
/**
- * Filter out any VFs that the user is not authorized for. Use the
- * CldsService to determine if the user is authorized for a VF.
+ * Filter out any VFs that the user is not authorized for. Use the CldsService
+ * to determine if the user is authorized for a VF.
*
* @param svc The clds service
*/
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java b/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java
index 8f3408375..b1247801b 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java
@@ -18,11 +18,13 @@
* limitations under the License.
* ============LICENSE_END============================================
* ===================================================================
- *
+ *
*/
package org.onap.clamp.clds.model;
+import com.google.gson.annotations.Expose;
+
import javax.ws.rs.NotFoundException;
import org.onap.clamp.clds.dao.CldsDao;
@@ -32,35 +34,48 @@ import org.onap.clamp.clds.dao.CldsDao;
*/
public class CldsTemplate {
- public static final String STATUS_DESIGN = "DESIGN";
- public static final String STATUS_ACTIVE = "ACTIVE";
- public static final String STATUS_STOPPED = "STOPPED";
+ public static final String STATUS_DESIGN = "DESIGN";
+ public static final String STATUS_ACTIVE = "ACTIVE";
+ public static final String STATUS_STOPPED = "STOPPED";
public static final String STATUS_DELETING = "DELETING";
// manual intervention required
- public static final String STATUS_ERROR = "ERROR";
- public static final String STATUS_UNKNOWN = "UNKNOWN";
-
- private String id;
- private String name;
- private String controlNamePrefix;
- private String controlNameUuid;
- private String bpmnId;
- private String bpmnUserid;
- private String bpmnText;
- private String imageId;
- private String imageUserid;
- private String imageText;
- private String propId;
- private String propUserid;
- private String propText;
-
- private boolean userAuthorizedToUpdate;
+ public static final String STATUS_ERROR = "ERROR";
+ public static final String STATUS_UNKNOWN = "UNKNOWN";
+
+ @Expose
+ private String id;
+ @Expose
+ private String name;
+ @Expose
+ private String controlNamePrefix;
+ @Expose
+ private String controlNameUuid;
+ @Expose
+ private String bpmnId;
+ @Expose
+ private String bpmnUserid;
+ @Expose
+ private String bpmnText;
+ @Expose
+ private String imageId;
+ @Expose
+ private String imageUserid;
+ @Expose
+ private String imageText;
+ @Expose
+ private String propId;
+ @Expose
+ private String propUserid;
+ @Expose
+ private String propText;
+ @Expose
+ private boolean userAuthorizedToUpdate;
/**
* Save template to DB.
*
* @param cldsDao The cldsDao
- * @param userid The user Id
+ * @param userid The user Id
*/
public void save(CldsDao cldsDao, String userid) {
cldsDao.setTemplate(this, userid);
@@ -69,10 +84,10 @@ public class CldsTemplate {
/**
* Retrieve from DB.
*
- * @param cldsDao The cldsDao
- * @param name The template name to retrieve
- * @param okIfNotFound
- * The flag indicating whether exception will be returned in case nothing is found
+ * @param cldsDao The cldsDao
+ * @param name The template name to retrieve
+ * @param okIfNotFound The flag indicating whether exception will be returned in
+ * case nothing is found
* @return Clds template from DB
*/
public static CldsTemplate retrieve(CldsDao cldsDao, String name, boolean okIfNotFound) {
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java b/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java
index 961e1e328..5ae13d20a 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java
@@ -23,6 +23,8 @@
package org.onap.clamp.clds.model;
+import com.google.gson.annotations.Expose;
+
import java.util.List;
import org.apache.commons.lang3.StringUtils;
@@ -33,8 +35,11 @@ import org.onap.clamp.clds.tosca.ToscaYamlToJsonConvertor;
public class CldsToscaModel extends CldsToscaModelRevision {
+ @Expose
private String id;
+ @Expose
private String policyType;
+ @Expose
private String toscaModelName;
/**
@@ -47,7 +52,7 @@ public class CldsToscaModel extends CldsToscaModelRevision {
* Creates or updates Tosca Model to DB.
*
* @param cldsDao The cldsDao
- * @param userId The user Id
+ * @param userId The user Id
*/
public CldsToscaModel save(CldsDao cldsDao, ClampProperties refProp, PolicyClient policyClient, String userId) {
CldsToscaModel cldsToscaModel = null;
@@ -85,6 +90,7 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Get the Id.
+ *
* @return the id
*/
public String getId() {
@@ -93,8 +99,8 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Set the id.
- * @param id
- * the id to set
+ *
+ * @param id the id to set
*/
public void setId(String id) {
this.id = id;
@@ -102,6 +108,7 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Get the policy type.
+ *
* @return the policyType
*/
public String getPolicyType() {
@@ -110,8 +117,8 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Set the policy type.
- * @param policyType
- * the policyType to set
+ *
+ * @param policyType the policyType to set
*/
public void setPolicyType(String policyType) {
this.policyType = policyType;
@@ -119,6 +126,7 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Get the tosca model name.
+ *
* @return the toscaModelName
*/
public String getToscaModelName() {
@@ -127,8 +135,8 @@ public class CldsToscaModel extends CldsToscaModelRevision {
/**
* Set the tosca model name.
- * @param toscaModelName
- * the toscaModelName to set
+ *
+ * @param toscaModelName the toscaModelName to set
*/
public void setToscaModelName(String toscaModelName) {
this.toscaModelName = toscaModelName;
diff --git a/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java b/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java
index 9448cefcd..034fc28ce 100644
--- a/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java
+++ b/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java
@@ -5,20 +5,20 @@
* Copyright (C) 2017 AT&T Intellectual Property. All rights
* reserved.
* ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
+ * 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
+ *
+ * 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.model;
@@ -28,9 +28,9 @@ import java.util.List;
public class CldsVfcData implements Serializable {
- private static final long serialVersionUID = 406993878174183557L;
- private String vfcName;
- private String vfcInvariantResourceUuid;
+ private static final long serialVersionUID = 406993878174183557L;
+ private String vfcName;
+ private String vfcInvariantResourceUuid;
private List<CldsAlarmCondition> cldsAlarmConditions;
public String getVfcName() {
diff --git a/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java b/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java
index a6ad10b72..9126f9abf 100644
--- a/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java
+++ b/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java
@@ -23,6 +23,8 @@
package org.onap.clamp.clds.model;
+import com.google.gson.annotations.Expose;
+
import java.util.List;
import javax.ws.rs.BadRequestException;
@@ -38,10 +40,15 @@ public class DcaeEvent {
public static final String EVENT_UNDEPLOYMENT = "undeployment";
public static final String ARTIFACT_NAME_SUFFIX = ".yml";
+ @Expose
private String event;
+ @Expose
private String serviceUuid;
+ @Expose
private String resourceUuid;
+ @Expose
private String artifactName; // controlName.yml
+ @Expose
private List<CldsModelInstance> instances;
/**
@@ -83,12 +90,13 @@ public class DcaeEvent {
return artifactName.substring(0, artifactName.length() - ARTIFACT_NAME_SUFFIX.length());
} else {
throw new BadRequestException("artifactName value not valid (expecting it to end with "
- + ARTIFACT_NAME_SUFFIX + "): " + artifactName);
+ + ARTIFACT_NAME_SUFFIX + "): " + artifactName);
}
}
/**
* Get the event.
+ *
* @return the event
*/
public String getEvent() {
@@ -97,8 +105,8 @@ public class DcaeEvent {
/**
* Set the event.
- * @param event
- * the event to set
+ *
+ * @param event the event to set
*/
public void setEvent(String event) {
this.event = event;
@@ -106,6 +114,7 @@ public class DcaeEvent {
/**
* Get the serviceUUID.
+ *
* @return the serviceUUID
*/
public String getServiceUUID() {
@@ -114,8 +123,8 @@ public class DcaeEvent {
/**
* Set the serviceUUID.
- * @param serviceUUID
- * the serviceUUID to set
+ *
+ * @param serviceUUID the serviceUUID to set
*/
public void setServiceUUID(String serviceUuid) {
this.serviceUuid = serviceUuid;
@@ -123,6 +132,7 @@ public class DcaeEvent {
/**
* Get the resourceUUID.
+ *
* @return the resourceUUID
*/
public String getResourceUUID() {
@@ -131,8 +141,8 @@ public class DcaeEvent {
/**
* Set the resourceUUID.
- * @param resourceUUID
- * the resourceUUID to set
+ *
+ * @param resourceUUID the resourceUUID to set
*/
public void setResourceUUID(String resourceUuid) {
this.resourceUuid = resourceUuid;
@@ -140,6 +150,7 @@ public class DcaeEvent {
/**
* Get the artifact name.
+ *
* @return the artifactName
*/
public String getArtifactName() {
@@ -148,8 +159,8 @@ public class DcaeEvent {
/**
* Set the artifact name.
- * @param artifactName
- * the artifactName to set
+ *
+ * @param artifactName the artifactName to set
*/
public void setArtifactName(String artifactName) {
this.artifactName = artifactName;
@@ -157,6 +168,7 @@ public class DcaeEvent {
/**
* Get the list of instances.
+ *
* @return The list of model instances
*/
public List<CldsModelInstance> getInstances() {
@@ -165,6 +177,7 @@ public class DcaeEvent {
/**
* Set the list of model instances.
+ *
* @param instances The list of model instances to set
*/
public void setInstances(List<CldsModelInstance> instances) {
diff --git a/src/main/java/org/onap/clamp/clds/model/ValueItem.java b/src/main/java/org/onap/clamp/clds/model/ValueItem.java
index 74b369f79..42323908c 100644
--- a/src/main/java/org/onap/clamp/clds/model/ValueItem.java
+++ b/src/main/java/org/onap/clamp/clds/model/ValueItem.java
@@ -5,28 +5,31 @@
* Copyright (C) 2017 AT&T Intellectual Property. All rights
* reserved.
* ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
+ * 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
+ *
+ * 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.model;
+import com.google.gson.annotations.Expose;
+
/**
* ValueItem used for value lists.
*/
public class ValueItem {
+ @Expose
private String value;
/**
@@ -43,6 +46,7 @@ public class ValueItem {
/**
* Get the value.
+ *
* @return the value
*/
public String getValue() {
@@ -51,8 +55,8 @@ public class ValueItem {
/**
* Set the value.
- * @param value
- * the value to set
+ *
+ * @param value the value to set
*/
public void setValue(String value) {
this.value = value;
diff --git a/src/main/java/org/onap/clamp/loop/LoopController.java b/src/main/java/org/onap/clamp/loop/LoopController.java
index b862780de..7d41e489a 100644
--- a/src/main/java/org/onap/clamp/loop/LoopController.java
+++ b/src/main/java/org/onap/clamp/loop/LoopController.java
@@ -116,12 +116,13 @@ public class LoopController {
/**
* Get the SVG representation of the loop
- *
+ *
* @param loopName
* The loop name
* @return The SVG representation
*/
public String getSVGRepresentation(String loopName) {
- return loopService.getLoop(loopName).getSvgRepresentation();
+ Loop loop = loopService.getLoop(loopName);
+ return loop != null ? loop.getSvgRepresentation() : null;
}
}
diff --git a/src/main/resources/boot-message.txt b/src/main/resources/boot-message.txt
index 92e4ab029..46b0a6faa 100644
--- a/src/main/resources/boot-message.txt
+++ b/src/main/resources/boot-message.txt
@@ -1,14 +1,15 @@
- _____ _ _ __ ____ ____ __ __ ____ __ ____ _ _
-( _ )( \( ) /__\ ( _ \ ( _ \( )( )( _ \( ) (_ _)( \( )
- )(_)( ) ( /(__)\ )___/ )(_) ))(__)( ) _ < )(__ _)(_ ) (
-(_____)(_)\_)(__)(__)(__) (____/(______)(____/(____)(____)(_)\_)
- ___ __ __ __ __ ____
- / __)( ) /__\ ( \/ )( _ \
- ( (__ )(__ /(__)\ ) ( )___/
- \___)(____)(__)(__)(_/\/\_)(__)
-
+ ▐ ▄ ▄▄▄· ▄▄▄· ▄▄▄ .▄▄▌ ▄▄▄· ▄▄▌ ▄▄▄▄▄
+▪ •█▌▐█▐█ ▀█ ▐█ ▄█ ▀▄.▀·██• ▐█ ▀█ ██• •██ ▪
+ ▄█▀▄ ▐█▐▐▌▄█▀▀█ ██▀· ▐▀▀▪▄██▪ ▄█▀▀█ ██▪ ▐█.▪ ▄█▀▄
+▐█▌.▐▌██▐█▌▐█ ▪▐▌▐█▪·• ▐█▄▄▌▐█▌▐▌ ▐█ ▪▐▌▐█▌▐▌▐█▌·▐█▌.▐▌
+ ▀█▄▀▪▀▀ █▪ ▀ ▀ .▀ ▀▀▀ .▀▀▀ ▀ ▀ .▀▀▀ ▀▀▀ ▀█▄▀▪
+ ▄▄· ▄▄▌ ▄▄▄· • ▌ ▄ ·. ▄▄▄·
+ ▐█ ▌▪██• ▐█ ▀█ ·██ ▐███▪▐█ ▄█
+ ██ ▄▄██▪ ▄█▀▀█ ▐█ ▌▐▌▐█· ██▀·
+ ▐███▌▐█▌▐▌▐█ ▪▐▌██ ██▌▐█▌▐█▪·•
+ ·▀▀▀ .▀▀▀ ▀ ▀ ▀▀ █▪▀▀▀.▀
:: Starting :: \ No newline at end of file
diff --git a/ui-react/nginx/nginx.conf b/ui-react/nginx/nginx.conf
new file mode 100644
index 000000000..758a646e5
--- /dev/null
+++ b/ui-react/nginx/nginx.conf
@@ -0,0 +1,17 @@
+server {
+
+ listen 80;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+
+} \ No newline at end of file
diff --git a/ui-react/package.json b/ui-react/package.json
new file mode 100644
index 000000000..de7cb26d1
--- /dev/null
+++ b/ui-react/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "clamp-ui",
+ "version": "0.0.1",
+ "description": "ONAP Clamp Designer UI",
+ "main": "index.js",
+ "proxy": "http://localhost:8080",
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "author": "ONAP Clamp Team",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@json-editor/json-editor": "1.3.5",
+ "react": "16.8.0",
+ "react-dom": "16.8.0",
+ "react-scripts": "3.0.1",
+ "react-bootstrap": "1.0.0-beta.9",
+ "bootstrap-css-only": "4.3.1",
+ "styled-components": "4.3.2",
+ "react-router-dom": "5.0.1",
+ "react-select": "3.0.4"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ],
+ "devDependencies": {
+ "enzyme": "3.10.0",
+ "enzyme-adapter-react-16": "1.14.0"
+ }
+}
diff --git a/ui-react/public/index.html b/ui-react/public/index.html
new file mode 100644
index 000000000..2b740fea8
--- /dev/null
+++ b/ui-react/public/index.html
@@ -0,0 +1,40 @@
+<!--
+ ============LICENSE_START=======================================================
+ ONAP CLAMP
+ ================================================================================
+ Copyright (C) 2019 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============================================
+ ===================================================================
+
+ -->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport"
+ content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<meta name="theme-color" content="#000000">
+<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+<link rel="shortcut icon" href="%PUBLIC_URL%/onap.ico">
+
+<title>Clamp Designer UI</title>
+</head>
+<body>
+ <noscript>You need to enable JavaScript to run this app.</noscript>
+ <div id="root"></div>
+</body>
+</html>
diff --git a/ui-react/public/manifest.json b/ui-react/public/manifest.json
new file mode 100644
index 000000000..8210c4ee5
--- /dev/null
+++ b/ui-react/public/manifest.json
@@ -0,0 +1,15 @@
+{
+ "short_name": "Clamp Designer UI",
+ "name": "Clamp Designer UI",
+ "icons": [
+ {
+ "src": "onap.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ }
+ ],
+ "start_url": "./index.html",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/ui-react/public/onap.ico b/ui-react/public/onap.ico
new file mode 100644
index 000000000..85e168ae2
--- /dev/null
+++ b/ui-react/public/onap.ico
Binary files differ
diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js
new file mode 100644
index 000000000..c7080a2b6
--- /dev/null
+++ b/ui-react/src/LoopUI.js
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react';
+import styled from 'styled-components';
+import MenuBar from './components/menu/MenuBar';
+import Navbar from 'react-bootstrap/Navbar';
+import logo from './logo.png';
+import { GlobalClampStyle } from './theme/globalStyle.js';
+
+import LoopSvg from './components/loop_viewer/svg/LoopSvg';
+import LoopLogs from './components/loop_viewer/logs/LoopLogs';
+import LoopStatus from './components/loop_viewer/status/LoopStatus';
+import UserService from './api/UserService';
+import LoopCache from './api/LoopCache';
+
+import { Route, Redirect } from 'react-router-dom'
+import OpenLoopModal from './components/dialogs/OpenLoop/OpenLoopModal';
+import OperationalPolicyModal from './components/dialogs/OperationalPolicy/OperationalPolicyModal';
+import ConfigurationPolicyModal from './components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal';
+import LoopProperties from './components/dialogs/LoopProperties';
+import UserInfo from './components/dialogs/UserInfo';
+import LoopService from './api/LoopService';
+import PerformAction from './components/menu/PerformActions';
+import RefreshStatus from './components/menu/RefreshStatus';
+
+const ProjectNameStyled = styled.a`
+ vertical-align: middle;
+ padding-left: 30px;
+ font-size: 30px;
+
+`
+const LoopViewDivStyled = styled.div`
+ height: 100%;
+ overflow: hidden;
+ margin-left: 10px;
+ margin-right: 10px;
+ margin-bottom: 10px;
+ color: ${props => props.theme.loopViewerFontColor};
+ background-color: ${props => props.theme.loopViewerBackgroundColor};
+ border: 1px solid transparent;
+ border-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+`
+
+const LoopViewHeaderDivStyled = styled.div`
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ padding: 10px 10px;
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+`
+
+const LoopViewBodyDivStyled = styled.div`
+ background-color: ${props => (props.theme.loopViewerBackgroundColor)};
+ padding: 10px 10px;
+ color: ${props => (props.theme.loopViewerHeaderFontColor)};
+ height: 95%;
+`
+
+export default class LoopUI extends React.Component {
+
+ static defaultLoopName="Empty (NO loop loaded yet)";
+
+ state = {
+ userName: null,
+ loopName: LoopUI.defaultLoopName,
+ loopCache: new LoopCache({})
+ };
+
+ constructor() {
+ super();
+ this.getUser = this.getUser.bind(this);
+ this.updateLoopCache = this.updateLoopCache.bind(this);
+ this.loadLoop = this.loadLoop.bind(this);
+ }
+
+ componentWillMount() {
+ this.getUser();
+ }
+
+ getUser() {
+ UserService.login().then(user => {
+ this.setState({ userName: user })
+ });
+ }
+
+ renderMenuNavBar() {
+ return (
+ <MenuBar/>
+ );
+ }
+
+ renderUserLoggedNavBar() {
+ return (
+ <Navbar.Text>
+ Signed in as: <a href="/login">{this.state.userName}</a>
+ </Navbar.Text>
+ );
+ }
+
+ renderLogoNavBar() {
+ return (
+ <Navbar.Brand>
+ <img height="50px" width="234px" src={logo} alt="" />
+ <ProjectNameStyled>CLAMP</ProjectNameStyled>
+ </Navbar.Brand>
+ );
+ }
+
+ renderNavBar() {
+ return (
+ <Navbar expand="lg">
+ {this.renderLogoNavBar()}
+ {this.renderMenuNavBar()}
+ {this.renderUserLoggedNavBar()}
+ </Navbar>
+ );
+ }
+
+ renderLoopViewHeader() {
+ return (
+ <LoopViewHeaderDivStyled>
+ Loop Viewer - {this.state.loopName}
+ </LoopViewHeaderDivStyled>
+ );
+ }
+
+ renderLoopViewBody() {
+ return (
+ <LoopViewBodyDivStyled>
+ <LoopSvg loopCache={this.state.loopCache} />
+ <LoopStatus loopCache={this.state.loopCache}/>
+ <LoopLogs loopCache={this.state.loopCache} />
+ </LoopViewBodyDivStyled>
+ );
+ }
+
+ getLoopCache() {
+ return this.state.loopCache;
+
+ }
+
+ renderLoopViewer() {
+ return (
+ <LoopViewDivStyled>
+ {this.renderLoopViewHeader()}
+ {this.renderLoopViewBody()}
+ </LoopViewDivStyled>
+ );
+ }
+
+ updateLoopCache(loopJson) {
+ this.setState({ loopCache: new LoopCache(loopJson) });
+ this.setState({ loopName: this.state.loopCache.getLoopName() });
+ console.info(this.state.loopName+" loop loaded successfully");
+ }
+
+ loadLoop(loopName) {
+ LoopService.getLoop(loopName).then(loop => {
+ console.debug("Updating loopCache");
+ this.updateLoopCache(loop);
+ });
+ }
+
+ render() {
+ return (
+ <div id="main_div">
+ <Route path="/operationalPolicyModal"
+ render={(routeProps) => (<OperationalPolicyModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
+ <Route path="/configurationPolicyModal/:componentName" render={(routeProps) => (<ConfigurationPolicyModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
+ <Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps} loadLoopFunction={this.loadLoop} />)} />
+ <Route path="/loopProperties" render={(routeProps) => (<LoopProperties {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} />
+ <Route path="/userInfo" render={(routeProps) => (<UserInfo {...routeProps} />)} />
+ <Route path="/closeLoop" render={(routeProps) => (<Redirect to='/'/>)} />
+ <Route path="/submit" render={(routeProps) => (<PerformAction {...routeProps} loopAction="submit" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <Route path="/stop" render={(routeProps) => (<PerformAction {...routeProps} loopAction="stop" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <Route path="/restart" render={(routeProps) => (<PerformAction {...routeProps} loopAction="restart" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <Route path="/delete" render={(routeProps) => (<PerformAction {...routeProps} loopAction="delete" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <Route path="/undeploy" render={(routeProps) => (<PerformAction {...routeProps} loopAction="undeploy" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <Route path="/refreshStatus" render={(routeProps) => (<RefreshStatus {...routeProps} loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache}/>)} />
+ <GlobalClampStyle />
+ {this.renderNavBar()}
+ {this.renderLoopViewer()}
+ </div>
+ );
+ }
+}
diff --git a/ui-react/src/NotFound.js b/ui-react/src/NotFound.js
new file mode 100644
index 000000000..d4b53fd71
--- /dev/null
+++ b/ui-react/src/NotFound.js
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react'
+
+
+export default class NotFound extends React.Component {
+ render () {
+ return (
+ <div id='main'>
+ <div class="divRow"><b>Page Not Found!</b></div>
+ <div class="divRow">Please cick <a href="/">here</a> to go back to the main page.</div>
+ </div>
+
+ );
+ }
+}
diff --git a/ui-react/src/OnapClamp.js b/ui-react/src/OnapClamp.js
new file mode 100644
index 000000000..506f6e09d
--- /dev/null
+++ b/ui-react/src/OnapClamp.js
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react';
+import LoopUI from './LoopUI'
+import { ThemeProvider } from 'styled-components';
+import { DefaultClampTheme } from './theme/globalStyle.js';
+
+export default class OnapClamp extends LoopUI {
+
+ render() {
+ console.info("Onap Clamp UI starting");
+ return (
+ <ThemeProvider theme={DefaultClampTheme}>
+ {super.render()}
+ </ThemeProvider>);
+ }
+}
+
diff --git a/ui-react/src/React-Spinner.jpg b/ui-react/src/React-Spinner.jpg
new file mode 100644
index 000000000..227689764
--- /dev/null
+++ b/ui-react/src/React-Spinner.jpg
Binary files differ
diff --git a/ui-react/src/__test__/LoopCache.test.js b/ui-react/src/__test__/LoopCache.test.js
new file mode 100644
index 000000000..e5b50259d
--- /dev/null
+++ b/ui-react/src/__test__/LoopCache.test.js
@@ -0,0 +1,217 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import LoopCache from '../api/LoopCache';
+
+const json = require('./LoopCache_mokeLoopJsonCache.json');
+
+describe('Verify LoopCache functions', () => {
+ const loopCache = new LoopCache(json);
+ it('getLoopName', () => {
+ expect(loopCache.getLoopName()).toBe("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+ });
+
+ it('getOperationalPolicyConfigurationJson', () => {
+ const opPolicyConfig = {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ };
+ expect(loopCache.getOperationalPolicyConfigurationJson()).toStrictEqual(opPolicyConfig);
+ });
+
+ it('getOperationalPolicies', () => {
+ const opPolicy = [{
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+
+ }
+ }
+ }];
+ expect(loopCache.getOperationalPolicies()).toStrictEqual(opPolicy);
+ });
+
+ it('getGlobalProperties', () => {
+ const globelProp = {
+ "dcaeDeployParameters": {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }
+ };
+ expect(loopCache.getGlobalProperties()).toStrictEqual(globelProp);
+ });
+
+ it('getDcaeDeploymentProperties', () => {
+ const deploymentProp = {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ };
+ expect(loopCache.getDcaeDeploymentProperties()).toStrictEqual(deploymentProp);
+ });
+
+ it('getMicroServicesJsonForType', () => {
+ const msJson = {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScaling"},
+ "shared": false,
+ "jsonRepresentation": {"schema": {}}
+ };
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJson);
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+ });
+
+ it('getMicroServiceProperties', () => {
+ const msProp = {"domain": "measurementsForVfScaling"};
+ expect(loopCache.getMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msProp);
+ expect(loopCache.getMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+ });
+
+ it('getMicroServiceJsonRepresentationForType', () => {
+ const msJsonRepresentation = {"schema": {}};
+ expect(loopCache.getMicroServiceJsonRepresentationForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJsonRepresentation);
+ });
+
+ it('getMicroServiceJsonRepresentationForType', () => {
+ const msJsonRepresentation = {"schema": {}};
+ expect(loopCache.getMicroServiceJsonRepresentationForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJsonRepresentation);
+ });
+
+ it('getResourceDetailsVfProperty', () => {
+ const resourceVF = {
+ "vLoadBalancerMS 0": {
+ "resourceVendor": "Test",
+ "resourceVendorModelNumber": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+ "subcategory": "Load Balancer",
+ "category": "Application L4+",
+ "type": "VF",
+ "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+ "version": "1.0",
+ "resourceVendorRelease": "1.0",
+ "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+ }
+ };
+ expect(loopCache.getResourceDetailsVfProperty()).toStrictEqual(resourceVF);
+ });
+
+ it('getResourceDetailsVfModuleProperty', () => {
+ const vfModule = {
+ "Vloadbalancerms..vpkg..module-1": {
+ "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+ "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+ "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vpkg",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ }
+ };
+ expect(loopCache.getResourceDetailsVfModuleProperty()).toStrictEqual(vfModule);
+ });
+
+ it('getLoopLogsArray', () => {
+ const logs = [
+ {
+ "id": 1,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Operational and Guard policies UPDATED",
+ "logInstant": "2019-07-08T09:44:37Z"
+ }
+ ];
+ expect(loopCache.getLoopLogsArray()).toStrictEqual(logs);
+ });
+
+ it('getComponentStates', () => {
+ const component = {
+ "POLICY": {
+ "componentState": {
+ "stateName": "NOT_SENT",
+ "description": "The policies defined have NOT yet been created on the policy engine"
+ }
+ },
+ "DCAE": {
+ "componentState": {
+ "stateName": "BLUEPRINT_DEPLOYED",
+ "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+ }
+ }
+ };
+ expect(loopCache.getComponentStates()).toStrictEqual(component);
+ });
+
+ it('updateGlobalProperties', () => {
+ const newGlobalProps = {
+ "dcaeDeployParameters": {
+ "location_id": "newLocation",
+ "service_id": "newServiceId",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2"
+ }
+ };
+ loopCache.updateGlobalProperties(newGlobalProps);
+ expect(loopCache.getGlobalProperties()).toStrictEqual(newGlobalProps);
+ });
+
+ it('updateOperationalPolicyProperties', () => {
+ const newOpPolicy = [{
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca_new",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ }
+ }];
+ loopCache.updateOperationalPolicyProperties(newOpPolicy);
+ expect(loopCache.getOperationalPolicies()).toStrictEqual(newOpPolicy);
+ });
+
+ it('updateMicroServiceProperties', () => {
+ const newMsPolicy = {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScalingNew"},
+ "shared": true,
+ "jsonRepresentation": {"schema": {}}
+ };;
+ loopCache.updateMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca", newMsPolicy);
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(newMsPolicy);
+ });
+ });
diff --git a/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json b/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json
new file mode 100644
index 000000000..184eaf7cd
--- /dev/null
+++ b/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json
@@ -0,0 +1,117 @@
+{
+ "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+ "dcaeBlueprintId": "typeId-3a942643-a8f7-4e54-b2c1-eea8daba2b17",
+ "globalPropertiesJson": {
+ "dcaeDeployParameters": {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }
+ },
+ "modelPropertiesJson": {
+ "serviceDetails": {
+ "serviceType": "",
+ "namingPolicy": "",
+ "environmentContext": "General_Revenue-Bearing",
+ "serviceEcompNaming": "true",
+ "serviceRole": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "30ec5b59-4799-48d8-ac5f-1058a6b0e48f",
+ "ecompGeneratedNaming": "true",
+ "category": "Network L4+",
+ "type": "Service",
+ "UUID": "63cac700-ab9a-4115-a74f-7eac85e3fce0",
+ "instantiationType": "A-la-carte"
+ },
+ "resourceDetails": {
+ "CP": {},
+ "VL": {},
+ "VF": {
+ "vLoadBalancerMS 0": {
+ "resourceVendor": "Test",
+ "resourceVendorModelNumber": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+ "subcategory": "Load Balancer",
+ "category": "Application L4+",
+ "type": "VF",
+ "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+ "version": "1.0",
+ "resourceVendorRelease": "1.0",
+ "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+ }
+ },
+ "CR": {},
+ "VFC": {},
+ "PNF": {},
+ "Service": {},
+ "CVFC": {},
+ "Service Proxy": {},
+ "Configuration": {},
+ "AllottedResource": {},
+ "VFModule": {
+ "Vloadbalancerms..vpkg..module-1": {
+ "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+ "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+ "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vpkg",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ }
+ }
+ }
+ },
+ "lastComputedState": "DESIGN",
+ "components": {
+ "POLICY": {
+ "componentState": {
+ "stateName": "NOT_SENT",
+ "description": "The policies defined have NOT yet been created on the policy engine"
+ }
+ },
+ "DCAE": {
+ "componentState": {
+ "stateName": "BLUEPRINT_DEPLOYED",
+ "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+ }
+ }
+ },
+ "operationalPolicies": [
+ {
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ }
+ }
+ ],
+ "microServicePolicies": [
+ {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScaling"},
+ "shared": false,
+ "jsonRepresentation": {"schema": {}}
+ }
+ ],
+ "loopLogs": [
+ {
+ "id": 1,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Operational and Guard policies UPDATED",
+ "logInstant": "2019-07-08T09:44:37Z"
+ }
+ ]
+}
diff --git a/ui-react/src/__test__/OpenLoopModal.test.js b/ui-react/src/__test__/OpenLoopModal.test.js
new file mode 100644
index 000000000..044eeda89
--- /dev/null
+++ b/ui-react/src/__test__/OpenLoopModal.test.js
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import { shallow } from 'enzyme';
+import OpenLoopModal from '../components/dialogs/OpenLoop/OpenLoopModal';
+
+describe('Verify OpenLoopModal', () => {
+
+ it('Test the render method', () => {
+ const component = shallow(<OpenLoopModal/>);
+ expect(component).toMatchSnapshot();
+ });
+
+
+});
diff --git a/ui-react/src/api/LoopActionService.js b/ui-react/src/api/LoopActionService.js
new file mode 100644
index 000000000..6e45ce4b9
--- /dev/null
+++ b/ui-react/src/api/LoopActionService.js
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopActionService{
+
+ static performAction(cl_name, uiAction) {
+ console.log("LoopActionService perform action: " + uiAction + " closedloopName=" + cl_name);
+ const svcAction = uiAction.toLowerCase();
+ return fetch("/restservices/clds/v2/loop/" + svcAction + "/" + cl_name, {
+ method: 'PUT',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ if (response.ok) {
+ return response.json();
+ } else {
+ return Promise.reject("Perform action failed with code:" + response.status);
+ }
+ })
+ .then(function (data) {
+ alert("Action Successful: " + uiAction);
+ return data;
+ })
+ .catch(function(error) {
+ console.log("Action Failure: " + uiAction);
+ return Promise.reject(error);
+ });
+ }
+
+
+ static refreshStatus(cl_name) {
+ console.log("Refresh the status for closedloopName=" + cl_name);
+
+ return fetch("/restservices/clds/v2/loop/getstatus/" + cl_name, {
+ method: 'GET',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ if (response.ok) {
+ return response.json();
+ } else {
+ return Promise.reject("Refresh status failed with code:" + response.status);
+ }
+ })
+ .then(function (data) {
+ console.info ("Refresh status Successful");
+ return data;
+ })
+ .catch(function(error) {
+ console.info ("Refresh status failed:", error);
+ return Promise.reject(error);
+ });
+ }
+}
diff --git a/ui-react/src/api/LoopCache.js b/ui-react/src/api/LoopCache.js
new file mode 100644
index 000000000..3ee5acc68
--- /dev/null
+++ b/ui-react/src/api/LoopCache.js
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopCache {
+ loopJsonCache;
+
+ constructor(loopJson) {
+ this.loopJsonCache=loopJson;
+ }
+
+ updateMicroServiceProperties(type, newMsProperties) {
+ for (var policy in this.loopJsonCache["microServicePolicies"]) {
+ if (this.loopJsonCache["microServicePolicies"][policy]["name"] === type) {
+ this.loopJsonCache["microServicePolicies"][policy]["properties"] = newMsProperties;
+ }
+ }
+ }
+
+ updateGlobalProperties(newGlobalProperties) {
+ this.loopJsonCache["globalPropertiesJson"] = newGlobalProperties;
+ }
+
+ updateOperationalPolicyProperties(newOpProperties) {
+ this.loopJsonCache["operationalPolicies"] = newOpProperties;
+ }
+
+ getLoopName() {
+ return this.loopJsonCache["name"];
+ }
+
+ getOperationalPolicyConfigurationJson() {
+ return this.loopJsonCache["operationalPolicies"]["0"]["configurationsJson"];
+ }
+
+ getOperationalPolicies() {
+ return this.loopJsonCache["operationalPolicies"];
+ }
+
+ getGlobalProperties() {
+ return this.loopJsonCache["globalPropertiesJson"];
+ }
+
+ getDcaeDeploymentProperties() {
+ return this.loopJsonCache["globalPropertiesJson"]["dcaeDeployParameters"];
+ }
+
+ getMicroServicePolicies() {
+ return this.loopJsonCache["microServicePolicies"];
+ }
+
+ getMicroServiceForName(name) {
+ var msProperties=this.getMicroServicePolicies();
+ for (var policy in msProperties) {
+ if (msProperties[policy]["name"] === name) {
+ return msProperties[policy];
+ }
+ }
+ return null;
+ }
+
+ getMicroServicePropertiesForName(name) {
+ var msConfig = this.getMicroServiceForName(name);
+ if (msConfig !== null) {
+ return msConfig["properties"];
+ }
+ return null;
+ }
+
+ getMicroServiceJsonRepresentationForName(name) {
+ var msConfig = this.getMicroServiceForName(name);
+ if (msConfig !== null) {
+ return msConfig["jsonRepresentation"];
+ }
+ return null;
+ }
+
+ getResourceDetailsVfProperty() {
+ return this.loopJsonCache["modelPropertiesJson"]["resourceDetails"]["VF"];
+ }
+
+ getResourceDetailsVfModuleProperty() {
+ return this.loopJsonCache["modelPropertiesJson"]["resourceDetails"]["VFModule"];
+ }
+
+ getLoopLogsArray() {
+ return this.loopJsonCache.loopLogs;
+ }
+
+ getComputedState() {
+ return this.loopJsonCache.lastComputedState;
+ }
+
+ getComponentStates() {
+ return this.loopJsonCache.components;
+ }
+}
diff --git a/ui-react/src/api/LoopService.js b/ui-react/src/api/LoopService.js
new file mode 100644
index 000000000..031ec638f
--- /dev/null
+++ b/ui-react/src/api/LoopService.js
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class LoopService {
+ static getLoopNames() {
+ return fetch('/restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'same-origin', })
+ .then(function (response) {
+ console.debug("GetLoopNames response received: ", response.status);
+ if (response.ok) {
+ return response.json();
+ } else {
+ console.error("GetLoopNames query failed");
+ return {};
+ }
+ })
+ .catch(function (error) {
+ console.error("GetLoopNames error received", error);
+ return {};
+ });
+ }
+
+ static getLoop(loopName) {
+ return fetch('/restservices/clds/v2/loop/' + loopName, {
+ method: 'GET',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ console.debug("GetLoop response received: ", response.status);
+ if (response.ok) {
+ return response.json();
+ } else {
+ console.error("GetLoop query failed");
+ return {};
+ }
+ })
+ .catch(function (error) {
+ console.error("GetLoop error received", error);
+ return {};
+ });
+ }
+
+ static getSvg(loopName) {
+ return fetch('/restservices/clds/v2/loop/svgRepresentation/' + loopName, {
+ method: 'GET',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ console.debug("svgRepresentation response received: ", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("svgRepresentation query failed");
+ return "";
+ }
+ })
+ .catch(function (error) {
+ console.error("svgRepresentation error received", error);
+ return "";
+ });
+ }
+
+ static setMicroServiceProperties(loopName, jsonData) {
+ return fetch('/restservices/clds/v2/loop/updateMicroservicePolicy/' + loopName, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(jsonData),
+ })
+ .then(function (response) {
+ console.debug("updateMicroservicePolicy response received: ", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("updateMicroservicePolicy query failed");
+ return "";
+ }
+ })
+ .catch(function (error) {
+ console.error("updateMicroservicePolicy error received", error);
+ return "";
+ });
+ }
+
+ static updateGlobalProperties(loopName, jsonData) {
+ return fetch('/restservices/clds/v2/loop/updateGlobalProperties/' + loopName, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(jsonData),
+ })
+ .then(function (response) {
+ console.debug("updateGlobalProperties response received: ", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("updateGlobalProperties query failed");
+ return "";
+ }
+ })
+ .catch(function (error) {
+ console.error("updateGlobalProperties error received", error);
+ return "";
+ });
+ }
+}
diff --git a/ui-react/src/api/UserService.js b/ui-react/src/api/UserService.js
new file mode 100644
index 000000000..be21e692a
--- /dev/null
+++ b/ui-react/src/api/UserService.js
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+export default class UserService {
+ static notLoggedUserName='Anonymous';
+ static login() {
+ return fetch('/restservices/clds/v1/user/getUser', {
+ method: 'GET',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ console.debug("getUser response received, status code:", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("getUser response is nok");
+ return UserService.notLoggedUserName;
+ }
+ })
+ .then(function (data) {
+ console.info ("User connected:",data)
+ return data;
+ })
+ .catch(function(error) {
+ console.warn("getUser error received, user set to: ",UserService.notLoggedUserName);
+ console.error("getUser error:",error);
+ return UserService.notLoggedUserName;
+ });
+ }
+
+ static getUserInfo() {
+ return fetch('/restservices/clds/v1/clds/cldsInfo', {
+ method: 'GET',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ console.debug("getUserInfo response received, status code:", response.status);
+ if (response.ok) {
+ return response.json();
+ }
+ })
+ .then(function (data) {
+ console.info ("User info received:",data)
+ return data;
+ })
+ .catch(function(error) {
+ console.warn("getUserInfo error received, user set to: ",UserService.notLoggedUserName);
+ console.error("getUserInfo error:",error);
+ return;
+ });
+ }
+}
diff --git a/ui-react/src/api/example.json b/ui-react/src/api/example.json
new file mode 100644
index 000000000..108cf78e2
--- /dev/null
+++ b/ui-react/src/api/example.json
@@ -0,0 +1,417 @@
+{
+ "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+ "dcaeBlueprintId": "typeId-3a942643-a8f7-4e54-b2c1-eea8daba2b17",
+ "globalPropertiesJson": {
+ "dcaeDeployParameters": {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }
+ },
+ "modelPropertiesJson": {
+ "serviceDetails": {
+ "serviceType": "",
+ "namingPolicy": "",
+ "environmentContext": "General_Revenue-Bearing",
+ "serviceEcompNaming": "true",
+ "serviceRole": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "30ec5b59-4799-48d8-ac5f-1058a6b0e48f",
+ "ecompGeneratedNaming": "true",
+ "category": "Network L4+",
+ "type": "Service",
+ "UUID": "63cac700-ab9a-4115-a74f-7eac85e3fce0",
+ "instantiationType": "A-la-carte"
+ },
+ "resourceDetails": {
+ "CP": {},
+ "VL": {},
+ "VF": {
+ "vLoadBalancerMS 0": {
+ "resourceVendor": "Test",
+ "resourceVendorModelNumber": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+ "subcategory": "Load Balancer",
+ "category": "Application L4+",
+ "type": "VF",
+ "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+ "version": "1.0",
+ "resourceVendorRelease": "1.0",
+ "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+ }
+ },
+ "CR": {},
+ "VFC": {},
+ "PNF": {},
+ "Service": {},
+ "CVFC": {},
+ "Service Proxy": {},
+ "Configuration": {},
+ "AllottedResource": {},
+ "VFModule": {
+ "Vloadbalancerms..vpkg..module-1": {
+ "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+ "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+ "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vpkg",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ },
+ "Vloadbalancerms..vdns..module-3": {
+ "vfModuleModelInvariantUUID": "4c10ba9b-f88f-415e-9de3-5d33336047fa",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vdns..module-3",
+ "vfModuleModelUUID": "4fa73b49-8a6c-493e-816b-eb401567b720",
+ "vfModuleModelCustomizationUUID": "bafcdab0-801d-4d81-9ead-f464640a38b1",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vdns",
+ "max_vf_module_instances": 50,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ },
+ "Vloadbalancerms..base_template..module-0": {
+ "vfModuleModelInvariantUUID": "921f7c96-ebdd-42e6-81b9-1cfc0c9796f3",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..base_template..module-0",
+ "vfModuleModelUUID": "63734409-f745-4e4d-a38b-131638a0edce",
+ "vfModuleModelCustomizationUUID": "86baddea-c730-4fb8-9410-cd2e17fd7f27",
+ "min_vf_module_instances": 1,
+ "vf_module_label": "base_template",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Base",
+ "isBase": true,
+ "initial_count": 1,
+ "volume_group": false
+ },
+ "Vloadbalancerms..vlb..module-2": {
+ "vfModuleModelInvariantUUID": "a772a1f4-0064-412c-833d-4749b15828dd",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vlb..module-2",
+ "vfModuleModelUUID": "0f5c3f6a-650a-4303-abb6-fff3e573a07a",
+ "vfModuleModelCustomizationUUID": "96a78aad-4ffb-4ef0-9c4f-deb03bf1d806",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vlb",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ }
+ }
+ }
+ },
+ "lastComputedState": "DESIGN",
+ "components": {
+ "POLICY": {
+ "componentState": {
+ "stateName": "NOT_SENT",
+ "description": "The policies defined have NOT yet been created on the policy engine"
+ }
+ },
+ "DCAE": {
+ "componentState": {
+ "stateName": "BLUEPRINT_DEPLOYED",
+ "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+ }
+ }
+ },
+ "operationalPolicies": [
+ {
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "guard_policies": {
+ "guard.minmax.new": {
+ "recipe": "",
+ "clname": "LOOP_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "actor": "",
+ "targets": "",
+ "min": "gg",
+ "max": "gg",
+ "limit": "",
+ "timeUnits": "",
+ "timeWindow": "",
+ "guardActiveStart": "00:00:00Z",
+ "guardActiveEnd": "00:00:01Z"
+ }
+ },
+ "operational_policy": {
+ "controlLoop": {
+ "trigger_policy": "new",
+ "timeout": "0",
+ "abatement": "false",
+ "controlLoopName": "LOOP_h2NMX_v1_0_ResourceInstanceName1_tca"
+ },
+ "policies": [
+ {
+ "id": "new",
+ "recipe": "",
+ "retry": "0",
+ "timeout": "0",
+ "actor": "",
+ "payload": "",
+ "success": "",
+ "failure": "",
+ "failure_timeout": "",
+ "failure_retries": "",
+ "failure_exception": "",
+ "failure_guard": "",
+ "target": {
+ "type": "VM",
+ "resourceID": ""
+ }
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "microServicePolicies": [
+ {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {
+ "domain": "measurementsForVfScaling",
+ "metricsPerEventName": [
+ {
+ "policyVersion": "ff",
+ "thresholds": [
+ {
+ "severity": "CRITICAL",
+ "fieldPath": "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedTotalPacketsDelta",
+ "thresholdValue": 0,
+ "closedLoopEventStatus": "ONSET",
+ "closedLoopControlName": "ff",
+ "version": "ff",
+ "direction": "LESS"
+ }
+ ],
+ "policyName": "ff",
+ "controlLoopSchemaType": "VM",
+ "policyScope": "ff",
+ "eventName": "ff"
+ }
+ ]
+ },
+ "shared": false,
+ "jsonRepresentation": {
+ "schema": {
+ "uniqueItems": "true",
+ "format": "tabs-top",
+ "type": "array",
+ "title": "TCA Policy JSON",
+ "items": {
+ "type": "object",
+ "title": "TCA Policy JSON",
+ "required": [
+ "domain",
+ "metricsPerEventName"
+ ],
+ "properties": {
+ "domain": {
+ "propertyOrder": 1001,
+ "default": "measurementsForVfScaling",
+ "title": "Domain name to which TCA needs to be applied",
+ "type": "string"
+ },
+ "metricsPerEventName": {
+ "propertyOrder": 1002,
+ "uniqueItems": "true",
+ "format": "tabs-top",
+ "title": "Contains eventName and threshold details that need to be applied to given eventName",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "controlLoopSchemaType",
+ "eventName",
+ "policyName",
+ "policyScope",
+ "policyVersion",
+ "thresholds"
+ ],
+ "properties": {
+ "policyVersion": {
+ "propertyOrder": 1007,
+ "title": "TCA Policy Scope Version",
+ "type": "string"
+ },
+ "thresholds": {
+ "propertyOrder": 1008,
+ "uniqueItems": "true",
+ "format": "tabs-top",
+ "title": "Thresholds associated with eventName",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "closedLoopControlName",
+ "closedLoopEventStatus",
+ "direction",
+ "fieldPath",
+ "severity",
+ "thresholdValue",
+ "version"
+ ],
+ "properties": {
+ "severity": {
+ "propertyOrder": 1013,
+ "title": "Threshold Event Severity",
+ "type": "string",
+ "enum": [
+ "CRITICAL",
+ "MAJOR",
+ "MINOR",
+ "WARNING",
+ "NORMAL"
+ ]
+ },
+ "fieldPath": {
+ "propertyOrder": 1012,
+ "title": "Json field Path as per CEF message which needs to be analyzed for TCA",
+ "type": "string",
+ "enum": [
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedTotalPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedOctetsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedUnicastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedMulticastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedBroadcastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedDiscardedPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedErrorPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedTotalPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedOctetsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedUnicastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedMulticastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedBroadcastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedDiscardedPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].receivedErrorPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedTotalPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedOctetsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedUnicastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedMulticastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedBroadcastPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedDiscardedPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedErrorPacketsDelta",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedTotalPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedOctetsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedUnicastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedMulticastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedBroadcastPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedDiscardedPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].transmittedErrorPacketsAccumulated",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuIdle",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuUsageInterrupt",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuUsageNice",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuUsageSoftIrq",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuUsageSteal",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuUsageSystem",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].cpuWait",
+ "$.event.measurementsForVfScalingFields.cpuUsageArray[*].percentUsage",
+ "$.event.measurementsForVfScalingFields.meanRequestLatency",
+ "$.event.measurementsForVfScalingFields.memoryUsageArray[*].memoryBuffered",
+ "$.event.measurementsForVfScalingFields.memoryUsageArray[*].memoryCached",
+ "$.event.measurementsForVfScalingFields.memoryUsageArray[*].memoryConfigured",
+ "$.event.measurementsForVfScalingFields.memoryUsageArray[*].memoryFree",
+ "$.event.measurementsForVfScalingFields.memoryUsageArray[*].memoryUsed",
+ "$.event.measurementsForVfScalingFields.additionalMeasurements[*].arrayOfFields[0].value"
+ ]
+ },
+ "thresholdValue": {
+ "propertyOrder": 1014,
+ "title": "Threshold value for the field Path inside CEF message",
+ "type": "integer"
+ },
+ "closedLoopEventStatus": {
+ "propertyOrder": 1010,
+ "title": "Closed Loop Event Status of the threshold",
+ "type": "string",
+ "enum": [
+ "ONSET",
+ "ABATED"
+ ]
+ },
+ "closedLoopControlName": {
+ "propertyOrder": 1009,
+ "title": "Closed Loop Control Name associated with the threshold",
+ "type": "string"
+ },
+ "version": {
+ "propertyOrder": 1015,
+ "title": "Version number associated with the threshold",
+ "type": "string"
+ },
+ "direction": {
+ "propertyOrder": 1011,
+ "title": "Direction of the threshold",
+ "type": "string",
+ "enum": [
+ "LESS",
+ "LESS_OR_EQUAL",
+ "GREATER",
+ "GREATER_OR_EQUAL",
+ "EQUAL"
+ ]
+ }
+ }
+ }
+ },
+ "policyName": {
+ "propertyOrder": 1005,
+ "title": "TCA Policy Scope Name",
+ "type": "string"
+ },
+ "controlLoopSchemaType": {
+ "propertyOrder": 1003,
+ "title": "Specifies Control Loop Schema Type for the event Name e.g. VNF, VM",
+ "type": "string",
+ "enum": [
+ "VM",
+ "VNF"
+ ]
+ },
+ "policyScope": {
+ "propertyOrder": 1006,
+ "title": "TCA Policy Scope",
+ "type": "string"
+ },
+ "eventName": {
+ "propertyOrder": 1004,
+ "title": "Event name to which thresholds need to be applied",
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "loopLogs": [
+ {
+ "id": 2,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Micro Service policies UPDATED",
+ "logInstant": "2019-07-08T09:44:53Z"
+ },
+ {
+ "id": 1,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Operational and Guard policies UPDATED",
+ "logInstant": "2019-07-08T09:44:37Z"
+ }
+ ]
+}
diff --git a/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js
new file mode 100644
index 000000000..4fbb7832c
--- /dev/null
+++ b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import JSONEditor from '@json-editor/json-editor';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class ConfigurationPolicyModal extends React.Component {
+
+ state = {
+ show: true,
+ loopCache: this.props.loopCache,
+ jsonEditor: null,
+ componentName: this.props.match.params.componentName,
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.handleClose = this.handleClose.bind(this);
+ this.handleSave = this.handleSave.bind(this);
+ this.renderJsonEditor = this.renderJsonEditor.bind(this);
+ }
+
+ handleSave() {
+ var errors = this.state.jsonEditor.validate();
+ var editorData = this.state.jsonEditor.getValue();
+
+ if (errors.length !== 0) {
+ console.error("Errors detected during config policy data validation ", errors);
+ this.setState({ show: false });
+ this.props.history.push('/');
+ }
+ else {
+ console.info("NO validation errors found in config policy data");
+ this.state.loopCache.updateMicroServiceProperties(this.state.componentName, editorData[0]);
+ LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.componentName)).then(resp => {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+ });
+ }
+ }
+
+ handleClose() {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ }
+
+ componentDidMount() {
+ this.renderJsonEditor();
+ }
+
+ renderJsonEditor() {
+ console.debug("Rendering ConfigurationPolicyModal ", this.state.componentName);
+ var toscaModel = this.state.loopCache.getMicroServiceJsonRepresentationForName(this.state.componentName);
+ if (toscaModel == null) {
+ return;
+ }
+ var editorData = this.state.loopCache.getMicroServicePropertiesForName(this.state.componentName);
+
+ JSONEditor.defaults.options.theme = 'bootstrap4';
+ //JSONEditor.defaults.options.iconlib = 'bootstrap2';
+ JSONEditor.defaults.options.object_layout = 'grid';
+ JSONEditor.defaults.options.disable_properties = true;
+ JSONEditor.defaults.options.disable_edit_json = false;
+ JSONEditor.defaults.options.disable_array_reorder = true;
+ JSONEditor.defaults.options.disable_array_delete_last_row = true;
+ JSONEditor.defaults.options.disable_array_delete_all_rows = false;
+ JSONEditor.defaults.options.show_errors = 'always';
+
+ this.setState({
+ jsonEditor: new JSONEditor(document.getElementById("editor"),
+ { schema: toscaModel.schema, startval: editorData })
+ })
+ }
+
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose}>
+ <Modal.Header closeButton>
+ <Modal.Title>Configuration policies</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <div id="editor" />
+
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" onClick={this.handleClose}>
+ Close
+ </Button>
+ <Button variant="primary" onClick={this.handleSave}>
+ Save Changes
+ </Button>
+ </Modal.Footer>
+ </ModalStyled>
+
+ );
+ }
+} \ No newline at end of file
diff --git a/ui-react/src/components/dialogs/LoopProperties.js b/ui-react/src/components/dialogs/LoopProperties.js
new file mode 100644
index 000000000..dac77655f
--- /dev/null
+++ b/ui-react/src/components/dialogs/LoopProperties.js
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import styled from 'styled-components';
+import LoopService from '../../api/LoopService';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class LoopProperties extends React.Component {
+
+ state = {
+ show: true,
+ loopCache: this.props.loopCache,
+ temporaryPropertiesJson: JSON.parse(JSON.stringify(this.props.loopCache.getGlobalProperties())),
+ };
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.handleSave = this.handleSave.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+
+ this.renderDcaeParameters = this.renderDcaeParameters.bind(this);
+ this.renderAllParameters = this.renderAllParameters.bind(this);
+ this.getDcaeParameters = this.getDcaeParameters.bind(this);
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ temporaryPropertiesJson: JSON.parse(JSON.stringify(newProps.loopCache.getGlobalProperties())),
+
+ });
+ }
+
+ handleClose() {
+ this.props.history.push('/');
+ }
+
+ handleSave(event) {
+ LoopService.updateGlobalProperties(this.state.loopCache.getLoopName(), this.state.temporaryPropertiesJson).then(resp => {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+ });
+ }
+
+ handleChange(event) {
+ this.setState({temporaryPropertiesJson:{[event.target.name]: JSON.parse(event.target.value)}});
+ }
+
+ renderAllParameters() {
+ return (<Modal.Body>
+ <Form>
+ {this.renderDcaeParameters()}
+ </Form>
+ </Modal.Body>
+ );
+ }
+
+ getDcaeParameters() {
+ if (typeof (this.state.temporaryPropertiesJson) !== "undefined") {
+ return JSON.stringify(this.state.temporaryPropertiesJson["dcaeDeployParameters"]);
+ } else {
+ return "";
+ }
+
+ }
+
+ renderDcaeParameters() {
+ return (
+ <Form.Group >
+ <Form.Label>Deploy Parameters</Form.Label>
+ <Form.Control as="textarea" rows="3" name="dcaeDeployParameters" onChange={this.handleChange} defaultValue={this.getDcaeParameters()}></Form.Control>
+ </Form.Group>
+ );
+ }
+
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} >
+ <Modal.Header closeButton>
+ <Modal.Title>Model Properties</Modal.Title>
+ </Modal.Header>
+ {this.renderAllParameters()}
+ <Modal.Footer>
+ <Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
+ <Button variant="primary" type="submit" onClick={this.handleSave}>Save Changes</Button>
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}
diff --git a/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js b/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js
new file mode 100644
index 000000000..fbfc727eb
--- /dev/null
+++ b/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Select from 'react-select';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import FormCheck from 'react-bootstrap/FormCheck'
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+const CheckBoxStyled = styled(FormCheck.Input)`
+ margin-left:3rem;
+`
+
+export default class OpenLoopModal extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+
+ this.getLoopNames = this.getLoopNames.bind(this);
+ this.handleOpen = this.handleOpen.bind(this);
+ this.handleClose = this.handleClose.bind(this);
+ this.handleDropdownListChange = this.handleDropdownListChange.bind(this);
+ this.state = {
+ show: true,
+ chosenLoopName: '',
+ loopNames: []
+ };
+ }
+
+ componentWillMount() {
+ this.getLoopNames();
+ }
+
+ handleClose() {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ }
+
+ handleDropdownListChange(e) {
+ this.setState({ chosenLoopName: e.value });
+ }
+
+ getLoopNames() {
+ LoopService.getLoopNames().then(loopNames => {
+ const loopOptions = loopNames.map((loopName) => { return { label: loopName, value: loopName } });
+ this.setState({ loopNames: loopOptions })
+ });
+ }
+
+ handleOpen() {
+ console.info("Loop " + this.state.chosenLoopName + " is chosen");
+ this.setState({ show: false });
+ this.props.history.push('/');
+ this.props.loadLoopFunction(this.state.chosenLoopName);
+ }
+
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose}>
+ <Modal.Header closeButton>
+ <Modal.Title>Open Model</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form.Group as={Row} controlId="formPlaintextEmail">
+ <Form.Label column sm="2">Model Name</Form.Label>
+ <Col sm="10">
+ <Select onChange={this.handleDropdownListChange} options={this.state.loopNames} />
+ </Col>
+ </Form.Group>
+ <Form.Group controlId="formBasicChecbox">
+ <Form.Check>
+ <FormCheck.Label>Read Only</FormCheck.Label>
+ <CheckBoxStyled type="checkbox" />
+ </Form.Check>
+ </Form.Group>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
+ <Button variant="primary" type="submit" onClick={this.handleOpen}>Open</Button>
+ </Modal.Footer>
+ </ModalStyled>
+
+ );
+ }
+}
diff --git a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicy.css b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicy.css
new file mode 100644
index 000000000..94a91c234
--- /dev/null
+++ b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicy.css
@@ -0,0 +1,73 @@
+.disabled {
+ background-color: #dddd;
+}
+
+label {
+ text-align: right;
+ vertical-align: middle;
+}
+
+.leftPolicyPanel {
+ padding: 0 10px 0 0;
+}
+
+.idError {
+ color: red;
+ padding: 50px 0px;
+ text-align: center;
+ display: none;
+}
+
+.policyPanel {
+ background-color: #f5f5f5;
+ padding: 15px 5px 0 5px;
+}
+
+.form-group.clearfix {
+ display: -webkit-flex;
+ display: flex;
+ align-items: center;
+}
+
+label {
+ margin-bottom: 0px;
+}
+
+.withnote {
+ margin-bottom: 0px;
+}
+
+.note {
+ font-size:10px;
+ margin-left: 250px;
+ font-weight: normal;
+}
+
+#policyTable {
+ cursor: pointer;
+ width: 100%;
+}
+
+#policyTable tr {
+ border-bottom: 1px solid #ddd;
+ border-collapse: collapse;
+ text-align: left;
+ font-size: 12px;
+ font-weight: normal;
+}
+
+#policyTable td {
+ padding: 8px 10px;
+}
+
+#policyTable tr.highlight {
+ background-color: #f5f5f5;
+ font-weight: bold;
+ font-size: 13px;
+}
+
+#policyTableHolder {
+ height: 200px;
+ width: 100%;
+ overflow: auto;
+} \ No newline at end of file
diff --git a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js
new file mode 100644
index 000000000..2a812c877
--- /dev/null
+++ b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js
@@ -0,0 +1,552 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import './OperationalPolicy.css'
+import styled from 'styled-components';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class OperationalPolicyModal extends React.Component {
+
+ state = {
+ show: true,
+ loopCache: this.props.loopCache,
+ };
+
+ allPolicies = [];
+ policyIds = [];
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.initPolicySelect = this.initPolicySelect.bind(this);
+ this.initPolicySelect();
+ }
+
+ handleClose() {
+ this.setState({ show: false });
+ this.props.history.push('/')
+ }
+
+ initPolicySelect() {
+ if (this.allPolicies['operational_policy'] === undefined || this.allPolicies['operational_policy'] === null) {
+ this.allPolicies = this.state.loopCache.getOperationalPolicyConfigurationJson();
+ }
+ // Provision all policies ID first
+ if (this.policyIds.length === 0 && this.allPolicies['operational_policy'] !== undefined) {
+
+ for (let i = 0; i < this.allPolicies['operational_policy']['policies'].length; i++) {
+ this.policyIds.push(this.allPolicies['operational_policy']['policies'][i]['id']);
+ }
+ }
+ }
+
+ renderPolicyIdSelect() {
+ return (
+ <select type="text" id="trigger_policy" name="trigger_policy"
+ className="form-control">
+ <option value="">-- choose an option --</option>
+ {this.policyIds.map(policyId => (<option key={policyId}>{policyId}</option>))}
+ </select>
+ );
+ }
+
+ serializeElement(element) {
+ var o = {};
+ element.serializeArray().forEach(function () {
+ if (o[this.name]) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+ }
+
+ // When we change the name of a policy
+ isDuplicatedId(event) {
+ // update policy id structure
+ var formNum = document.getElementById(event.target).closest('.formId').attr('id').substring(6);
+ var policyId = document.getElementById(event.target).val();
+ if (this.policyIds.includes(policyId)) {
+ console.log("Duplicated ID, cannot proceed");
+ return true;
+ } else {
+ this.duplicated = false;
+ this.policyIds.splice(this.policyIds.indexOf(document.getElementById("#formId" + formNum + " #id").val()), 1);
+ this.policyIds.push(document.getElementById(event.target).val());
+ // Update the tab now
+ document.getElementById("#go_properties_tab" + formNum).text(document.getElementById(event.target).val());
+ }
+ }
+
+ configureComponents() {
+ console.log("Load properties to op policy");
+ // Set the header
+ document.getElementsByClassName('form-control').forEach(function () {
+ this.val(this.allPolicies['operational_policy']['controlLoop'][this.id]);
+ });
+ // Set the sub-policies
+ this.allPolicies['operational_policy']['policies'].forEach(function (opPolicyElemIndex, opPolicyElemValue) {
+
+ /* var formNum = add_one_more();
+ forEach(document.getElementsByClassName('policyProperties').find('.form-control'), function(opPolicyPropIndex, opPolicyPropValue) {
+
+ $("#formId" + formNum + " .policyProperties").find("#" + opPolicyPropValue.id).val(
+ allPolicies['operational_policy']['policies'][opPolicyElemIndex][opPolicyPropValue.id]);
+ });
+
+ // Initial TargetResourceId options
+ initTargetResourceIdOptions(allPolicies['operational_policy']['policies'][opPolicyElemIndex]['target']['type'], formNum);
+ $.each($('.policyTarget').find('.form-control'), function(opPolicyTargetPropIndex, opPolicyTargetPropValue) {
+
+ $("#formId" + formNum + " .policyTarget").find("#" + opPolicyTargetPropValue.id).val(
+ allPolicies['operational_policy']['policies'][opPolicyElemIndex]['target'][opPolicyTargetPropValue.id]);
+ });
+
+ // update the current tab label
+ $("#go_properties_tab" + formNum).text(
+ allPolicies['operational_policy']['policies'][opPolicyElemIndex]['id']);
+ // Check if there is a guard set for it
+ $.each(allPolicies['guard_policies'], function(guardElemId, guardElemValue) {
+
+ if (guardElemValue.recipe === $($("#formId" + formNum + " #recipe")[0]).val()) {
+ // Found one, set all guard prop
+ $.each($('.guardProperties').find('.form-control'), function(guardPropElemIndex,
+ guardPropElemValue) {
+
+ guardElemValue['id'] = guardElemId;
+ $("#formId" + formNum + " .guardProperties").find("#" + guardPropElemValue.id).val(
+ guardElemValue[guardPropElemValue.id]);
+ });
+ iniGuardPolicyType(guardElemId, formNum);
+ // And finally enable the flag
+ $("#formId" + formNum + " #enableGuardPolicy").prop("checked", true);
+ }
+ });*/
+ });
+ }
+
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose}>
+ <Modal.Header closeButton>
+ <Modal.Title>Operational policies</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <div attribute-test="policywindowproperties" id="configure-widgets"
+ className="disabled-block-container">
+ <div attribute-test="policywindowpropertiesb" className="modal-body row">
+ <div className="panel panel-default col-sm-10 policyPanel">
+ <form id="operationalPolicyHeaderForm" className="form-horizontal">
+ <div className="form-group clearfix">
+ <label className="col-sm-2">Parent policy</label>
+ <div className="col-sm-3" style={{ padding: '0px' }}>
+ {this.renderPolicyIdSelect()}
+ </div>
+
+ <label htmlFor="timeout" className="col-sm-3"
+ style={{ paddingLeft: '5px', paddingRight: '10px' }}>Overall
+ Time Limit</label>
+ <div className="col-sm-2" style={{ paddingLeft: '0px' }}>
+ <input type="text" ng-pattern="/^[0-9]*$/" ng-model="number"
+ className="form-control" id="timeout" name="timeout" />
+ </div>
+
+ <label htmlFor="abatement" className="col-sm-2">Abatement</label>
+ <div className="col-sm-2" style={{ paddingLeft: '0px' }}>
+ <select className="form-control" id="abatement" name="abatement">
+ <option value="false">False</option>
+ <option value="true">True</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix row">
+ <label className="col-sm-4 control-label" htmlFor="clname">ControlLoopName</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="controlLoopName"
+ readOnly="readonly" id="clname" value={this.state.loopCache.getLoopName()} />
+ </div>
+ </div>
+ </form>
+ <div className="panel-heading" style={{ backgroundColor: 'white' }}>
+ <ul id="nav_Tabs" className="nav nav-tabs">
+ <li>
+ <a id="add_one_more" href="#desc_tab">
+ <span
+ className="glyphicon glyphicon-plus" aria-hidden="true">
+ </span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ <div className="panel-body">
+ <div className="tab-content">
+ <div id="properties_tab" className="tab-pane fade in active"></div>
+ </div>
+ </div>
+ </div>
+
+ <span id="formSpan" style={{ display: 'none' }}>
+ <form className="policyProperties form-horizontal"
+ style={{ border: '2px dotted gray' }}
+ title="Operational Policy Properties">
+ <div className="form-group clearfix">
+ <label className="col-sm-4 control-label" htmlFor="id">ID</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="id" id="id"
+ onKeyUp="updateTabLabel($event)" />
+ <span >ID must be unique</span>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label className="col-sm-4 control-label" htmlFor="recipe">Recipe</label>
+ <div className="col-sm-8">
+ <select className="form-control" name="recipe" id="recipe"
+ ng-model="recipe" ng-click="updateGuardRecipe($event)">
+ <option value="">-- choose an option --</option>
+ <option value="Restart">Restart</option>
+ <option value="Rebuild">Rebuild</option>
+ <option value="Migrate">Migrate</option>
+ <option value="Health-Check">Health-Check</option>
+ <option value="ModifyConfig">ModifyConfig</option>
+ <option value="VF Module Create">VF Module Create</option>
+ <option value="VF Module Delete">VF Module Delete</option>
+ <option value="Reroute">Reroute</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="retry" className="col-sm-4 control-label"> Retry</label>
+ <div className="col-sm-8">
+ <input type="text" maxLength="5" className="form-control" id="retry"
+ ng-pattern="/^[0-9]*$/" ng-model="number" name="retry">
+ </input>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="timeout" className="col-sm-4 control-label">
+ Timeout</label>
+ <div className="col-sm-8">
+ <input type="text" maxLength="5" className="form-control"
+ id="timeout" ng-pattern="/^[0-9]*$/" ng-model="number"
+ name="timeout"></input>
+ </div>
+ </div>
+
+ <div className="form-group clearfix">
+ <label htmlFor="actor" className="col-sm-4 control-label"> Actor</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="actor" name="actor" ng-click="updateGuardActor($event)" ng-model="actor">
+ <option value="">-- choose an option --</option>
+ <option value="APPC">APPC</option>
+ <option value="SO">SO</option>
+ <option value="VFC">VFC</option>
+ <option value="SDNC">SDNC</option>°
+ <option value="SDNR">SDNR</option>°
+ </select>
+ </div>
+
+ <label htmlFor="payload" className="col-sm-4 control-label">
+ Payload (YAML)</label>
+ <div className="col-sm-8">
+ <textarea className="form-control" id="payload" name="payload"></textarea>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="success" className="col-sm-4 control-label">When
+ Success</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="success" name="success"
+ ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="failure" className="col-sm-4 control-label">When
+ Failure</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="failure" name="failure"
+ ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="failure_timeout" className="col-sm-4 control-label">When
+ Failure Timeout</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="failure_timeout"
+ name="failure_timeout" ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="failure_retries" className="col-sm-4 control-label">When
+ Failure Retries</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="failure_retries"
+ name="failure_retries" ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="failure_exception" className="col-sm-4 control-label">When
+ Failure Exception</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="failure_exception"
+ name="failure_exception" ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="failure_guard" className="col-sm-4 control-label">When
+ Failure Guard</label>
+ <div className="col-sm-8">
+ <select className="form-control" id="failure_guard"
+ name="failure_guard" ng-model="null_dump"
+ ng-options="policy for policy in policy_ids track by policy">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ </form>
+ <form className="policyTarget form-horizontal"
+ title="Operational Policy Target" style={{ border: '2px dotted gray' }}>
+ <div className="form-group clearfix">
+ <label htmlFor="type" className="col-sm-4 control-label"> Target
+ Type</label>
+ <div className="col-sm-8">
+ <select className="form-control" name="type" id="type"
+ ng-click="initTargetResourceId($event)" ng-model="type">
+ <option value="">-- choose an option --</option>
+ <option value="VFMODULE">VFMODULE</option>
+ <option value="VM">VM</option>
+ <option value="VNF">VNF</option>
+ </select>
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="resourceID" className="col-sm-4 control-label">
+ Target ResourceId</label>
+ <div className="col-sm-8">
+ <select className="form-control" name="resourceID" id="resourceID"
+ ng-click="changeTargetResourceId($event)"
+ ng-model="resourceId">
+ <option value="">-- choose an option --</option>
+ </select>
+ </div>
+ </div>
+ <div id="metadata">
+ <div className="form-group clearfix">
+ <label htmlFor="modelInvariantId" className="col-sm-4 control-label">
+ Model Invariant Id</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="modelInvariantId"
+ id="modelInvariantId" readOnly />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="modelVersionId" className="col-sm-4 control-label">
+ Model Version Id</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="modelVersionId"
+ id="modelVersionId" readOnly />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="modelName" className="col-sm-4 control-label">
+ Model Name</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="modelName" id="modelName"
+ readOnly />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="modelVersion" className="col-sm-4 control-label">
+ Model Version</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="modelVersion"
+ id="modelVersion" readOnly />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="modelCustomizationId" className="col-sm-4 control-label">
+ Model Customization Id</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="modelCustomizationId"
+ id="modelCustomizationId" readOnly />
+ </div>
+ </div>
+ </div>
+ </form>
+ <div className="form-group clearfix">
+ <label htmlFor="enableGuardPolicy" className="col-sm-4 control-label">
+ Enable Guard Policy</label>
+ <div className="col-sm-8">
+ <input type="checkbox" className="form-control"
+ name="enableGuardPolicy" id="enableGuardPolicy" />
+ </div>
+
+ <div className="col-sm-8">
+ <label htmlFor="guardPolicyType" className="col-sm-4 control-label">
+ Guard Policy Type</label> <select className="form-control"
+ name="guardPolicyType" id="guardPolicyType"
+ ng-click="changeGuardPolicyType()" ng-model="guardType">
+ <option value="GUARD_MIN_MAX">MinMax</option>
+ <option value="GUARD_YAML">FrequencyLimiter</option>
+ </select>
+ </div>
+ </div>
+ <form className="guardProperties form-horizontal"
+ title="Guard policy associated" style={{ border: '2px dotted gray' }}>
+
+ <div className="form-group clearfix withnote">
+ <label className="col-sm-4 control-label" htmlFor="id">Guard Policy ID</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="id" id="id" ng-blur="changeGuardId()" ng-model="id" />
+ </div>
+ </div>
+ <div>
+ <label className="form-group note">Note: Prefix will be added to Guard Policy ID automatically based on Guard Policy Type</label>
+ </div>
+ <div className="form-group clearfix">
+ <label className="col-sm-4 control-label" htmlFor="recipe">Recipe</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="recipe"
+ readOnly="readonly" id="recipe" />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label className="col-sm-4 control-label" htmlFor="clname">ControlLoopName</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="clname"
+ readOnly="readonly" id="clname" ng-model="clname" />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="actor" className="col-sm-4 control-label">Actor</label>
+ <div className="col-sm-8">
+ <input type="text" className="form-control" name="actor"
+ readOnly="readonly" id="actor" />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+
+ <label htmlFor="targets" className="col-sm-4 control-label">Guard
+ targets</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="targets" id="targets" />
+ </div>
+ </div>
+
+ <div className="form-group clearfix" id="minMaxGuardPolicyDiv">
+ <label htmlFor="min" className="col-sm-4 control-label"> Min
+ Guard</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="min" id="min" />
+ </div>
+ <label htmlFor="max" className="col-sm-4 control-label"> Max
+ Guard</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="max" id="max" />
+ </div>
+ </div>
+ <div className="form-group clearfix"
+ id="frequencyLimiterGuardPolicyDiv" style={{ display: 'none' }}>
+ <label htmlFor="limit" className="col-sm-4 control-label">Limit</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="limit" id="limit" />
+ </div>
+ <label htmlFor="timeUnits" className="col-sm-4 control-label">Time Units</label>
+ <div className="col-sm-8">
+ <select className="form-control" name="timeUnits"
+ id="timeUnits">
+ <option value=""></option>
+ <option value="minute">minute</option>
+ <option value="hour">hour</option>
+ <option value="day">day</option>
+ <option value="week">week</option>
+ <option value="month">month</option>
+ <option value="year">year</option>
+ </select>
+ </div>
+ <label htmlFor="timeWindow" className="col-sm-4 control-label">Time Window</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="timeWindow" id="timeWindow" />
+ </div>
+ </div>
+ <div className="form-group clearfix">
+ <label htmlFor="guardActiveStart" className="col-sm-4 control-label">
+ Guard Active Start</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="guardActiveStart"
+ id="guardActiveStart" value="00:00:00Z" />
+ </div>
+ <label htmlFor="guardActiveEnd" className="col-sm-4 control-label">
+ Guard Active End</label>
+ <div className="col-sm-8">
+ <input className="form-control" name="guardActiveEnd"
+ id="guardActiveEnd" value="00:00:01Z" />
+ </div>
+ </div>
+
+ </form>
+
+ </span>
+ </div>
+ </div>
+
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" onClick={this.handleClose}>
+ Close
+ </Button>
+ <Button variant="primary" onClick={this.handleClose}>
+ Save Changes
+ </Button>
+ </Modal.Footer>
+ </ModalStyled>
+
+ );
+ }
+} \ No newline at end of file
diff --git a/ui-react/src/components/dialogs/OperationalPolicy/template.json b/ui-react/src/components/dialogs/OperationalPolicy/template.json
new file mode 100644
index 000000000..6b4477f88
--- /dev/null
+++ b/ui-react/src/components/dialogs/OperationalPolicy/template.json
@@ -0,0 +1,52 @@
+{
+ "operationalPolicies": [
+ {
+ "name": "OPERATIONAL_LOOP_NAME",
+ "configurationsJson": {
+ "guard_policies": {
+ "guard.minmax.new": {
+ "recipe": "",
+ "clname": "LOOP_NAME",
+ "actor": "",
+ "targets": "",
+ "min": "",
+ "max": "",
+ "limit": "",
+ "timeUnits": "",
+ "timeWindow": "",
+ "guardActiveStart": "00:00:00Z",
+ "guardActiveEnd": "00:00:01Z"
+ }
+ },
+ "operational_policy": {
+ "controlLoop": {
+ "trigger_policy": "new",
+ "timeout": "0",
+ "abatement": "false",
+ "controlLoopName": "LOOP_h2NMX_v1_0_ResourceInstanceName1_tca"
+ },
+ "policies": [
+ {
+ "id": "new",
+ "recipe": "",
+ "retry": "0",
+ "timeout": "0",
+ "actor": "",
+ "payload": "",
+ "success": "",
+ "failure": "",
+ "failure_timeout": "",
+ "failure_retries": "",
+ "failure_exception": "",
+ "failure_guard": "",
+ "target": {
+ "type": "VM",
+ "resourceID": ""
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/ui-react/src/components/dialogs/UserInfo.js b/ui-react/src/components/dialogs/UserInfo.js
new file mode 100644
index 000000000..b8d68b849
--- /dev/null
+++ b/ui-react/src/components/dialogs/UserInfo.js
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import styled from 'styled-components';
+import UserService from '../../api/UserService';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class UserInfo extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.initialValues = this.initialValues.bind(this);
+ this.renderReadTemplatePermission = this.renderReadTemplatePermission.bind(this);
+ this.renderReadModelPermission = this.renderReadModelPermission.bind(this);
+ this.renderReadToscaPermission = this.renderReadToscaPermission.bind(this);
+ this.renderUpdateTemplatePermission = this.renderUpdateTemplatePermission.bind(this);
+ this.renderUpdateModelPermission = this.renderUpdateModelPermission.bind(this);
+ this.renderUpdateToscaPermission = this.renderUpdateToscaPermission.bind(this);
+ this.renderUserName = this.renderUserName.bind(this);
+ this.state = {
+ show: true,
+ userInfo: {permissionReadTemplate: true,
+ permissionReadCl: true,
+ permissionReadTosca: true,
+ permissionUpdateCl: true,
+ permissionUpdateTemplate: true,
+ permissionUpdateTosca: true,
+ userName: 'admin',
+ cldsVersion: '1.0.0'
+ }
+ };
+
+ }
+ initialValues() {
+ UserService.getUserInfo().then(userInfo => {
+ this.setState({ userInfo: userInfo })
+ });
+ }
+ handleClose() {
+ this.props.history.push('/');
+ }
+ renderReadTemplatePermission() {
+ if (this.state.userInfo["permissionReadTemplate"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Template" />
+ } else {
+ return;
+ }
+ }
+ renderReadModelPermission() {
+ if (this.state.userInfo["permissionReadCl"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Model" />
+ } else {
+ return;
+ }
+ }
+ renderReadToscaPermission() {
+ if (this.state.userInfo["permissionReadTosca"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Tosca" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateTemplatePermission() {
+ if (this.state.userInfo["permissionUpdateTemplate"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Template" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateModelPermission() {
+ if (this.state.userInfo["permissionUpdateCl"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Model" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateToscaPermission() {
+ if (this.state.userInfo["permissionUpdateTosca"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Tosca" />
+ } else {
+ return;
+ }
+ }
+ renderUserName() {
+ if (this.state.userInfo["userName"]) {
+ return <Form.Control plaintext readOnly defaultValue={this.state.userInfo["userName"]} />
+ } else {
+ return;
+ }
+ }
+ renderVersion() {
+ if (this.state.userInfo["cldsVersion"]) {
+ return <Form.Control plaintext readOnly defaultValue={this.state.userInfo["cldsVersion"]} />
+ } else {
+ return;
+ }
+ }
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} onEntered={this.initialValues}>
+ <Modal.Header closeButton>
+ <Modal.Title>User Info</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form.Group as={Row} controlId="userName">
+ <Form.Label column sm="3">Current User:</Form.Label>
+ <Col>{this.renderUserName()}</Col>
+ </Form.Group>
+ <Form.Group as={Row} controlId="cldsVersion">
+ <Form.Label column sm="3">CLDS Version:</Form.Label>
+ <Col>{this.renderVersion()}</Col>
+ </Form.Group>
+ <Form.Group as={Row} controlId="userPermissions">
+ <Form.Label column sm="3">User Permissions:</Form.Label>
+ <Col>
+ {this.renderReadTemplatePermission()}
+ {this.renderReadModelPermission()}
+ {this.renderReadToscaPermission()}
+ {this.renderUpdateTemplatePermission()}
+ {this.renderUpdateModelPermission()}
+ {this.renderUpdateToscaPermission()}
+ </Col>
+ </Form.Group>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/logs/LoopLogs.js b/ui-react/src/components/loop_viewer/logs/LoopLogs.js
new file mode 100644
index 000000000..b6a777a40
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/logs/LoopLogs.js
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import Table from 'react-bootstrap/Table';
+import LoopCache from '../../../api/LoopCache';
+import styled from 'styled-components';
+
+const LoopLogsHeaderDivStyled = styled.div`
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ padding: 10px 10px;
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+`
+const TableStyled = styled(Table)`
+
+ overflow: auto;
+`
+const TableRow = ({ logRow }) => (
+ <tr>
+ <td>{logRow.logInstant}</td>
+ <td>{logRow.logType}</td>
+ <td>{logRow.logComponent}</td>
+ <td>{logRow.message}</td>
+ </tr>
+
+)
+
+export default class LoopLogs extends React.Component {
+
+ state = {
+ loopCache: new LoopCache({}),
+ }
+ constructor(props) {
+ super(props);
+ this.renderLogs = this.renderLogs.bind(this);
+ this.state.loopCache = props.loopCache;
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.state.loopCache !== nextState.loopCache;
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ });
+ }
+
+ renderLogs() {
+ if (this.state.loopCache.getLoopLogsArray() != null) {
+ return (
+ this.state.loopCache.getLoopLogsArray().map(row => <TableRow logRow={row} />)
+ )
+ }
+ }
+
+ render() {
+ return (
+ <LoopLogsHeaderDivStyled>
+ <label>Loop Logs</label>
+ <TableStyled striped hover variant responsive>
+ <thead>
+ <tr>
+ <th><span align="left">Date</span></th>
+ <th><span align="left">Type</span></th>
+ <th><span align="left">Component</span></th>
+ <th><span align="right">Log</span></th>
+ </tr>
+ </thead>
+ <tbody>
+ {this.renderLogs()}
+ </tbody>
+ </TableStyled>
+ </LoopLogsHeaderDivStyled>
+
+ );
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/status/LoopStatus.js b/ui-react/src/components/loop_viewer/status/LoopStatus.js
new file mode 100644
index 000000000..141a41f51
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/status/LoopStatus.js
@@ -0,0 +1,105 @@
+/*-
+* ============LICENSE_START=======================================================
+* ONAP CLAMP
+* ================================================================================
+* Copyright (C) 2019 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============================================
+* ===================================================================
+*
+*/
+import React from 'react';
+import Table from 'react-bootstrap/Table';
+import styled from 'styled-components';
+import LoopCache from '../../../api/LoopCache';
+
+const LoopStatusViewDivStyled = styled.div`
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ padding: 10px 10px;
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+`
+
+const TableStyled = styled(Table)`
+ overflow: auto;
+`
+
+const TableRow = ({ statusRow }) => (
+ <tr>
+ <td>{statusRow.componentName}</td>
+ <td>{statusRow.stateName}</td>
+ <td>{statusRow.description}</td>
+ </tr>
+
+)
+
+export default class LoopStatus extends React.Component {
+ state = {
+ loopCache: new LoopCache({}),
+ }
+
+ constructor(props) {
+ super(props);
+ this.renderStatus = this.renderStatus.bind(this);
+ this.state.loopCache = props.loopCache;
+ }
+
+
+ renderStatus() {
+ if (this.state.loopCache.getComponentStates() != null) {
+ return Object.keys(this.state.loopCache.getComponentStates()).map((key) => {
+ console.debug("Adding status for: ",key);
+ var res={}
+ res[key]=this.state.loopCache.getComponentStates()[key];
+ return (<TableRow statusRow={{'componentName':key,'stateName':this.state.loopCache.getComponentStates()[key].componentState.stateName,'description':this.state.loopCache.getComponentStates()[key].componentState.description}} />)
+ })
+
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.state.loopCache !== nextState.loopCache;
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ });
+ }
+
+ render() {
+ return (
+ <LoopStatusViewDivStyled>
+ <label>Loop Status: {this.state.loopCache.getComputedState()}
+ </label>
+
+ <div >
+ <TableStyled striped hover variant responsive>
+ <thead>
+ <tr>
+ <th><span align="left">Component Name</span></th>
+ <th><span align="left">Component State</span></th>
+ <th><span align="right">Description</span></th>
+ </tr>
+ </thead>
+ <tbody>
+ {this.renderStatus()}
+ </tbody>
+ </TableStyled>
+ </div>
+ </LoopStatusViewDivStyled>
+ );
+ }
+}
+
diff --git a/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js b/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js
new file mode 100644
index 000000000..a409d2cd0
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js
@@ -0,0 +1,18 @@
+export default class LoopComponentConverter {
+
+ static buildMapOfComponents(loopCache) {
+ var componentsMap = new Map([]);
+ if (typeof (loopCache.getMicroServicePolicies()) !== "undefined") {
+ loopCache.getMicroServicePolicies().forEach(ms => {
+ componentsMap.set(ms.name, "/configurationPolicyModal/"+ms.name);
+ })
+ }
+ if (typeof (loopCache.getOperationalPolicies()) !== "undefined") {
+ loopCache.getOperationalPolicies().forEach(op => {
+ componentsMap.set(op.name, "/operationalPolicyModal");
+ })
+ }
+ componentsMap.set("OperationalPolicy","/operationalPolicyModal");
+ return componentsMap;
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/svg/LoopSvg.js b/ui-react/src/components/loop_viewer/svg/LoopSvg.js
new file mode 100644
index 000000000..3ac2f31fd
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/svg/LoopSvg.js
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import styled from 'styled-components';
+import LoopCache from '../../../api/LoopCache';
+import { withRouter } from "react-router";
+import LoopService from '../../../api/LoopService';
+import LoopComponentConverter from './LoopComponentConverter';
+
+const LoopViewSvgDivStyled = styled.div`
+ overflow: hidden;
+ background-color: ${props => (props.theme.loopViewerBackgroundColor)};
+ border: 1px solid;
+ border-color: ${props => (props.theme.loopViewerHeaderColor)};
+ margin-left: auto;
+ margin-right:auto;
+ text-align: center;
+
+`
+
+class LoopViewSvg extends React.Component {
+
+ static emptySvg = "<svg><text x=\"20\" y=\"40\">No LOOP (SVG)</text></svg>";
+
+ state = {
+ svgContent: LoopViewSvg.emptySvg,
+ loopCache: new LoopCache({}),
+ componentModalMapping: new Map([]),
+ }
+
+ constructor(props) {
+ super(props);
+ this.handleSvgClick = this.handleSvgClick.bind(this);
+ this.getSvg = this.getSvg.bind(this);
+ this.state.loopCache = props.loopCache;
+ this.state.componentModalMapping = LoopComponentConverter.buildMapOfComponents(props.loopCache);
+ this.getSvg(props.loopCache.getLoopName());
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.state.svgContent !== nextState.svgContent;
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ componentModalMapping: LoopComponentConverter.buildMapOfComponents(newProps.loopCache),
+
+ });
+ this.getSvg(newProps.loopCache.getLoopName());
+ }
+
+ getSvg(loopName) {
+ if (typeof loopName !== "undefined") {
+ LoopService.getSvg(loopName).then(svgXml => {
+ if (svgXml.length !== 0) {
+ this.setState({ svgContent: svgXml })
+ } else {
+ this.setState({ svgContent: LoopViewSvg.emptySvg })
+ }
+ });
+ }
+ }
+
+ handleSvgClick(event) {
+ console.debug("svg click event received");
+ var elementName = event.target.parentNode.parentNode.parentNode.getAttribute('data-element-id');
+ console.info("SVG element clicked", elementName);
+ this.props.history.push(this.state.componentModalMapping.get(elementName));
+ }
+
+ render() {
+ return (
+ <LoopViewSvgDivStyled dangerouslySetInnerHTML={{ __html: this.state.svgContent }} onClick={this.handleSvgClick}>
+
+ </LoopViewSvgDivStyled>
+ );
+ }
+}
+
+export default withRouter(LoopViewSvg); \ No newline at end of file
diff --git a/ui-react/src/components/loop_viewer/svg/example.svg b/ui-react/src/components/loop_viewer/svg/example.svg
new file mode 100644
index 000000000..a7c40ee27
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/svg/example.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="start-circle" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><circle fill="none" r="17" cx="18" cy="41"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="Arrow-82c14603-02fc-4df7-8977-9b10e4c775d1" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><line y2="41" fill="none" x1="35" x2="123" y1="41"/><polygon fill="none" points=" 121 39 121 43 125 41"/><polygon points=" 121 39 121 43 125 41" stroke="none"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="VES" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><rect fill="none" x="127" width="123" y="1" height="82"/></g><g fill-opacity="0" fill="rgb(0,0,0)" text-rendering="optimizeQuality" shape-rendering="geometricPrecision" stroke="rgb(0,0,0)" stroke-opacity="0" stroke-width="2"><rect x="127" width="123" y="1" height="82" stroke="none"/></g><g text-rendering="optimizeQuality" stroke-width="2" shape-rendering="geometricPrecision" font-family="sans-serif"><text x="176.5" xml:space="preserve" y="46.5" stroke="none">VES</text><line y2="83" fill="none" x1="147" x2="147" y1="1"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="Arrow-dbbb2d5a-e9c4-446d-92b9-c71908854434" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><line y2="41" fill="none" x1="250" x2="338" y1="41"/><polygon fill="none" points=" 336 39 336 43 340 41"/><polygon points=" 336 39 336 43 340 41" stroke="none"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="TCA_Jbv1z_v1_0_ResourceInstanceName1_tca" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><rect fill="none" x="342" width="123" y="1" height="82"/></g><g fill-opacity="0" fill="rgb(0,0,0)" text-rendering="optimizeQuality" shape-rendering="geometricPrecision" stroke="rgb(0,0,0)" stroke-opacity="0" stroke-width="2"><rect x="342" width="123" y="1" height="82" stroke="none"/></g><g text-rendering="optimizeQuality" stroke-width="2" shape-rendering="geometricPrecision" font-family="sans-serif"><text x="392" xml:space="preserve" y="46.5" stroke="none">TCA</text><line y2="61" fill="none" x1="342" x2="465" y1="61"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="Arrow-3892abbc-c49c-40df-984b-8959b6df44e6" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><line y2="41" fill="none" x1="465" x2="553" y1="41"/><polygon fill="none" points=" 551 39 551 43 555 41"/><polygon points=" 551 39 551 43 555 41" stroke="none"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="OperationalPolicy" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><rect fill="none" x="557" width="123" y="1" height="82"/></g><g fill-opacity="0" fill="rgb(0,0,0)" text-rendering="optimizeQuality" shape-rendering="geometricPrecision" stroke="rgb(0,0,0)" stroke-opacity="0" stroke-width="2"><rect x="557" width="123" y="1" height="82" stroke="none"/></g><g text-rendering="optimizeQuality" stroke-width="2" shape-rendering="geometricPrecision" font-family="sans-serif"><text x="564.5" xml:space="preserve" y="46.5" stroke="none">OperationalPolicy</text><line y2="1" fill="none" x1="557" x2="618" y1="42"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="Arrow-44a8b77e-d0eb-4c0d-82b6-0822ff35573f" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="2"><line y2="41" fill="none" x1="680" x2="768" y1="41"/><polygon fill="none" points=" 766 39 766 43 770 41"/><polygon points=" 766 39 766 43 770 41" stroke="none"/></g></g></g><g fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" font-family="'Dialog'" font-style="normal" data-element-id="stop-circle" stroke-linejoin="miter" font-size="12px" image-rendering="auto" stroke-dashoffset="0"><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"/><g><g shape-rendering="geometricPrecision" text-rendering="optimizeQuality" stroke-width="4"><circle fill="none" r="17" cx="789" cy="41"/></g></g></g></svg> \ No newline at end of file
diff --git a/ui-react/src/components/menu/MenuBar.js b/ui-react/src/components/menu/MenuBar.js
new file mode 100644
index 000000000..5022152e3
--- /dev/null
+++ b/ui-react/src/components/menu/MenuBar.js
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import Nav from 'react-bootstrap/Nav';
+import Navbar from 'react-bootstrap/Navbar';
+import NavDropdown from 'react-bootstrap/NavDropdown';
+import 'bootstrap-css-only/css/bootstrap.min.css';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom'
+
+const StyledLink = styled(Link)`
+ color: ${props => props.theme.menuColor};
+ background-color: ${props => props.theme.menuBackgroundColor};
+ font-weight: normal;
+ display: block;
+ width: 100%;
+ padding: .25rem 1.5rem;
+ clear: both;
+ text-align: inherit;
+ white-space: nowrap;
+ border: 0;
+ :hover {
+ text-decoration: none;
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+ }
+`;
+const StyledNavLink = styled(Nav.Link)`
+ color: ${props => props.theme.menuColor};
+ background-color: ${props => props.theme.menuBackgroundColor};
+ font-weight: normal;
+ padding: .25rem 1.5rem;
+ :hover {
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ color: ${props => props.theme.loopViewerHeaderFontColor}
+ }
+`;
+export default class MenuBar extends React.Component {
+
+ render () {
+ return (
+ <Navbar.Collapse>
+ <NavDropdown title="Closed Loop">
+ <NavDropdown.Item as={StyledLink} to="/openLoop">Open CL</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/loopProperties">Properties CL</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/closeLoop">Close Model</NavDropdown.Item>
+ </NavDropdown>
+ <NavDropdown title="Manage">
+ <NavDropdown.Item as={StyledLink} to="/submit">Submit</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/stop">Stop</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/restart">Restart</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/delete">Delete</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/deploy">Deploy</NavDropdown.Item>
+ <NavDropdown.Item as={StyledLink} to="/undeploy">UnDeploy</NavDropdown.Item>
+ </NavDropdown>
+ <NavDropdown title="View">
+ <NavDropdown.Item as={StyledLink} to="/refreshStatus">Refresh Status</NavDropdown.Item>
+ </NavDropdown>
+ <NavDropdown title="Help">
+ <StyledNavLink href="https://wiki.onap.org/" target="_blank">Wiki</StyledNavLink>
+ <StyledNavLink href="mailto:onap-discuss@lists.onap.org?subject=CLAMP&body=Please send us suggestions or feature enhancements or defect. If possible, please send us the steps to replicate any defect.">Contact Us</StyledNavLink>
+ <NavDropdown.Item as={StyledLink} to="/userInfo">User Info</NavDropdown.Item>
+ </NavDropdown>
+ </Navbar.Collapse>
+ );
+ }
+}
diff --git a/ui-react/src/components/menu/PerformActions.js b/ui-react/src/components/menu/PerformActions.js
new file mode 100644
index 000000000..9c34e141b
--- /dev/null
+++ b/ui-react/src/components/menu/PerformActions.js
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+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(),
+ loopAction: newProps.loopAction
+ });
+ }
+
+ componentDidMount() {
+ const action = this.state.loopAction;
+ const loopName = this.state.loopName;
+ console.log("Perform action:" + action);
+ LoopActionService.performAction(loopName, action).then(pars => {
+ alert("Action " + action + " successfully performed");
+ // refresh status and update loop logs
+ this.refreshStatus(loopName);
+ })
+ .catch(error => {
+ alert("Action " + action + " failed");
+ // refresh status and update loop logs
+ this.refreshStatus(loopName);
+ });
+
+ }
+
+ refreshStatus(loopName) {
+ LoopActionService.refreshStatus(loopName).then(data => {
+ this.props.updateLoopFunction(data);
+ this.props.history.push('/');
+ })
+ .catch(error => {
+ this.props.history.push('/');
+ });
+ }
+
+ render() {
+ return (
+ <StyledSpinnerDiv>
+ <Spinner animation="border" role="status">
+ </Spinner>
+ </StyledSpinnerDiv>
+ );
+ }
+}
diff --git a/ui-react/src/components/menu/RefreshStatus.js b/ui-react/src/components/menu/RefreshStatus.js
new file mode 100644
index 000000000..cf08655ee
--- /dev/null
+++ b/ui-react/src/components/menu/RefreshStatus.js
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+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 RefreshStatus extends React.Component {
+ state = {
+ loopName: this.props.loopCache.getLoopName()
+ };
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopName: newProps.loopCache.getLoopName()
+ });
+ }
+
+ componentDidMount() {
+ console.log("Refresh status for: " + this.state.loopName);
+ // refresh status and update loop logs
+ LoopActionService.refreshStatus(this.state.loopName).then(data => {
+ alert("Status successfully refreshed")
+ this.props.updateLoopFunction(data);
+ this.props.history.push('/');
+ })
+ .catch(error => {
+ alert("Status refreshing failed");
+ this.props.history.push('/');
+ });
+ }
+
+ render() {
+ return (
+ <StyledSpinnerDiv>
+ <Spinner animation="border" role="status">
+ </Spinner>
+ </StyledSpinnerDiv>
+ );
+ }
+}
diff --git a/ui-react/src/index.js b/ui-react/src/index.js
new file mode 100644
index 000000000..39df36427
--- /dev/null
+++ b/ui-react/src/index.js
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import OnapClamp from './OnapClamp';
+import { Route, BrowserRouter } from 'react-router-dom'
+
+
+const routing = (
+ <BrowserRouter forceRefresh={false}>
+ <Route path="/" component={OnapClamp}/>
+ </BrowserRouter>
+);
+
+ReactDOM.render(
+ routing,
+ document.getElementById('root')
+)
diff --git a/ui-react/src/logo.png b/ui-react/src/logo.png
new file mode 100644
index 000000000..c6f6857a5
--- /dev/null
+++ b/ui-react/src/logo.png
Binary files differ
diff --git a/ui-react/src/setupTests.js b/ui-react/src/setupTests.js
new file mode 100644
index 000000000..fc7b0dce1
--- /dev/null
+++ b/ui-react/src/setupTests.js
@@ -0,0 +1,4 @@
+import Enzyme from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+Enzyme.configure({ adapter: new Adapter() });
diff --git a/ui-react/src/theme/globalStyle.js b/ui-react/src/theme/globalStyle.js
new file mode 100644
index 000000000..cbd86b199
--- /dev/null
+++ b/ui-react/src/theme/globalStyle.js
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 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============================================
+ * ===================================================================
+ *
+ */
+
+import { createGlobalStyle } from 'styled-components';
+
+export const GlobalClampStyle = createGlobalStyle`
+ body {
+ padding: 0;
+ margin: 0;
+ font-family: ${props => props.theme.fontFamily};
+ font-size: ${props => props.theme.fontSize};
+ font-weight: normal;
+ color: ${props => props.theme.fontNormal};
+ background-color: ${props => props.theme.backgroundColor};
+ }
+
+ span {
+ font-family: ${props => props.theme.fontFamily};
+ font-size: ${props => props.theme.fontSize};
+ font-weight: bold;
+ color: ${props => props.theme.fontNormal};
+ background-color: ${props => props.theme.backgroundColor};
+ }
+
+ a {
+ font-family: ${props => props.theme.fontFamily};
+ font-size: ${props => props.theme.fontSize};
+ font-weight: bold;
+ color: ${props => props.theme.fontNormal};
+ background-color: ${props => props.theme.backgroundColor};
+
+ }
+
+ div {
+ font-family: ${props => props.theme.fontFamily};
+ font-size: ${props => props.theme.fontSize};
+ border-radius: 4px;
+ color: ${props => props.theme.fontNormal};
+ background-color: ${props => (props.theme.backgroundColor)};
+ margin-top: 1px;
+ }
+
+ svg {
+ overflow: hidden;
+ width: 100%;
+ height: 100%;
+ }
+`
+
+export const DefaultClampTheme = {
+ fontDanger: '#eb238e',
+ fontWarning: '#eb238e',
+ fontLight: '#ffffff',
+ fontDark: '#888888',
+ fontHighlight: '#ffff00',
+ fontNormal: 'black',
+
+ backgroundColor: '#eeeeee',
+ fontFamily: 'Arial, Sans-serif',
+ fontSize: '15px',
+
+ loopViewerBackgroundColor: 'white',
+ loopViewerFontColor: 'yellow',
+ loopViewerHeaderBackgroundColor: '#337ab7',
+ loopViewerHeaderFontColor: 'white',
+
+ menuBackgroundColor: 'white',
+ menuFontColor: 'black',
+ menuHighlightedBackgroundColor: '#337ab7',
+ menuHighlightedFontColor: 'white',
+};