diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a71870cdca3ece49bf90eb5e82ba1b04dc9737e5..6798c936a9ec04e109da4bc24b9feb17bd3a0bcc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,21 +49,20 @@ test:all: - echo "REPO_USER=${REPO_USER}" > $CI_PROJECT_DIR/reporc - echo "REPO_PASS=${REPO_PASS}" >> $CI_PROJECT_DIR/reporc - sudo scripts/test/fixture.sh create -f src/test/clmctest/rspec.json -r $CI_PROJECT_DIR -c all - - sudo mkdir /var/lib/lxc/test-runner/rootfs/vagrant/build - - sudo cp build/clmctest-SNAPSHOT.tar.gz /var/lib/lxc/test-runner/rootfs/vagrant/build - - sudo cp build/clmcservice-SNAPSHOT.tar.gz /var/lib/lxc/test-runner/rootfs/vagrant/build - - sudo lxc-attach -n test-runner -- pip3 install /vagrant/build/clmctest-SNAPSHOT.tar.gz - - sudo lxc-attach -n test-runner -- pip3 install /vagrant/build/clmcservice-SNAPSHOT.tar.gz - - sudo lxc-attach -n test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.scripts - - sudo lxc-attach -n test-runner -- pytest -s --tb=short -rfp --pyargs clmcservice.tests - - sudo lxc-attach -n test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.inputs - - sudo lxc-attach -n test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.monitoring - when: on_success - + - sudo mkdir /var/lib/lxd/containers/test-runner/rootfs/vagrant/build + - sudo cp build/clmctest-SNAPSHOT.tar.gz /var/lib/lxd/containers/test-runner/rootfs/vagrant/build + - sudo cp build/clmcservice-SNAPSHOT.tar.gz /var/lib/lxd/containers/test-runner/rootfs/vagrant/build + - sudo lxc exec test-runner -- pip3 install /vagrant/build/clmctest-SNAPSHOT.tar.gz + - sudo lxc exec test-runner -- pip3 install /vagrant/build/clmcservice-SNAPSHOT.tar.gz + - sudo lxc exec test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.scripts + - sudo lxc exec test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.inputs + - sudo lxc exec test-runner -- pytest -s --tb=short -rfp --pyargs clmctest.monitoring + when: on_success + clean: stage: clean only: - - schedules - script: - - sudo scripts/test/fixture.sh destroy -f src/test/clmctest/rspec.json - when: always + - schedules + script: + - sudo scripts/test/fixture.sh destroy -f src/test/clmctest/rspec.json -r $CI_PROJECT_DIR -c all + when: always diff --git a/README.md b/README.md index 0c12639f257a9e77292e0df2ec817ba9903b320c..b6db183816274575708cafea052c477837b27869 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ // © University of Southampton IT Innovation Centre, 2017 // // Copyright in this software belongs to University of Southampton -// IT Innovation Centre of Gamma House, Enterprise Road, +// IT Innovation Centre of Gamma House, Enterprise Road, // Chilworth Science Park, Southampton, SO16 7NS, UK. // // This software may not be used, sold, licensed, transferred, copied @@ -28,7 +28,7 @@ #### Authors -|Authors|Organisation| +|Authors|Organisation| |-|-| |[Michael Boniface](mailto:mjb@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)| |[Simon Crowle](mailto:sgc@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)| @@ -37,7 +37,7 @@ #### Documentation -Implementation documentation and discussion can be found in the docs directory. +Implementation documentation and discussion can be found in the docs directory. #### Testing @@ -46,89 +46,112 @@ Testing is implemented using pytest using the following convention: * The testing environment is Vagrant/Virtualbox for a base VM with LXC is installed for specific containers. * Tests are written in python using pytest * Related tests are stored in a python module `src/test/clmctest/<testmodule>` to create a suite of tests. All tests are stored in files test_*.py, there can be many tests per file, and many files per module -* Tests are executed against a set of LXC containers described in `src/test/clmctest/rspec.json`. +* Tests are executed against a set of LXC containers described in `src/test/clmctest/rspec.json`. * Tests are executed from the test-runner container on the VM using install python modules -Here's the instructions +Create a single VM with LXC installed and configured with lxcbr0 configured for the network 172.40.231.0/24 -`vagrant up` - -This will create a single VM with LXC installed and configured with lxcbr0 configured for the network 172.40.231.0/24 +```shell +vagrant up +``` -SSH into the VM +SSH into the VM: -`vagrant ssh` +```shell +vagrant ssh +``` -The containers are controlled using a script called /vagrant/scripts/test/fixtures.sh +The containers are controlled using a script called `/vagrant/scripts/test/fixtures.sh` -``` -Usage: fixture.sh create|start|stop|destroy [-f config_file] [-r repo_root] [-c container_name|all]" +```shell +Usage: fixture.sh create|start|stop|destroy [-f config_file] [-r repo_root] [-c container_name|all] ``` -To create all the services needed for integration tests +The containers created are defined an rspec.json file, there's an example here: `/vagrant/src/test/clmctest/rspec.json`. The `fixtures.sh` script defaults to look for a `rspec.json` in the current directory, you can specify a specific `rspec.json` file using the `-f` option. -``` -sudo su -/vagrant/scripts/test/fixture.sh create -f /vagrant/src/test/clmctest/rspec.json -c all +To create|start|stop|destroy specific services use the `-c` option e.g. + +```shell +/vagrant/scripts/test/fixture.sh create -f /vagrant/src/test/clmctest/rspec.json -c clmc-service ``` -The containers created are defined an rspec.json file, there's an example here `/vagrant/src/test/clmctest/rspec.json` +The installation of several of the services depend on accessing the Nexus binary repository (for the custom Telegraf agent). To do this, a username and password for the repository must be specified in a `reporc` file in the user's home directory, e.g. -The `fixtures.sh` script defaults to look for a rspec.json in the current directory, you can specify a specific rspec.json file using the -f option +```shell +REPO_USER=itinnov.flame.integration +REPO_PASS=xxxxxxx +``` -To create|start|stop|destroy specific services use the -c option e.g. +Create all the services needed for integration tests: +```shell +sudo su +/vagrant/scripts/test/fixture.sh create -f /vagrant/src/test/clmctest/rspec.json -c all ``` -/vagrant/scripts/test/fixture.sh create -f /vagrant/src/test/clmctest/rspec.json -c clmc-service -``` + +As part of the clmc-service installation, the service's unit tests have been run. The fixture script will fail if any individual service installation fails to install (or fails its tests). Attach to the test-runner to run the tests -`lxc-attach -n test-runner` +```shell +lxc-attach -n test-runner +``` -Here we need to define a make file but for now the commands are manual +Build and install the CLMC test Python module: -``` +```shell cd /vagrant/src/test python setup.py sdist --dist-dir=../../build -cd ../../src/service -python setup.py sdist --dist-dir=../../build pip3 install /vagrant/build/clmctest-SNAPSHOT.tar.gz -pip3 install /vagrant/build/clmcservice-SNAPSHOT.tar.gz ``` -The following modules are unit tests +The following module is unit tests: -``` +```shell pytest -s --pyargs clmctest.scripts -pytest -s --pyargs clmcservice.tests ``` -The following modules are integration tests +The following modules are integration tests: -``` +```shell pytest -s --pyargs clmctest.inputs -pytest -s --pyargs clmctest.monitoring +pytest -s --pyargs clmctest.monitoring ``` #### CI Testing -A vagrant VM is setup on givry in the directory `/home/mjb/flame-clmc` -The VM is started using a libvirt vagrant file through the following command: +A lxd container is setup on givry called `clmc-ci`. The container is priviledged and allows for nested containers. The container was created using the following commands -`VAGRANT_VAGRANTFILE=Vagrantfile.libvirt vagrant up` +``` +lxc launch ubuntu:18.04 clmc-ci -c security.privileged=true -c security.nesting=true +lxc network attach br_ci clmc-ci eth0 +lxc config device set clmc-ci eth0 ipv4.address 10.0.3.15 +``` -The VM has gitlab-runner installed:: +the container is then started and LXD initialised ``` -VAGRANT_VAGRANTFILE=Vagrantfile.libvirt vagrant up -VAGRANT_VAGRANTFILE=Vagrantfile.libvirt vagrant ssh -- -tt "sudo /vagrant/scripts/test/install-git-runner.sh" +lxc start clmc-ci +lxc exec clmc-ci -- bash +lxc init ``` -Runners can be installed on developers virtualbox VMs if necessary, in that case you can just use +Follow the interactive prompts, use the defaults apart from the storage pool type, use `dir` type + +Now configure ip table persistence, this is needed for the fixtures script to work but not for CI ``` -vagrant up -vagrant ssh -- -tt "sudo /vagrant/scripts/test/install-git-runner.sh" -``` \ No newline at end of file +echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections +echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections +apt-get -y install iptables-persistent +``` + +The install git runner file was just copied to the container from a tmp director + +``` +cp /tmp/install-git-runner.sh /var/lib/lxd/containers/clmc-ci/rootfs/tmp +chmod 755 /var/lib/lxd/containers/clmc-ci/rootfs/tmp +``` + +Note that the branch must be "protected" for the secret repo rc variables to be passed to the CI script diff --git a/Vagrantfile b/Vagrantfile index a45ee26605c74dce29f9e5269e3fac7a3611b409..ae26dee0f94f528bc889d52bc5a317b1fca380ed 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,32 +1,29 @@ $lxc_script = <<-SCRIPT apt-get update +apt-get install python3 python3-setuptools jq -y # install util for persistent ip tables echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections apt-get -y install iptables-persistent -# install lxc -apt-get install lxc lxc-templates wget bridge-utils jq -y -lxc-checkconfig - -# configure lxc for specific CIDR network -touch /etc/lxc/dnsmasq.conf -sed -i s/10.0.3/172.40.231/g /etc/default/lxc-net -sed -i s/#LXC_DHCP_CONFILE/LXC_DHCP_CONFILE/g /etc/default/lxc-net -service lxc-net restart +lxd init --auto --storage-backend dir +lxc network create lxcbr0 ipv6.address=none ipv4.address=172.40.231.1/24 ipv4.nat=true # enable NTP # use network time to make sure we are synchronised echo "Disabling timesyncd..." timedatectl set-ntp no -until timedatectl | grep -m 1 "Network time on: no"; -do - echo "Waiting for timesyncd to turn off.." - sleep 1 -done -apt-get install ntp + +# The following hangs with bionic +#until timedatectl | grep -m 1 "Network time on: no"; +#do +# echo "Waiting for timesyncd to turn off.." +# sleep 1 +#done + +apt-get install ntp -y echo "timesync set to ntpd" # set timezone to London @@ -35,7 +32,8 @@ timedatectl set-timezone Europe/London SCRIPT Vagrant.configure("2") do |config| - config.vm.box = "ubuntu/xenial64" + config.vm.box = "ubuntu/bionic64" + config.disksize.size = '50GB' config.vm.provider "virtualbox" do |vb| vb.cpus = 4 vb.memory = "8192" diff --git a/docs/clmc-service.md b/docs/clmc-service.md index ab1179f4664ce39634802f446ab8e379bc6b3cca..ed81c97dce20a74be753db24c2967cc5435dc241 100644 --- a/docs/clmc-service.md +++ b/docs/clmc-service.md @@ -2,7 +2,7 @@ // © University of Southampton IT Innovation Centre, 2018 // // Copyright in this software belongs to University of Southampton -// IT Innovation Centre of Gamma House, Enterprise Road, +// IT Innovation Centre of Gamma House, Enterprise Road, // Chilworth Science Park, Southampton, SO16 7NS, UK. // // This software may not be used, sold, licensed, transferred, copied @@ -25,8 +25,8 @@ #### **Authors** -|Authors|Organisation| -|---|---| +|Authors|Organisation| +|---|---| |[Nikolay Stanchev](mailto:ns17@it-innovation.soton.ac.uk)|[University of Southampton, IT Innovation Centre](http://www.it-innovation.soton.ac.uk)| #### Description @@ -35,7 +35,7 @@ This document describes the CLMC service and its API endpoints. The CLMC service It offers different API endpoints to configure and control the aggregator as well as a CRUD API for service function endpoints configuration data and Graph API for calculating round trip time. All source code, tests and configuration files of the service can be found in the **src/service** folder. -#### Graph API Endpoints +## Graph API Endpoints * **Assumptions** * For each service function, there is a field/fields from which the service function response time (service delay) can be derived. @@ -49,13 +49,13 @@ It offers different API endpoints to configure and control the aggregator as wel This API method sends a request to the CLMC service to build a graph related to the time range declared with the *from* and *to* URL parameters. * Request: - + Expects a JSON-formatted request body which declares the database, retention policy and service function chain instance for which the graph is built. The request should also include the service functions that must be included in the graph along with the measurement name, response time field, request size field and response size field for each service function. The declared fields could be influx functions across multiple fields. - - * Request Body Example: - + + * Request Body Example: + ```json { "database": "MSDemo", @@ -77,34 +77,34 @@ It offers different API endpoints to configure and control the aggregator as wel } } ``` - + These parameters are then filled in the following influx query template: - + ``` SELECT {0} AS mean_response_time, {1} AS mean_request_size, {2} AS mean_response_size FROM "{3}"."{4}".{5} WHERE sfc_i='{6}' and time>={7} and time<{8} GROUP BY ipendpoint, location, sf_i ``` - + E.g. for the minio service function, the following query will be used to retrieve the data from influx (request url is /graph/build?from=1528385420&to=1528385860): - + ``` SELECT mean(sum)/mean(count) AS mean_response_time, mean(request_size)/mean(count) AS mean_request_size, mean(response_size)/mean(count) AS mean_response_size FROM "MSDemo"."autogen".minio_http_requests_duration_seconds WHERE sfc_i='MSDemo_1' and time>=1528385420000000000 and time<1528385860000000000 GROUP BY ipendpoint, location, sf_i ``` - + N.B. timestamps are converted to nano seconds. - + * Response: The response of this request is a JSON content, which contains all request parameters used to build the graph, along with a request UUID. This request ID can then be used to manage the temporal subgraph that was created in response to this request. - + Returns a 400 Bad Request error if the request body is invalid. - + Returns a 400 Bad Request error if the request URL parameters are invalid or missing. - + Returns a 400 Bad Request error if the service function chain instance ID is not in the format `<sfcID>_<numberID>` - - * Response Body Example: - + + * Response Body Example: + ```json { "database": "MSDemo", @@ -132,27 +132,27 @@ It offers different API endpoints to configure and control the aggregator as wel } } } - ``` + ``` * **DELETE** ***/graph/temporal/{graph_id}*** This API method sends a request to delete the temporal graph associated with a given request UUID (retrieved from the response of a build-graph request). The request UUID must be given in the request URL, e.g. request sent to */graph/temporal/75df6f8d-3829-4fd8-a3e6-b3e917010141* - + * Response: The response of this request is a JSON content, which contains the request UUID and the number of deleted nodes. - + Returns a 404 Not Found error if the request UUID is not associated with any nodes in the graph. - * Response Body Example: - + * Response Body Example: + ```json { "uuid": "75df6f8d-3829-4fd8-a3e6-b3e917010141", "deleted": 5 } - ``` + ``` * **GET** ***/graph/temporal/{graph_id}/round-trip-time?compute_node={compute_node_id}&endpoint={endpoint_id}*** @@ -163,15 +163,15 @@ It offers different API endpoints to configure and control the aggregator as wel The response of this request is a JSON content, which contains the result from the Cypher query including forward latencies, reverse latencies and service function response time along with the calculated round trip time and global tag values for the given service function endpoint. - + Returns a 400 Bad Request error if the URL parameters are invalid - + Returns a 404 Not Found error if the request UUID and the endpoint ID are not associated with an endpoint node in the graph. - + Returns a 404 Not Found error if the compute node ID is not associated with a compute node in the graph. - * Response Body Example: - + * Response Body Example: + ```json { "request_size": 2048, @@ -180,11 +180,11 @@ It offers different API endpoints to configure and control the aggregator as wel "forward_latencies": [ 22, 11 ], - "total_forward_latency": 0, + "total_forward_latency": 33, "reverse_latencies": [ 15, 18 ], - "total_reverse_latency": 0, + "total_reverse_latency": 33, "response_time": 15.75, "round_trip_time": 81.75, "global_tags": { @@ -198,13 +198,13 @@ It offers different API endpoints to configure and control the aggregator as wel "host": "host2" } } - ``` - + ``` + Here, the *forward_latencies* and *reverse_latencies* lists represent the latency experienced at each hop between compute nodes. For example, if the path was DC2-DC3-DC4 and the SF endpoint was hosted on DC4, the response data shows that latency(DC2-DC3) = 22, latency(DC3-DC4) = 11, latency(DC4-DC3) = 15, latency(DC3-DC2) = 18, response_time(minio_1_ep1) = 15.75 - + N.B. if the endpoint is hosted on the compute node identified in the URL parameter, then there will be no network hops between compute nodes, so the latency lists would be empty, example: - + ```json { "request_size": 2048, @@ -227,22 +227,23 @@ It offers different API endpoints to configure and control the aggregator as wel "host": "host2" } } - ``` + ``` +## Aggregator API Endpoints -#### Aggregator API Endpoints +**Note: this API is deprecated. The graph API should be used to compute RTT instead.** * **GET** ***/aggregator/config*** This API method retrieves information about the configuration of the aggregator. - + * Response: - + Returns a JSON-formatted response with the configuration data of the aggregator - *aggregator_report_period*, *aggregator_database_name*, *aggregator_database_url*. - + * Response Body Example: - + ```json { "aggregator_report_period": 5, @@ -254,14 +255,14 @@ It offers different API endpoints to configure and control the aggregator as wel * **PUT** ***/aggregator/config*** This API method updates the configuration of the aggregator. - + * Request: - + Expects a JSON-formatted request body with the new configuration of the aggregator. The body should contain only three key fields - *aggregator_report_period* (positive integer, seconds), *aggregator_database_name* and *aggregator_database_url* (a valid URL). - - * Request Body Example: - + + * Request Body Example: + ```json { "aggregator_report_period": 25, @@ -269,14 +270,14 @@ It offers different API endpoints to configure and control the aggregator as wel "aggregator_database_url": "http://172.50.231.61:8086" } ``` - + * Response: - - The body of the request is first validated before updating the configuration. If validation is successful, returns + + The body of the request is first validated before updating the configuration. If validation is successful, returns a JSON-formatted response with the new configuration data. Otherwise, an **HTTP Bad Request** response is returned. - + * Response Body Example: - + ```json { "aggregator_report_period": 25, @@ -284,15 +285,15 @@ It offers different API endpoints to configure and control the aggregator as wel "aggregator_database_url": "http://172.50.231.61:8086" } ``` - + * Notes: - + If the configuration is updated, while the aggregator is running, it is not automatically restarted. An explicit API call must be made with a *restart* request to apply the updated configuration. In the case of such PUT request as the one described above, the response will contain more information indicating that the configuration of the aggregator is in a malformed state. - + * Response Body Example: - + ```json { "aggregator_report_period": 125, @@ -306,22 +307,22 @@ It offers different API endpoints to configure and control the aggregator as wel * **GET** ***/aggregator/control*** This API method retrieves information about the status of the aggregator - whether it is running or not. - + * Response: - + Returns a JSON-formatted response with the status data of the aggregator - *aggregator_running* field. If the aggregator is running in a malformed state, the response will also indicate this with two additional fields - *malformed* and *comment*. - + * Response Body Example: - + ```json { "aggregator_running": true } ``` - + * Response Body Example - for malformed configuration: - + ```json { "aggregator_running": true, @@ -333,64 +334,66 @@ It offers different API endpoints to configure and control the aggregator as wel * **PUT** ***/aggregator/control*** This API method updates the status of the aggregator - a user can start, stop or restart it. - + * Request: - + Expects a JSON-formatted request body with the new status of the aggregator. The body should contain only one key field - *action* (the action to undertake, which can be **start**, **restart** or **stop**) - - * Request Body Example: - + + * Request Body Example: + ```json { "action": "start" } ``` - + * Response: - + The body of the request is first validated before taking any actions. If the action is not one of the listed above, then the validation will fail. If validation is successful, returns a JSON-formatted response with the new status of the aggregator. Otherwise, an **HTTP Bad Request** response is returned. - + * Response Body Example: - + ```json { "aggregator_running": true } ``` - + * Notes: - + * If a **start** action is requested, while the aggregator is running, then the request will be ignored. To restart the aggregator, a user should use a **restart** action. - + * If a **stop** action is requested, while the aggregator is not running, then the request will be ignored. - + * A request with a **restart** action, while the aggregator is not running, has the same functionality as a request with a **start** action. - + * The functionality of a request with a **restart** action is the same as the functionlity of a **stop** action followed by a **start** action. - -#### CRUD API for service function endpoint configurations + +## CRUD API for service function endpoint configurations + +**Note: this API is experimental and is not intended to be used** * **GET** ***/whoami/endpoints*** This API method retrieves all service function endpoint configurations in a JSON format. - + * Response: - + Returns a JSON-formatted response - a list of JSON objects, each object representing a service function endpoint configuration. - + * Response Body Example: - No service function endpoint configurations found. ```json [] ``` - + - Multiple service function endpoint configurations found. ```json [ @@ -418,17 +421,17 @@ It offers different API endpoints to configure and control the aggregator as wel * **GET** ***/whoami/endpoints/instance?sr={sr_id}&sf_i={sf_instance_id}&sf_endpoint={sf_endpoint_id}*** This API method retrieves the uniquely defined service function endpoint configuration associated with the given URL parameters - sr, sf_i and sf_endpoint. - + * Response: - + Returns a JSON-formatted response - a JSON object representing the service function endpoint configuration if it exists. - + Returns a 404 Not Found error if there is no service function endpoint configuration associated with the given URL parameters. - + Returns a 400 Bad Request error if the url parameters are invalid. - + * Response Body Example: - + - Request made to /whoami/endpoints/instance?sr=sr_1&sf_i=sf_i_1&sf_endpoint=sf_endpoint_1 ```json { @@ -445,12 +448,12 @@ It offers different API endpoints to configure and control the aggregator as wel * **POST** ***/whoami/endpoints*** This API method creates a new service function endpoint configuration. - + * Request: - + Expects a JSON-formatted request body with the new service function endpoint configuration. - - * Request Body Example: + + * Request Body Example: ```json { @@ -465,14 +468,14 @@ It offers different API endpoints to configure and control the aggregator as wel ``` * Response - + Returns a JSON-formatted response - a JSON object representing the service function endpoint configuration that was created. - + Returns a 400 Bad Request error if the request body is invalid. - + Returns a 409 Conflict error if there exists another service function endpoint configuration with the same 'sr', 'sf_i' and 'sf_endpoint' values. - - * Response Body Example: + + * Response Body Example: ```json { @@ -490,12 +493,12 @@ It offers different API endpoints to configure and control the aggregator as wel This API method replaces the uniquely defined service function endpoint configuration associated with the given URL parameters - sr, sf_i and sf_endpoint with a new service function endpoint configuration given in the request body (JSON format). It can also be used for updating. - + * Request: - + Expects a JSON-formatted request body with the new service function endpoint configuration. - - * Request Body Example: + + * Request Body Example: ```json { @@ -510,18 +513,18 @@ It offers different API endpoints to configure and control the aggregator as wel ``` * Response - + Returns a JSON-formatted response - a JSON object representing the new service function endpoint configuration that was created (updated). - + Returns a 400 Bad Request error if the request body is invalid. - + Returns a 400 Bad Request error if the url parameters are invalid. - + Returns an 404 Not Found error if there is no service function endpoint configuration associated with the given URL parameters. - + Returns a 409 Conflict error if there exists another service function endpoint configuration with the same 'sr', 'sf_i' and 'sf_endpoint' values as the ones in the request body. - - * Response Body Example: + + * Response Body Example: - Request made to /whoami/endpoints/instance?sr=sr_1&sf_i=sf_i_1&sf_endpoint=sf_endpoint_1 ```json @@ -539,17 +542,17 @@ It offers different API endpoints to configure and control the aggregator as wel * **DELETE** ***/whoami/endpoints/instance?sr={sr_id}&sf_i={sf_instance_id}&sf_endpoint={sf_endpoint_id}*** This API method deletes the uniquely defined service function endpoint configuration associated with the given URL parameters - sr, sf_i and sf_endpoint. - + * Response: - + Returns the JSON representation of the deleted object. - + Returns an 404 Not Found error if there is no service function endpoint configuration associated with the given URL parameters. - + Returns a 400 Bad Request error if the url parameters are invalid. - + * Response Body Example: - + - Request made to /whoami/endpoints/instance?sr=sr_1&sf_i=sf_i_1&sf_endpoint=sf_endpoint_1 ```json { @@ -563,55 +566,56 @@ It offers different API endpoints to configure and control the aggregator as wel } ``` -#### Installing and running the CLMC service +## Installing and running the CLMC service Before installing the CLMC service and its dependencies, it is recommended to use a python virtual environment. To easily manage virtual environments, **virtualenvwrapper** can be used. -``` +```shell pip3 install virtualenvwrapper ``` To create a virtual environment use the **mkvirtualenv** command: -``` +```shell mkvirtualenv CLMC ``` When created, you should already be set to use the new virtual environment, but to make sure of this use the **workon** command: -``` +```shell workon CLMC ``` -Now, any installed libraries will be installed relative to this environment only. +Now, any installed libraries will be installed relative to this environment only. The easiest way to install and use the CLMC service locally is to use **pip**. Navigate to the clmc-service folder: -``` + +```shell cd src/service ``` Test the CLMC service using **tox** along with the ***tox.ini*** configuration file. If tox is not installed run: -``` +```shell pip3 install tox ``` After it is installed, simply use the **tox** command: -``` +```shell tox ``` Then install the service. -``` +```shell pip3 install . ``` Finally, start the service on localhost by using pyramid's **pserve** command line utility: -``` +```shell pserve production.ini ``` diff --git a/scripts/clmc-service/install-clmc-service.sh b/scripts/clmc-service/install-clmc-service.sh index 3c6bc443e1c1cea9c898e703db462d5beadb3c4f..75a6453bbfb59af31ad783d215f090a25ab6b489 100755 --- a/scripts/clmc-service/install-clmc-service.sh +++ b/scripts/clmc-service/install-clmc-service.sh @@ -3,7 +3,7 @@ # Get command line parameters if [ "$#" -ne 3 ]; then echo "Error: illegal number of arguments: "$# - echo "Usage: install.sh INFLUX_URL DATABASE_NAME REPORT_PERIOD" + echo "Usage: install-clmc-service.sh INFLUX_URL DATABASE_NAME REPORT_PERIOD" exit 1 fi @@ -12,6 +12,7 @@ DATABASE_NAME=$2 REPORT_PERIOD=$3 apt-get update +apt-get install libssl-dev -y # Create the database for the WHOAMI API apt-get install -y postgresql postgresql-contrib @@ -26,7 +27,6 @@ apt-get install -y python3 python3-pip curl update-alternatives --install /usr/bin/python python /usr/bin/python3 10 echo "----> Installing virtualenv and wrapper" -apt-get install -y python3-virtualenv virtualenvwrapper pip3 install virtualenv pip3 install virtualenvwrapper @@ -103,7 +103,7 @@ mkdir -p /var/log/flame/clmc echo "----> Initialising CLMC database" initialize_clmcservice_db production.ini if [ $? -ne 0 ] ; then - echo "Failed: switching to CLMC python environment" + echo "Failed: initialising CLMC database" exit 1 fi diff --git a/scripts/clmc-service/install-neo4j.sh b/scripts/clmc-service/install-neo4j.sh index 64188c4444f5b9674bb71fc5c9b48ae424dbcd4c..e0a7deba8cf7d0ecbfbbbe0adee2d099f251b325 100755 --- a/scripts/clmc-service/install-neo4j.sh +++ b/scripts/clmc-service/install-neo4j.sh @@ -35,6 +35,8 @@ echo "neo4j ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers # the following set of commands install everything as neo4j with some run as sudo. # Expected to only have to run the install as neo4j however it does not work unless all of these are # run as neo4j. hence the weird su neo4j -c "sudo..." + +echo "----->Installing neo4j" su neo4j -c "sudo apt-get install neo4j=1:3.4.0 -y" su neo4j -c "sed -i s/\#dbms.connectors.default_listen_address=0.0.0.0/dbms.connectors.default_listen_address=0.0.0.0/g /etc/neo4j/neo4j.conf" @@ -48,14 +50,10 @@ su neo4j -c "sudo systemctl enable neo4j" su neo4j -c "sudo systemctl start neo4j" ### waiting for the service to start +echo "----->Waiting for neo4j service to start" end="$((SECONDS+60))" while true; do nc -w 2 localhost 7687 && break [[ "${SECONDS}" -ge "${end}" ]] && exit 1 sleep 1 done - -apt-get -y install python3 python3-pip -update-alternatives --install /usr/bin/python python /usr/bin/python3 10 -apt-get update -pip3 install influxdb py2neo diff --git a/scripts/clmc-service/install.sh b/scripts/clmc-service/install.sh index 16e86e3dda31c4071737bbca568b9c869dc61651..420dddab39d17444c001114ee0427b7167e4b60d 100755 --- a/scripts/clmc-service/install.sh +++ b/scripts/clmc-service/install.sh @@ -25,7 +25,14 @@ #///////////////////////////////////////////////////////////////////////// # Force fail on command fail (off for now as virtualenvwrapper install fails) -# set -euo pipefail +set -euo pipefail + +# Get command line parameters +if [ "$#" -ne 3 ]; then + echo "Error: illegal number of arguments: "$# + echo "Usage: install.sh INFLUX_URL DATABASE_NAME REPORT_PERIOD" + exit 1 +fi # Ensure everything runs in directory of the parent script cd `dirname $0` diff --git a/scripts/test/fixture.sh b/scripts/test/fixture.sh index 45c7a00994e7d341e44a727c082d1301f295cfe5..0aa2f4c0d55663007a87daa01e5f8fd7a5783057 100755 --- a/scripts/test/fixture.sh +++ b/scripts/test/fixture.sh @@ -15,46 +15,61 @@ create() { service_name=$1 config_file=$2 repo_root=$3 - if ! lxc-info -n ${service_name}; then + if ! lxc list | grep ${service_name}; then # create a container with a static ip address echo "Creating container: ${service_name}" SERVICE=$(jq --arg NAME ${service_name} '.[] | select(.name==$NAME)' ${config_file}) echo $SERVICE ip=$(echo $SERVICE | jq -r '.ip_address') - echo "dhcp-host=${service_name},${ip}" >> /etc/lxc/dnsmasq.conf - service lxc-net restart - lxc-create -t download -n ${service_name} -- --dist ubuntu --release xenial --arch amd64 + lxc init ubuntu:16.04 ${service_name} + echo "Creating network: ${service_name}" + lxc network attach lxcbr0 ${service_name} eth0 + lxc config device set ${service_name} eth0 ipv4.address ${ip} + lxc config set ${service_name} security.privileged true # copy flame clmc files into the root container echo "Copying files to rootfs" - container_dir="/var/lib/lxc/"${service_name}"/rootfs" + container_dir="/var/lib/lxd/containers/"${service_name}"/rootfs" container_vagrant_dir=${container_dir}"/vagrant" - mkdir -p ${container_vagrant_dir} - cp -f ${repo_root}/reporc "${container_vagrant_dir}" - cp -rf ${repo_root}/scripts ${container_vagrant_dir} + mkdir -p ${container_vagrant_dir} + cp -f ${repo_root}/reporc ${container_vagrant_dir} + cp -rf ${repo_root}/scripts ${container_vagrant_dir} cp -rf ${repo_root}/src ${container_vagrant_dir} + chown -R 100000:100000 ${container_vagrant_dir} # start the container echo "Starting: ${service_name}" - lxc-start -n ${service_name} - echo "Waiting for container to start: ${service_name}" - STARTED="0" - while [ "$STARTED" == "0" ]; do STARTED=$(lxc-info -n ${service_name} -i | wc -l); done; + lxc start ${service_name} + while :; do + echo "Waiting for container to start: ${service_name}" + lxc file pull ${service_name}/etc/resolv.conf - | grep -q nameserver && break + sleep 1 + done # provision software into each container echo "Provisioning: ${service_name}" - if [ ${service_name} == "clmc-service" ] - then + if [ ${service_name} == "clmc-service" ]; then influxdb_url=$(echo $SERVICE | jq -r '.influxdb_url') database_name=$(echo $SERVICE | jq -r '.database_name') report_period=$(echo $SERVICE | jq -r '.report_period') cmd="/vagrant/scripts/clmc-service/install.sh ${influxdb_url} ${database_name} ${report_period}" - lxc-attach -n ${service_name} -v REPO_ROOT="/vagrant" -- ${cmd} + echo "Provisioning command ${cmd}" + lxc exec ${service_name} --env REPO_ROOT="/vagrant" -- ${cmd} + exit_code=$? + if [ $exit_code != 0 ]; then + echo "clmc-service installation failed with exit code ${exit_code}" + exit 1 + fi elif [ ${service_name} == "test-runner" ] then cmd=/vagrant/src/test/clmctest/services/pytest/install.sh - lxc-attach -n ${service_name} -- ${cmd} + lxc exec ${service_name} -- ${cmd} + exit_code=$? + if [ $exit_code != 0 ]; then + echo "test-runner installation failed with exit code ${exit_code}" + exit 1 + fi else # get container parameters location=$(echo $SERVICE | jq -r '.location') @@ -69,29 +84,40 @@ create() { # install service function specific software cmd=/vagrant/src/test/clmctest/services/${sf_id}/install.sh - lxc-attach -n ${service_name} -v REPO_ROOT="/vagrant" -- ${cmd} - + lxc exec ${service_name} --env REPO_ROOT="/vagrant" -- ${cmd} + exit_code=$? + if [ $exit_code != 0 ]; then + echo "${sf_id} installation failed with exit code ${exit_code}" + exit 1 + fi # install telegraf cmd=/vagrant/scripts/clmc-agent/install.sh - lxc-attach -n ${service_name} -v REPO_ROOT="/vagrant" -- ${cmd} + lxc exec ${service_name} --env REPO_ROOT="/vagrant" -- /vagrant/scripts/clmc-agent/install.sh + + # check that telegraf installed (it may not have if the reporc file was not present or Nexus server was down) + if lxc-attach -n ${service_name} -- ls /etc/telegraf |& grep 'ls: cannot access'; then + echo "Telegraf agent failed to install (check reporc?)" + exit 1 + fi # stop telegraf before changing the configs - lxc-attach -n ${service_name} -- service telegraf stop + lxc exec ${service_name} -- service telegraf stop # copy telegraf configuration templates cp -f ${repo_root}/scripts/clmc-agent/telegraf.conf ${container_dir}/etc/telegraf/ cp -f ${repo_root}/scripts/clmc-agent/telegraf_output.conf ${container_dir}/etc/telegraf/telegraf.d/ # copy the 'host' config into all service containers - cp ${repo_root}/src/test/clmctest/services/host/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/ + cp -f ${repo_root}/src/test/clmctest/services/host/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/ # copy the service-specific config - cp ${repo_root}/src/test/clmctest/services/${sf_id}/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/ + cp -f ${repo_root}/src/test/clmctest/services/${sf_id}/telegraf*.conf ${container_dir}/etc/telegraf/telegraf.d/ + chown -R 100000:100000 ${container_dir}/etc/telegraf/ # replace telegraf template with container parameters cmd="/vagrant/scripts/clmc-agent/configure.sh ${location} ${sfc_id} ${sfc_id_instance} ${sf_id} ${sf_id_instance} ${ipendpoint_id} ${sr_id} ${influxdb_url} ${database_name}" - lxc-attach -n ${service_name} -- ${cmd} + lxc exec ${service_name} -- ${cmd} # start telegraf - lxc-attach -n ${service_name} -- service telegraf start + lxc exec ${service_name} -- service telegraf start fi # set forward ports @@ -103,7 +129,7 @@ create() { } guest_port=$(_jq '.guest') host_port=$(_jq '.host') - iptables -t nat -A PREROUTING -p tcp -i enp0s3 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port} + iptables -t nat -I PREROUTING -i enp0s3 -p TCP -d 10.0.2.15 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port} done fi fi @@ -111,33 +137,28 @@ create() { start() { service_name=$1 - if lxc-info -n ${service_name}; then + if lxc info ${service_name}; then echo "Starting container: ${service_name}" - lxc-start -n ${service_name} + lxc start ${service_name} fi } stop() { service_name=$1 - if lxc-info -n ${service_name}; then + if lxc info ${service_name}; then echo "Stopping container: ${service_name}" - lxc-stop -n ${service_name} + lxc stop -n ${service_name} fi } destroy() { service_name=$1 config_file=$2 - if lxc-info -n ${service_name}; then + if lxc list | grep ${service_name}; then echo "Stopping container: ${service_name}" - lxc-stop -n ${service_name} + lxc stop ${service_name} echo "Destroying container: ${service_name}" - lxc-destroy -n ${service_name} - - # remove static ip - SERVICE=$(jq --arg NAME ${service_name} '.[] | select(.name==$NAME)' ${config_file}) - ip=$(echo $SERVICE | jq -r '.ip_address') - sed -i "/dhcp-host=${service_name},/d" /etc/lxc/dnsmasq.conf + lxc delete ${service_name} # remove forward ports ports=$(echo $SERVICE | jq -r '.forward_ports') @@ -149,7 +170,7 @@ destroy() { } guest_port=$(_jq '.guest') host_port=$(_jq '.host') - iptables -t nat -D PREROUTING -p tcp -i enp0s3 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port} + iptables -t nat -D PREROUTING -i enp0s3 -p TCP -d 10.0.2.15 --dport ${host_port} -j DNAT --to-destination ${ip}:${guest_port} done fi fi @@ -220,6 +241,6 @@ done -echo "------>Create iptables summary" +echo -e "\n\n------>Create iptables summary" iptables -t nat -L -n -v iptables-save > /etc/iptables/rules.v4 diff --git a/scripts/test/install-git-runner.sh b/scripts/test/install-git-runner.sh index 4b50e4404865b55005239435872aa75d38d6518b..10aed80a5ee9421f3e449ddf179499b6004713ec 100755 --- a/scripts/test/install-git-runner.sh +++ b/scripts/test/install-git-runner.sh @@ -12,5 +12,7 @@ gitlab-runner start gitlab-runner register -n --url https://gitlab.it-innovation.soton.ac.uk/ --r sN3wMQp8EiAv7znNwG5s --executor shell --locked true --name ${HOSTNAME} --description ${HOSTNAME} --run-untagged true -apt-get -y install python3 python3-pip python-influxdb +# install test prerequisites + +apt-get -y install python3 python3-pip python-influxdb jq update-alternatives --install /usr/bin/python python /usr/bin/python3 10