Merge changes from topic "collectd_6_testing"
authorEmma Foley <efoley@redhat.com>
Fri, 17 Dec 2021 13:07:53 +0000 (13:07 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Fri, 17 Dec 2021 13:07:53 +0000 (13:07 +0000)
* changes:
  Add a playbook that tests collectd 6 vs collectd5
  [docker][ansible] Add a playbook for building the containers
  Create a flask app to show metrics from collectd

14 files changed:
docker/ansible/collectd6_test.yml [new file with mode: 0644]
docker/ansible/collectd_build.yml [new file with mode: 0644]
docker/ansible/roles/build_collectd/tasks/main.yml [new file with mode: 0644]
docker/ansible/roles/run_collectd/tasks/main.yml
docker/ansible/roles/run_collectd/vars/main.yml
docker/flask_app/Dockerfile [new file with mode: 0644]
docker/flask_app/README [new file with mode: 0644]
docker/flask_app/flask_app.py [new file with mode: 0644]
docker/flask_app/requirements.txt [new file with mode: 0644]
docs/release/release-notes/notes/ansible-build-containers-b4a4cc9cb70f83b3.yaml [new file with mode: 0644]
docs/release/release-notes/notes/collectd-5-v-6-testing-cc821b32bad2794c.yaml [new file with mode: 0644]
docs/release/release-notes/notes/collectd-6-testing-flask-app-2bb0ca1326775dd8.yaml [new file with mode: 0644]
docs/release/userguide/installguide.oneclick.rst
docs/testing/index.rst

diff --git a/docker/ansible/collectd6_test.yml b/docker/ansible/collectd6_test.yml
new file mode 100644 (file)
index 0000000..c1a3a8b
--- /dev/null
@@ -0,0 +1,143 @@
+---
+# ansible-playbook -e PR=<PRID> -e new_plugin=<plugin> collectd6_test.yml
+
+# As well as passing a PRID, a config command should be passable too since
+# a lot of the plugins have been explicitly disabled in the build.
+- hosts: localhost
+  become: true
+  tasks:
+    - name: Set names for containers to be used for testing
+      set_fact:
+        collectd5_container_name: "bar-collectd-latest"
+        collectd6_container_name: "bar-collectd-6{{ '-' + PR if PR is defined }}"
+        flask_container_name: "test-collectd-5-v-6"
+
+    - name: Remove existing containers
+      docker_container:
+        name: "{{ item }}"
+        state: absent
+        force_kill: yes
+      with_items:
+        - "{{ collectd5_container_name }}"
+        - "{{ collectd6_container_name }}"
+        - "{{ flask_container_name }}"
+
+    - name: Get a list of containers
+      command:
+        docker ps -a
+      register: output
+
+    - name: Confirm that existing test containers were removed
+      assert:
+        that:
+          - 'not "{{ collectd5_container_name }}" in output.stdout'
+          - 'not "{{ collectd6_container_name }}" in output.stdout'
+          - 'not "{{ flask_container_name }}" in output.stdout'
+
+    - name: Build collectd containers
+      include_role:
+        name: build_collectd
+      args:
+        apply:
+          tags:
+            - latest
+            - collectd-6
+            - flask_test
+      vars:
+        COLLECTD_PULL_REQUESTS: "{{ PR | default() }}"
+        COLLECTD_CONFIG_CMD_ARGS: "{{ '--enable-' + new_plugin if new_plugin is defined }}"
+
+    - name: "Set up config for write_http plugin"
+      set_fact:
+        collectd_plugins: "{{ collectd_plugins | default([]) | union(['write_http']) }}"
+        collectd_plugin_write_http_nodes:
+          flask:
+            url: http://localhost:5000
+            format: "Command"
+
+    - name: Generate collectd configs
+      include_role:
+        name: config_files
+
+    # Since I can't skip-tags here, I have to remove the plugins later
+    # TODO(efoley) Add a disable_plugins and enable_plugins list to
+    # roles/config_files, as an alternative to tags.
+    # This alternative is kinda needed anyway, so that it's easier to add extra
+    # plugins instead of using.
+    # ``{{ collectd_plugins | default([]) | union(['the_plugin_i_want_to_enable'])}}``
+    # Tags can stay, since they are convenient, and easier to pass to the
+    # command line than a list of plugins
+    - name: "Remove plugin configs"
+      file:
+        path: "/opt/collectd/etc/collectd.conf.d/{{ item }}.conf"
+        state: absent
+      with_items:
+        - snmp_agent
+        - intel_pmu
+
+    # TODO(efoley): The path here should be parameterised, to a degree, I don't
+    # want it to be repeated. And I shouldn't assume that this is always going
+    # to be the value (unless it is in vars/main instead of defaults/main)
+    - name: "Remove plugin configs (collectd 6)"
+      file:
+        path: "/opt/collectd/etc/collectd.conf.d/{{ item }}.conf"
+        state: absent
+      with_items:
+        - csv
+        - network
+        - rrdtool
+        - write_kafka
+        - write_prometheus
+        - logfile
+
+    - debug:
+        var: PR
+
+    - name: Run the collectd-6 container
+      include_role:
+        name: run_collectd
+      vars:
+        collectd_image_name: "opnfv/barometer-collectd-6{{ '-' + PR if PR is defined }}"
+        collectd_container_name: "{{ collectd6_container_name }}"
+
+    - name: Run the collectd-latest container
+      include_role:
+        name: run_collectd
+      vars:
+        collectd_image_name: opnfv/barometer-collectd-latest
+        collectd_container_name: "{{ collectd5_container_name }}"
+
+    - name: Run the flask test container
+      docker_container:
+        name: "{{ flask_container_name }}"
+        image: test-collectd-write_http
+        detach: yes
+        state: started
+          #network_mode: host
+        published_ports: 5000:5000
+
+    - name: Check output for flask app
+      become: true
+      shell: |
+        docker logs {{ flask_container_name }} {{ '| grep "' + new_plugin + '"' if new_plugin is defined  }} | tail -200
+      register: output
+
+    - debug:
+        var: output.stdout_lines
+
+    - name: Get a list of running containers
+      become: true
+      command:
+        docker ps
+      register: output
+
+    - name: Make sure that the expected containers are running
+      assert:
+        that:
+          - '"{{ collectd6_container_name }}" in output.stdout'
+          - '"{{ collectd5_container_name }}" in output.stdout'
+          - '"{{ flask_container_name }}" in output.stdout'
+
+# Create a small report at the end for collectd versions...
+# Update Apply PRs to check out a branch when it is a single PR
+# OR update these playbooks to use the tag way of checking out a PR
diff --git a/docker/ansible/collectd_build.yml b/docker/ansible/collectd_build.yml
new file mode 100644 (file)
index 0000000..d5cad07
--- /dev/null
@@ -0,0 +1,22 @@
+# Copyright 2021 Anuket and others
+#
+# 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.
+---
+# ansible-playbook collectd_build.yml
+#
+- hosts: localhost
+  become: true
+  become_user: root
+  gather_facts: true
+  roles:
+    - name: build_collectd
diff --git a/docker/ansible/roles/build_collectd/tasks/main.yml b/docker/ansible/roles/build_collectd/tasks/main.yml
new file mode 100644 (file)
index 0000000..2ab9229
--- /dev/null
@@ -0,0 +1,71 @@
+# Copyright 2021 Anuket and others
+#
+# 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.
+---
+- name: Build stable container
+  docker_image:
+    name: opnfv/barometer-collectd
+    build:
+      path: "{{ playbook_dir }}/../barometer-collectd/"
+    source: build
+  tags:
+    - stable
+
+- name: Build the latest container
+  docker_image:
+    name: opnfv/barometer-collectd-latest
+    build:
+      path: "{{ playbook_dir }}/../../"
+      dockerfile: "docker/barometer-collectd-latest/Dockerfile"
+    source: build
+  tags:
+    - latest
+
+- name: Build collectd-experimental
+  docker_image:
+    name: opnfv/barometer-collectd-experimental
+    build:
+      path: "{{ playbook_dir }}/../../"
+      dockerfile: "docker/barometer-collectd-experimental/Dockerfile"
+      args:
+        COLLECTD_FLAVOR: experimental
+        COLLECTD_TAG: "{{ COLLECTD_TAG | default('main') }}"
+        COLLECTD_PULL_REQUESTS: "{{ COLLECTD_PULL_REQUESTS | default() }}"
+    source: build
+  tags:
+    - experimental
+
+- name: Build collectd-6
+  docker_image:
+    name: "opnfv/barometer-collectd-6{{ ( '-' + COLLECTD_PULL_REQUESTS ) if COLLECTD_PULL_REQUESTS is defined else '' }}"
+    build:
+      path: "{{ playbook_dir }}/../../"
+      dockerfile: "docker/barometer-collectd-experimental/Dockerfile"
+      args:
+        COLLECTD_FLAVOR: collectd-6
+        COLLECTD_TAG: "{{ COLLECTD_TAG | default('collectd-6.0') }}"
+        COLLECTD_CONFIG_CMD_ARGS: "{{ COLLECTD_CONFIG_CMD_ARGS if COLLECTD_CONFIG_CMD_ARGS is defined }}"
+    source: build
+  tags:
+    - collectd-6
+
+- name: Build test_app for write_http
+  docker_image:
+    name: test-collectd-write_http
+    build:
+      path: "{{ playbook_dir }}/../flask_app/"
+    source: build
+  tags:
+    - flask_test
+    - never
+
index 2c5d0e6..bf5aabf 100644 (file)
@@ -15,7 +15,7 @@
 
 - name: remove bar-collectd container
   docker_container:
-    name: bar-collectd
+    name: "{{ collectd_container_name }}"
     state: absent
   tags:
     - rm_containers
@@ -52,7 +52,7 @@
 
 - name: launch collectd container
   docker_container:
-    name: bar-collectd
+    name: "{{ collectd_container_name }}"
     image: "{{ collectd_image_name }}"
     volumes: "{{ volumes_list }}"
     entrypoint: "/run_collectd.sh"
index 96c9032..26007ec 100644 (file)
@@ -13,6 +13,7 @@
 # limitations under the License.
 ---
 
+collectd_container_name: "bar-collectd"
 default_flavor: "{{ flavor|default('stable')|string }}"
 flavor_image_name: "{{
          'barometer-collectd-latest' if (default_flavor == 'master' or default_flavor == 'latest') else
diff --git a/docker/flask_app/Dockerfile b/docker/flask_app/Dockerfile
new file mode 100644 (file)
index 0000000..67e6d58
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright 2021 Anuket and others. 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.
+#
+
+FROM python:3-alpine
+
+EXPOSE 5000
+
+WORKDIR /app
+COPY ./ /app
+
+RUN python -m pip  install -r requirements.txt
+
+ENTRYPOINT ["python", "flask_app.py"]
diff --git a/docker/flask_app/README b/docker/flask_app/README
new file mode 100644 (file)
index 0000000..03f8f3f
--- /dev/null
@@ -0,0 +1,45 @@
+To build this run:
+  sudo docker build -t my-flask-app .
+
+To run the app and see collectd metrics:
+
+  sudo docker run -d --net=host  my-flask-app
+  OR
+  sudo docker run -d -p 5000:5000  my-flask-app
+
+and configure collectd to use the write_http plugin:
+
+ LoadPlugin write_http
+
+ <Plugin "write_http">
+   <Node "example">
+     URL "http://127.0.0.1:5000"
+     Format Command
+     # Format JSON
+   </Node>
+ </Plugin>
+
+Format Command is used to make the output more readable for humans.
+You can also use JSON.
+
+Later the server will do something more useful.
+To view the metrics that are being sent by collectd, run::
+
+   sudo docker inspect <container_id>
+   #OR
+   sudo docker logs <container_id>
+
+Metrics from collectd-5.x will use PUTVAL
+Metrics from collectd-6.x will use PUTMETRIC
+
+Sample output::
+
+  127.0.0.1 - - [21/Apr/2021 19:31:49] "POST / HTTP/1.1" 200 -
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-cpu00/gauge-TSC interval=10.000 1619029909.268:2112.02271161789
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-cpu00/frequency-busy interval=10.000 1619029909.268:1613.51555288381
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-cpu00/percent-c1 interval=10.000 1619029909.268:86.2353665532377
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-cpu00/frequency-average interval=10.000 1619029909.268:222.094501460956
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-pkg00/temperature interval=10.000 1619029909.268:53
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-pkg00/temperature-tcc_activation interval=10.000 1619029909.268:100
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/turbostat-cpu04/frequency-average interval=10.000 1619029909.268:206.978572579757
+
diff --git a/docker/flask_app/flask_app.py b/docker/flask_app/flask_app.py
new file mode 100644 (file)
index 0000000..771a91b
--- /dev/null
@@ -0,0 +1,16 @@
+from flask import Flask, request
+import json
+
+app = Flask(__name__)
+
+@app.route('/', methods=['GET', 'POST'])
+def get_data():
+    #print(request.data)
+    #print(type(request.data))
+    print(request.data.decode('utf-8'))
+    #print(json.loads(request.data.decode("utf-8")))
+
+    return 'This is working!'
+
+if __name__=='__main__':
+    app.run(debug=True, host='0.0.0.0')
diff --git a/docker/flask_app/requirements.txt b/docker/flask_app/requirements.txt
new file mode 100644 (file)
index 0000000..e3e9a71
--- /dev/null
@@ -0,0 +1 @@
+Flask
diff --git a/docs/release/release-notes/notes/ansible-build-containers-b4a4cc9cb70f83b3.yaml b/docs/release/release-notes/notes/ansible-build-containers-b4a4cc9cb70f83b3.yaml
new file mode 100644 (file)
index 0000000..aae4b99
--- /dev/null
@@ -0,0 +1,11 @@
+release_summary: >
+    Add ansible playbook for building the containers locally.
+ansible:
+  - | 
+    Added a playbook and role for building the collectd containers locally.
+    This automates the actions described in the docker install guide. The
+    ``barometer-collectd``, ``barometer-collectd-latest`` and the
+    ``barometer-collectd-experimental`` containers are now easier to build
+    locally. The ``barometer-collectd-6`` and
+    ``barometer-collectd-experimental`` containers can also be built with
+    arbirtary PRs applied, to aid in testing locally. 
diff --git a/docs/release/release-notes/notes/collectd-5-v-6-testing-cc821b32bad2794c.yaml b/docs/release/release-notes/notes/collectd-5-v-6-testing-cc821b32bad2794c.yaml
new file mode 100644 (file)
index 0000000..2001314
--- /dev/null
@@ -0,0 +1,10 @@
+release_summary: >
+  Testing playbooks were added to compare collectd5 vs collectd6, for the
+  purpose of helping to review new PRs by comparing the generated metrics
+  between versions.
+testing:
+  - |
+    Added a playbook to compare collectd 5 and collectd 6. The playbook uses
+    existing ansible roles to build both collectd 5 and collectd 6 container
+    images, creates a common configuration, then runs the containers and shows
+    the outputs to let the user inspect the metrics and whether they match.
diff --git a/docs/release/release-notes/notes/collectd-6-testing-flask-app-2bb0ca1326775dd8.yaml b/docs/release/release-notes/notes/collectd-6-testing-flask-app-2bb0ca1326775dd8.yaml
new file mode 100644 (file)
index 0000000..9c60587
--- /dev/null
@@ -0,0 +1,3 @@
+containers:
+  - |
+    Add a flask app for testing collectd using metrics sent via write_http plugin.
index ef3624c..78203a1 100644 (file)
@@ -231,6 +231,36 @@ Verify that key is added and password is not required to connect.
    example. For multinode installation keys need to be copied for each node:
    [collectd_hostname], [influxdb_hostname] etc.
 
+Build the Collectd containers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This is an optional step, if you do not wish to build the containers locally, please continue to `Download and run Collectd+Influxdb+Grafana containers`_.
+This step will build the container images locally, allowing for testing of new changes to collectd.
+This is particularly useful for the ``experimental`` flavour for testing PRs, and for building a ``collectd-6`` container.
+
+To run the playbook and build the containers, run::
+    sudo ansible-playbook docker/ansible/collectd_build.yml
+
+By default, all contaienrs will be built.
+Since this can take a while, it is recommended that you choose a flavor to build using tags::
+
+    sudo ansible-playbook docker/ansible/collectd_build.yml --tags='collectd-6,latest'
+
+The available tags are:
+
+* *stable* builds the ``barometer-collectd`` image
+* *latest* builds the ``barometer-collectd-latest`` image
+* *experimental* builds the ``barometer-collectd-experimental`` container, with optional PRs
+* *collectd-6* builds the ``baromter-collectd-6`` container, with optional PR(s)
+
+* *flask_test* builds a small webapp that displays the metrics sent via the write_http plugin
+
+.. note::
+   The flask_test tag must be explicitly enabled.
+   This can be done either through the ``--tags='flask_test'`` (to build just
+   this container) or with ``--tags=all`` to build this and all the other
+   containers as well.
+
 Download and run Collectd+Influxdb+Grafana containers
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index 392b39f..f763ca6 100644 (file)
@@ -1 +1,79 @@
-.. To be decided
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. (c) Anuket and others
+
+==============================
+Anuket Barometer testing guide
+==============================
+
+This document will describe how to use different tests in this repo.
+
+There are a number of tools and scripts in Barometer that can be used for testing, whether that is during development, building, code reviews, or run regularly in CI.
+Some of the tests are automated, and cover building collectd, others cover particular plugins.
+
+.. TODO: This guide should also include how to manually verify that collectd plugins are working as expected.
+
+.. TODO: There might be some troubleshooting guide in here too.
+
+Porting collectd to version 6
+=============================
+
+Thre is an ansible playbook for building and running collectd 5 and 6 together to compare the collected metrics.
+This is intended to help test porting from collectd 5 to 6, and confirm equivalency across the versions.
+
+The playbook will::
+
+  * build collectd-6, collectd-latest and flask app containers
+  * generate a set of collectd configs
+  * launch the collectd-6, collectd-latest with the generated configs
+  * run the flask app which has a http server that receives metrics from
+    collectd v5 and collectd v6
+  * display the received metrics from both versions of collectd
+    Collectd v5 shows PUTVAL
+    Collectd v6 shows PUTMETRIC
+
+To run this comparison, use the following command::
+
+  $ cd docker/ansible/
+  $ sudo ansible-playbook -i default.inv collectd6_test.yml
+
+The playbook takes the following parameters:
+
+  * PR (optional)
+    The PRID for an upstream collectd pull request that will be
+    passed to the collectd 6 container build
+
+  * plugin (optional)
+    The name of the plugin that is bneing ported
+    This will filter the received metrics to show the value passed.
+
+To run the playbook with these configs, pass the extra var to ansible::
+
+  sudo ansible-playbook -i default.inv -e PR=<PR_ID> -e plugin=<plugin_name> collectd6_test.yml
+
+The metrics can then be viewed by inspecting the container logs or attaching to the container to view the output::
+
+  $ docker attach <webserver-container>
+  $ #OR
+  $ docker logs <webserver-container>
+
+Metrics from collectd 5 will appear preceeded with ``PUTVAL``, and metrics from collectd 6 will appear preceeded by ``PUTMETRIC``.
+
+::
+
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-mm-2048Kb/vmpage_number-free interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-mm-2048Kb/vmpage_number-used interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-mm-1048576Kb/vmpage_number-free interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-mm-1048576Kb/vmpage_number-used interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-node0-2048Kb/vmpage_number-free interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-node0-2048Kb/vmpage_number-used interval=10.000 1629466502.664:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-node0-1048576Kb/vmpage_number-used interval=10.000 1629466502.665:0
+  PUTVAL fbae30cc-2f20-11b2-a85c-819293100691/hugepages-node0-1048576Kb/vmpage_number-free interval=10.000 1629466502.665:0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.807 interval=10.000 label:hugepages="mm-2048Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="free" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.807 interval=10.000 label:hugepages="mm-2048Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="used" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.808 interval=10.000 label:hugepages="mm-1048576Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="free" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.808 interval=10.000 label:hugepages="node0-2048Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="free" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.808 interval=10.000 label:hugepages="node0-2048Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="used" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.809 interval=10.000 label:hugepages="node0-1048576Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="free" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.809 interval=10.000 label:hugepages="node0-1048576Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="used" 0
+  PUTMETRIC collectd_hugepages_vmpage_number type=GAUGE time=1629466501.808 interval=10.000 label:hugepages="mm-1048576Kb" label:instance="fbae30cc-2f20-11b2-a85c-819293100691" label:type="used" 0