diff options
author | Cédric Ollivier <cedric.ollivier@orange.com> | 2019-05-07 11:02:43 +0200 |
---|---|---|
committer | Cédric Ollivier <cedric.ollivier@orange.com> | 2019-05-13 20:10:21 +0200 |
commit | 2a0702ea914a574ffa7c6d6f83a5c606e24ebd0a (patch) | |
tree | a6f07cda495c2c1631b3366d0d95adee5cf30ef7 | |
parent | 0619955a7998868c437a462c9ece3eb1ea7a5277 (diff) |
Update to Python3
Functest containers leverage on Python3 instead of python2.
https://mail.python.org/pipermail/python-dev/2018-March/152348.html
It also updates robotframework librairies to latest release and Vmtp
to master ([1] is needed)
It patches cloudify rest client to support python3.
Vmtp is currently disabled because it currently supports python2 only.
[1] https://github.com/openstack/vmtp/commit/a5d062881d91bf4f547d92c6e289bea30feb5d6e#diff-b4ef698db8ca845e5845c4618278f29a
Change-Id: I39964a212ec2d4dbf53c9ea7c63f02e0b6a05b48
Signed-off-by: Cédric Ollivier <cedric.ollivier@orange.com>
24 files changed, 338 insertions, 130 deletions
diff --git a/docker/benchmarking/Dockerfile b/docker/benchmarking/Dockerfile index 31a6c630b..57bc3eab7 100644 --- a/docker/benchmarking/Dockerfile +++ b/docker/benchmarking/Dockerfile @@ -2,23 +2,24 @@ FROM opnfv/functest-tempest ARG BRANCH=master ARG OPENSTACK_TAG=master -ARG VMTP_TAG=2.5.0 +ARG VMTP_TAG=master RUN apk --no-cache add --update libxml2 libxslt && \ apk --no-cache add --virtual .build-deps --update \ - python-dev build-base linux-headers libffi-dev \ + python3-dev build-base linux-headers libffi-dev \ openssl-dev libjpeg-turbo-dev libxml2-dev libxslt-dev && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ wget -q -O- https://git.opnfv.org/functest/plain/upper-constraints.txt?h=$BRANCH > upper-constraints.opnfv.txt && \ sed -i -E /#egg=functest/d upper-constraints.opnfv.txt && \ - CFLAGS="-O0" pip install --no-cache-dir -cupper-constraints.txt -cupper-constraints.opnfv.txt lxml && \ + CFLAGS="-O0" pip3 install --no-cache-dir -cupper-constraints.txt -cupper-constraints.opnfv.txt lxml && \ git init /src/vmtp && \ (cd /src/vmtp && \ git fetch --tags https://opendev.org/x/vmtp.git $VMTP_TAG && \ git checkout FETCH_HEAD) && \ update-requirements -s --source /src/openstack-requirements /src/vmtp/ && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ /src/vmtp && \ mkdir -p /home/opnfv/functest/data/rally/neutron && \ git init /src/neutron && \ @@ -28,5 +29,5 @@ RUN apk --no-cache add --update libxml2 libxslt && \ cp -r /src/neutron/rally-jobs /home/opnfv/functest/data/rally/neutron/rally-jobs && \ rm -r upper-constraints.txt upper-constraints.opnfv.txt /src/vmtp /src/neutron && \ apk del .build-deps -COPY testcases.yaml /usr/lib/python2.7/site-packages/xtesting/ci/testcases.yaml +COPY testcases.yaml /usr/lib/python3.6/site-packages/xtesting/ci/testcases.yaml CMD ["run_tests", "-t", "all"] diff --git a/docker/benchmarking/testcases.yaml b/docker/benchmarking/testcases.yaml index eeb209113..eaa276ca6 100644 --- a/docker/benchmarking/testcases.yaml +++ b/docker/benchmarking/testcases.yaml @@ -38,6 +38,7 @@ tiers: - case_name: vmtp project_name: functest + enabled: false criteria: 100 blocking: false description: >- diff --git a/docker/core/Dockerfile b/docker/core/Dockerfile index 5d414463d..009c8874b 100644 --- a/docker/core/Dockerfile +++ b/docker/core/Dockerfile @@ -5,17 +5,17 @@ ARG OPENSTACK_TAG=master ARG PIP_TAG=18.0 RUN apk --no-cache add --update \ - python libffi openssl libjpeg-turbo py-pip bash \ + python3 libffi openssl libjpeg-turbo py3-pip bash \ grep sed wget ca-certificates git openssh-client qemu-img && \ apk --no-cache add --virtual .build-deps --update \ - python-dev build-base linux-headers libffi-dev \ + python3-dev build-base linux-headers libffi-dev \ openssl-dev libjpeg-turbo-dev && \ wget -q -O- https://git.opnfv.org/functest/plain/upper-constraints.txt?h=$BRANCH > upper-constraints.opnfv.txt && \ sed -i -E /#egg=functest/d upper-constraints.opnfv.txt && \ - pip install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ -chttps://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt \ pip==$PIP_TAG && \ - pip install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ -chttps://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt \ -e git+https://opendev.org/openstack/requirements@$OPENSTACK_TAG#egg=openstack_requirements && \ git init /src/functest && \ @@ -23,12 +23,12 @@ RUN apk --no-cache add --update \ git fetch --tags https://gerrit.opnfv.org/gerrit/functest $BRANCH && \ git checkout FETCH_HEAD) && \ update-requirements -s --source /src/openstack-requirements /src/functest && \ - pip install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.opnfv.txt \ -chttps://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt \ /src/functest && \ rm -r upper-constraints.opnfv.txt /src/functest && \ - cp /usr/lib/python2.7/site-packages/functest/ci/logging.ini /usr/lib/python2.7/site-packages/xtesting/ci/ && \ - cp /usr/lib/python2.7/site-packages/functest/ci/logging.debug.ini /usr/lib/python2.7/site-packages/xtesting/ci/ && \ + cp /usr/lib/python3.6/site-packages/functest/ci/logging.ini /usr/lib/python3.6/site-packages/xtesting/ci/ && \ + cp /usr/lib/python3.6/site-packages/functest/ci/logging.debug.ini /usr/lib/python3.6/site-packages/xtesting/ci/ && \ bash -c "mkdir -p /var/lib/xtesting /home/opnfv" && \ ln -s /var/lib/xtesting /home/opnfv/functest && \ bash -c "mkdir -p /home/opnfv/functest{/conf,/data,/images,/results} /home/opnfv/repos/vnfs" && \ diff --git a/docker/features/Dockerfile b/docker/features/Dockerfile index 3561865c3..c321032dd 100644 --- a/docker/features/Dockerfile +++ b/docker/features/Dockerfile @@ -4,21 +4,18 @@ ARG BRANCH=master ARG OPENSTACK_TAG=master COPY thirdparty-requirements.txt thirdparty-requirements.txt -RUN apk --no-cache add --update python3 sshpass && \ +RUN apk --no-cache add --update sshpass && \ apk --no-cache add --virtual .build-deps --update \ - python-dev python3-dev build-base linux-headers libffi-dev \ + python3-dev build-base linux-headers libffi-dev \ openssl-dev libjpeg-turbo-dev file && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ wget -q -O- https://git.opnfv.org/functest/plain/upper-constraints.txt?h=$BRANCH > upper-constraints.opnfv.txt && \ sed -i -E /#egg=functest/d upper-constraints.opnfv.txt && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt \ - -cupper-constraints.opnfv.txt \ - -rthirdparty-requirements.txt && \ - python3 -m pip install --no-cache-dir --src /src -cupper-constraints.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt \ -cupper-constraints.opnfv.txt \ -rthirdparty-requirements.txt && \ rm -r upper-constraints.txt upper-constraints.opnfv.txt thirdparty-requirements.txt && \ apk del .build-deps -COPY testcases.yaml /usr/lib/python2.7/site-packages/xtesting/ci/testcases.yaml +COPY testcases.yaml /usr/lib/python3.6/site-packages/xtesting/ci/testcases.yaml CMD ["run_tests", "-t", "all"] diff --git a/docker/features/thirdparty-requirements.txt b/docker/features/thirdparty-requirements.txt index ec5f4802f..dd01480fd 100644 --- a/docker/features/thirdparty-requirements.txt +++ b/docker/features/thirdparty-requirements.txt @@ -1,7 +1,7 @@ -robotframework-httplibrary;python_version<'3.0' -robotframework-requests;python_version<'3.0' -robotframework-sshlibrary;python_version<'3.0' -# baro_tests;python_version<'3.0' -sfc;python_version<'3.0' -# stor4nfv_tests;python_version<'3.0' -# doctor-tests;python_version>='3.0' +robotframework-httplibrary +robotframework-requests +robotframework-sshlibrary +# baro_tests +# sfc +# stor4nfv_tests +# doctor-tests diff --git a/docker/healthcheck/Dockerfile b/docker/healthcheck/Dockerfile index 9326e6b2d..d65adb19d 100644 --- a/docker/healthcheck/Dockerfile +++ b/docker/healthcheck/Dockerfile @@ -6,12 +6,12 @@ ARG ODL_TAG=85448c9d97b89989488e675b29b38ac42d8674e4 COPY thirdparty-requirements.txt thirdparty-requirements.txt RUN apk --no-cache add --virtual .build-deps --update \ - python-dev build-base linux-headers libffi-dev openssl-dev && \ + python3-dev build-base linux-headers libffi-dev openssl-dev && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ wget -q -O- https://git.opnfv.org/functest/plain/upper-constraints.txt?h=$BRANCH > upper-constraints.opnfv.txt && \ sed -i -E /#egg=functest/d upper-constraints.opnfv.txt && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ -rthirdparty-requirements.txt && \ git init /src/odl_test && \ (cd /src/odl_test && \ @@ -20,5 +20,5 @@ RUN apk --no-cache add --virtual .build-deps --update \ rm -r /src/odl_test/.git thirdparty-requirements.txt upper-constraints.txt \ upper-constraints.opnfv.txt && \ apk del .build-deps -COPY testcases.yaml /usr/lib/python2.7/site-packages/xtesting/ci/testcases.yaml +COPY testcases.yaml /usr/lib/python3.6/site-packages/xtesting/ci/testcases.yaml CMD ["run_tests", "-t", "all"] diff --git a/docker/healthcheck/thirdparty-requirements.txt b/docker/healthcheck/thirdparty-requirements.txt index 6d7ee1226..f8e37e3cb 100644 --- a/docker/healthcheck/thirdparty-requirements.txt +++ b/docker/healthcheck/thirdparty-requirements.txt @@ -1,3 +1,3 @@ robotframework-httplibrary robotframework-requests -robotframework-sshlibrary;python_version=='2.7' +robotframework-sshlibrary diff --git a/docker/smoke/Dockerfile b/docker/smoke/Dockerfile index f372601ed..b95a7c50a 100644 --- a/docker/smoke/Dockerfile +++ b/docker/smoke/Dockerfile @@ -8,7 +8,7 @@ ARG NEUTRON_TEMPEST_TAG=master ARG BARBICAN_TAG=master RUN apk --no-cache add --virtual .build-deps --update \ - python-dev build-base linux-headers libffi-dev \ + python3-dev build-base linux-headers libffi-dev \ openssl-dev libjpeg-turbo-dev libxml2-dev libxslt-dev && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ @@ -29,14 +29,14 @@ RUN apk --no-cache add --virtual .build-deps --update \ git fetch --tags https://opendev.org/openstack/barbican-tempest-plugin.git $BARBICAN_TAG && \ git checkout FETCH_HEAD) && \ update-requirements -s --source /src/openstack-requirements /src/barbican-tempest-plugin/ && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ /src/patrole /src/barbican-tempest-plugin /src/neutron-tempest-plugin \ networking-bgpvpn networking-sfc && \ mkdir -p /home/opnfv/functest/data/refstack && \ mkdir -p /etc/neutron /etc/cinder /etc/glance /etc/keystone /etc/nova && \ wget -q -O /etc/glance/policy.json https://opendev.org/openstack/glance/raw/branch/$OPENSTACK_TAG/etc/policy.json && \ virtualenv --no-pip --no-setuptools --no-wheel oslo && . oslo/bin/activate && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ oslo.policy -e git+https://opendev.org/openstack/neutron.git@$OPENSTACK_TAG#egg=neutron && \ oslopolicy-sample-generator --format json --output-file /etc/neutron/policy.json --namespace neutron && \ deactivate && \ @@ -45,5 +45,5 @@ RUN apk --no-cache add --virtual .build-deps --update \ /src/neutron && \ apk del .build-deps COPY defcore.txt /home/opnfv/functest/data/refstack/defcore.txt -COPY testcases.yaml /usr/lib/python2.7/site-packages/xtesting/ci/testcases.yaml +COPY testcases.yaml /usr/lib/python3.6/site-packages/xtesting/ci/testcases.yaml CMD ["run_tests", "-t", "all"] diff --git a/docker/tempest/Dockerfile b/docker/tempest/Dockerfile index 75470fa1d..2a70e4b4c 100644 --- a/docker/tempest/Dockerfile +++ b/docker/tempest/Dockerfile @@ -7,7 +7,7 @@ ARG RALLY_OPENSTACK_TAG=master ARG UJSON_TAG=d25e024f481c5571d15f3c0c406a498ca0467cfd RUN apk --no-cache add --virtual .build-deps --update \ - python-dev build-base linux-headers libffi-dev \ + python3-dev build-base linux-headers libffi-dev \ openssl-dev libjpeg-turbo-dev && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ @@ -24,9 +24,9 @@ RUN apk --no-cache add --virtual .build-deps --update \ git fetch --tags https://opendev.org/openstack/rally-openstack.git $RALLY_OPENSTACK_TAG && \ git checkout FETCH_HEAD) && \ update-requirements -s --source /src/openstack-requirements /src/rally-openstack && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ tempest /src/rally-openstack && \ - pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ /src/rally && \ rm -r upper-constraints.txt upper-constraints.opnfv.txt /src/rally /src/rally-openstack && \ mkdir -p /etc/rally && \ diff --git a/docker/vnf/Dockerfile b/docker/vnf/Dockerfile index fc9294d56..8395cbfb0 100644 --- a/docker/vnf/Dockerfile +++ b/docker/vnf/Dockerfile @@ -18,15 +18,18 @@ ENV GOBIN /src/epc-requirements/go/bin ENV PATH $GOBIN:$PATH COPY clearwater-heat-singlenet-deps.patch /tmp/clearwater-heat-singlenet-deps.patch +COPY cloudify-rest-client-py3.patch /tmp/cloudify-rest-client-py3.patch RUN apk --no-cache add --update \ ruby ruby-bundler ruby-irb ruby-rdoc dnsmasq \ - procps libxslt libxml2 zlib libffi python3 go musl-dev && \ + procps libxslt libxml2 zlib libffi go musl-dev && \ apk --no-cache add --virtual .build-deps --update \ ruby-dev g++ make libxslt-dev libxml2-dev zlib-dev libffi-dev g++ make && \ wget -q -O- https://opendev.org/openstack/requirements/raw/branch/$OPENSTACK_TAG/upper-constraints.txt > upper-constraints.txt && \ sed -i -E s/^tempest==+.*$/-e\ git+https:\\/\\/opendev.org\\/openstack\\/tempest#egg=tempest/ upper-constraints.txt && \ wget -q -O- https://git.opnfv.org/functest/plain/upper-constraints.txt?h=$BRANCH > upper-constraints.opnfv.txt && \ sed -i -E /#egg=functest/d upper-constraints.opnfv.txt && \ + (cd /usr/lib/python3.6/site-packages/cloudify_rest_client && \ + patch -p2 < /tmp/cloudify-rest-client-py3.patch) && \ git clone --depth 1 -b $VIMS_TEST_TAG https://github.com/Metaswitch/clearwater-live-test /src/vims-test && \ sed -i s/unf_ext\ \(.*\)/unf_ext\ \(0.0.7.4\)/g /src/vims-test/Gemfile.lock && \ git init /src/vims-test/quaff && \ @@ -62,7 +65,7 @@ RUN apk --no-cache add --update \ (cd /src/epc-requirements/abot_charm && \ git fetch --tags https://github.com/RebacaInc/abot_charm.git $ABOT_CHARM && \ git checkout FETCH_HEAD) && \ - python3 -m pip install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ + pip3 install --no-cache-dir --src /src -cupper-constraints.txt -cupper-constraints.opnfv.txt \ juju-wait==$JUJU_WAIT_TAG && \ go get -d github.com/rogpeppe/godeps && \ (cd $GOPATH/src/github.com/rogpeppe/godeps && git checkout $GODEPS_TAG && go install -v github.com/rogpeppe/godeps) && \ @@ -74,7 +77,7 @@ RUN apk --no-cache add --update \ (cd /src/vims-test && bundle config build.nokogiri --use-system-libraries && bundle install --system && bundle update rest-client) && \ rm -r upper-constraints.txt upper-constraints.opnfv.txt /src/vims-test/.git /src/cloudify_vims/.git /src/heat_vims/.git /src/vims-test/quaff/.git \ /src/vims-test/build-infra/.git /src/opnfv-vnf-vyos-blueprint/.git \ - /tmp/clearwater-heat-singlenet-deps.patch && \ + /tmp/clearwater-heat-singlenet-deps.patch /tmp/cloudify-rest-client-py3.patch && \ apk del .build-deps -COPY testcases.yaml /usr/lib/python2.7/site-packages/xtesting/ci/testcases.yaml +COPY testcases.yaml /usr/lib/python3.6/site-packages/xtesting/ci/testcases.yaml CMD ["run_tests", "-t", "all"] diff --git a/docker/vnf/cloudify-rest-client-py3.patch b/docker/vnf/cloudify-rest-client-py3.patch new file mode 100644 index 000000000..ef47e1aa3 --- /dev/null +++ b/docker/vnf/cloudify-rest-client-py3.patch @@ -0,0 +1,234 @@ +diff --git a/cloudify_rest_client/aria/mapi/mapi.py b/cloudify_rest_client/aria/mapi/mapi.py +index 401c9de..5c4fa76 100644 +--- a/cloudify_rest_client/aria/mapi/mapi.py ++++ b/cloudify_rest_client/aria/mapi/mapi.py +@@ -88,7 +88,7 @@ class RESTMAPI(api.ModelAPI): + wrapper = wrappers.DictWrapper + kw = dict( + (key, self._wrap(value, '/'.join([attribute_path, key]))) +- for key, value in value.items() ++ for key, value in list(value.items()) + ) + elif isinstance(value, list): + wrapper = wrappers.ListWrapper +diff --git a/cloudify_rest_client/aria/mapi/wrappers.py b/cloudify_rest_client/aria/mapi/wrappers.py +index bdee6de..13af062 100644 +--- a/cloudify_rest_client/aria/mapi/wrappers.py ++++ b/cloudify_rest_client/aria/mapi/wrappers.py +@@ -71,7 +71,7 @@ class DictWrapper(dict, WrapperBase): + ) + + def itervalues(self): +- return iter(self.values()) ++ return iter(list(self.values())) + + def items(self): + return [ +@@ -81,7 +81,7 @@ class DictWrapper(dict, WrapperBase): + ] + + def iteritems(self): +- return iter(self.items()) ++ return iter(list(self.items())) + + def keys(self): + return [ +@@ -91,10 +91,10 @@ class DictWrapper(dict, WrapperBase): + ] + + def iterkeys(self): +- return iter(self.keys()) ++ return iter(list(self.keys())) + + def __iter__(self): +- return self.iterkeys() ++ return iter(self.keys()) + + + class ListWrapper(list, WrapperBase): +diff --git a/cloudify_rest_client/aria/service_templates.py b/cloudify_rest_client/aria/service_templates.py +index 05b86df..d49a91f 100644 +--- a/cloudify_rest_client/aria/service_templates.py ++++ b/cloudify_rest_client/aria/service_templates.py +@@ -14,8 +14,8 @@ + # * limitations under the License. + + import os +-import urllib +-import urlparse ++import urllib.request, urllib.parse, urllib.error ++import urllib.parse + from functools import partial + + from .. import bytes_stream_utils +@@ -41,14 +41,14 @@ class ServiceTemplateClient(BlueprintsClient): + + if application_file_name is not None: + query_params['application_file_name'] = \ +- urllib.quote(application_file_name) ++ urllib.parse.quote(application_file_name) + + uri = '/{self._uri_prefix}/{id}'.format(self=self, + id=service_template_id) + + # For a Windows path (e.g. "C:\aaa\bbb.zip") scheme is the + # drive letter and therefore the 2nd condition is present +- if urlparse.urlparse(archive_location).scheme and \ ++ if urllib.parse.urlparse(archive_location).scheme and \ + not os.path.exists(archive_location): + # archive location is URL + query_params['service_template_csar_url'] = archive_location +diff --git a/cloudify_rest_client/blueprints.py b/cloudify_rest_client/blueprints.py +index 5d19325..5f533cc 100644 +--- a/cloudify_rest_client/blueprints.py ++++ b/cloudify_rest_client/blueprints.py +@@ -16,8 +16,8 @@ + import os + import tempfile + import shutil +-import urllib +-import urlparse ++import urllib.request, urllib.parse, urllib.error ++import urllib.parse + import contextlib + + from cloudify_rest_client import utils +@@ -95,13 +95,13 @@ class BlueprintsClient(object): + query_params = {'visibility': visibility} + if application_file_name is not None: + query_params['application_file_name'] = \ +- urllib.quote(application_file_name) ++ urllib.parse.quote(application_file_name) + + uri = '/{self._uri_prefix}/{id}'.format(self=self, id=blueprint_id) + + # For a Windows path (e.g. "C:\aaa\bbb.zip") scheme is the + # drive letter and therefore the 2nd condition is present +- if urlparse.urlparse(archive_location).scheme and \ ++ if urllib.parse.urlparse(archive_location).scheme and \ + not os.path.exists(archive_location): + # archive location is URL + query_params['blueprint_archive_url'] = archive_location +diff --git a/cloudify_rest_client/client.py b/cloudify_rest_client/client.py +index 84641d2..0528e66 100644 +--- a/cloudify_rest_client/client.py ++++ b/cloudify_rest_client/client.py +@@ -144,13 +144,13 @@ class HTTPClient(object): + verify=verify, + timeout=timeout or self.default_timeout_sec) + if self.logger.isEnabledFor(logging.DEBUG): +- for hdr, hdr_content in response.request.headers.iteritems(): ++ for hdr, hdr_content in response.request.headers.items(): + self.logger.debug('request header: %s: %s' + % (hdr, hdr_content)) + self.logger.debug('reply: "%s %s" %s' + % (response.status_code, + response.reason, response.content)) +- for hdr, hdr_content in response.headers.iteritems(): ++ for hdr, hdr_content in response.headers.items(): + self.logger.debug('response header: %s: %s' + % (hdr, hdr_content)) + +@@ -209,7 +209,7 @@ class HTTPClient(object): + body = json.dumps(data) if is_dict_data else data + if self.logger.isEnabledFor(logging.DEBUG): + log_message = 'Sending request: {0} {1}'.format( +- requests_method.func_name.upper(), ++ requests_method.__name__.upper(), + request_url) + if is_dict_data: + log_message += '; body: {0}'.format(body) +@@ -299,8 +299,8 @@ class HTTPClient(object): + if not username or not password: + return None + credentials = '{0}:{1}'.format(username, password) +- encoded_credentials = urlsafe_b64encode(credentials) +- return BASIC_AUTH_PREFIX + ' ' + encoded_credentials ++ encoded_credentials = urlsafe_b64encode(credentials.encode("utf-8")) ++ return BASIC_AUTH_PREFIX + ' ' + str(encoded_credentials, "utf-8") + + def _set_header(self, key, value, log_value=True): + if not value: +diff --git a/cloudify_rest_client/deployment_updates.py b/cloudify_rest_client/deployment_updates.py +index 5010d86..d7db897 100644 +--- a/cloudify_rest_client/deployment_updates.py ++++ b/cloudify_rest_client/deployment_updates.py +@@ -14,9 +14,9 @@ + # * limitations under the License. + import os + import json +-import urllib ++import urllib.request, urllib.parse, urllib.error + import shutil +-import urlparse ++import urllib.parse + import tempfile + from mimetypes import MimeTypes + +@@ -129,11 +129,11 @@ class DeploymentUpdatesClient(object): + + if application_file_name: + params['application_file_name'] = \ +- urllib.quote(application_file_name) ++ urllib.parse.quote(application_file_name) + + # For a Windows path (e.g. "C:\aaa\bbb.zip") scheme is the + # drive letter and therefore the 2nd condition is present +- if all([urlparse.urlparse(archive_path).scheme, ++ if all([urllib.parse.urlparse(archive_path).scheme, + not os.path.exists(archive_path)]): + # archive location is URL + params['blueprint_archive_url'] = archive_path +@@ -142,7 +142,7 @@ class DeploymentUpdatesClient(object): + os.path.basename(archive_path), + open(archive_path, 'rb'), + # Guess the archive mime type +- mime_types.guess_type(urllib.pathname2url(archive_path))) ++ mime_types.guess_type(urllib.request.pathname2url(archive_path))) + + return data_form, params + +diff --git a/cloudify_rest_client/plugins.py b/cloudify_rest_client/plugins.py +index 76171b4..32e3e47 100644 +--- a/cloudify_rest_client/plugins.py ++++ b/cloudify_rest_client/plugins.py +@@ -14,7 +14,7 @@ + # * limitations under the License. + + import os +-import urlparse ++import urllib.parse + import contextlib + + from cloudify_rest_client import bytes_stream_utils +@@ -222,7 +222,7 @@ class PluginsClient(object): + assert plugin_path + query_params = {'visibility': visibility} + timeout = self.api.default_timeout_sec +- if urlparse.urlparse(plugin_path).scheme and \ ++ if urllib.parse.urlparse(plugin_path).scheme and \ + not os.path.exists(plugin_path): + query_params['plugin_archive_url'] = plugin_path + data = None +diff --git a/cloudify_rest_client/snapshots.py b/cloudify_rest_client/snapshots.py +index 851c3a3..0c40ceb 100644 +--- a/cloudify_rest_client/snapshots.py ++++ b/cloudify_rest_client/snapshots.py +@@ -14,7 +14,7 @@ + # * limitations under the License. + + import os +-import urlparse ++import urllib.parse + import contextlib + + from cloudify_rest_client import bytes_stream_utils +@@ -192,7 +192,7 @@ class SnapshotsClient(object): + uri = '/snapshots/{0}/archive'.format(snapshot_id) + query_params = {} + +- if urlparse.urlparse(snapshot_path).scheme and \ ++ if urllib.parse.urlparse(snapshot_path).scheme and \ + not os.path.exists(snapshot_path): + query_params['snapshot_archive_url'] = snapshot_path + data = None diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index f88265178..ee66426fd 100644 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -382,6 +382,7 @@ tiers: - case_name: vmtp project_name: functest + enabled: false criteria: 100 blocking: false description: >- diff --git a/functest/opnfv_tests/openstack/barbican/barbican.py b/functest/opnfv_tests/openstack/barbican/barbican.py index b9488c2b6..7b1bb24f7 100644 --- a/functest/opnfv_tests/openstack/barbican/barbican.py +++ b/functest/opnfv_tests/openstack/barbican/barbican.py @@ -36,6 +36,6 @@ class Barbican(tempest.TempestCommon): if not rconfig.has_section('image-feature-enabled'): rconfig.add_section('image-feature-enabled') rconfig.set('image-feature-enabled', 'api_v1', False) - with open(self.conf_file, 'wb') as config_file: + with open(self.conf_file, 'w') as config_file: rconfig.write(config_file) self.backup_tempest_config(self.conf_file, self.res_dir) diff --git a/functest/opnfv_tests/openstack/patrole/patrole.py b/functest/opnfv_tests/openstack/patrole/patrole.py index 45378b6cd..9bd877b60 100644 --- a/functest/opnfv_tests/openstack/patrole/patrole.py +++ b/functest/opnfv_tests/openstack/patrole/patrole.py @@ -27,7 +27,7 @@ class Patrole(tempest.TempestCommon): rconfig.add_section('rbac') rconfig.set('rbac', 'enable_rbac', True) rconfig.set('rbac', 'rbac_test_role', kwargs.get('role', 'admin')) - with open(self.conf_file, 'wb') as config_file: + with open(self.conf_file, 'w') as config_file: rconfig.write(config_file) self.backup_tempest_config(self.conf_file, self.res_dir) diff --git a/functest/opnfv_tests/openstack/rally/rally.py b/functest/opnfv_tests/openstack/rally/rally.py index 25de3cfb6..674c74252 100644 --- a/functest/opnfv_tests/openstack/rally/rally.py +++ b/functest/opnfv_tests/openstack/rally/rally.py @@ -166,7 +166,7 @@ class RallyBase(singlevm.VmReady2): stdout=subprocess.PIPE, stderr=subprocess.STDOUT) deployment_uuid = proc.stdout.readline().rstrip() - return deployment_uuid + return deployment_uuid.decode() @staticmethod def create_rally_deployment(environ=None): @@ -215,7 +215,7 @@ class RallyBase(singlevm.VmReady2): rconfig.add_section('openstack') rconfig.set( 'openstack', 'keystone_default_role', env.get("NEW_USER_ROLE")) - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) @staticmethod @@ -226,7 +226,7 @@ class RallyBase(singlevm.VmReady2): rconfig.read(rally_conf) if rconfig.has_option('openstack', 'keystone_default_role'): rconfig.remove_option('openstack', 'keystone_default_role') - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) @staticmethod @@ -240,7 +240,7 @@ class RallyBase(singlevm.VmReady2): taskid_re = re.compile('^Task +(.*): started$') for line in cmd_raw.splitlines(True): line = line.strip() - match = taskid_re.match(line) + match = taskid_re.match(line.decode()) if match: return match.group(1) return None @@ -657,8 +657,8 @@ class RallyBase(singlevm.VmReady2): "{}/{}.xml".format(self.results_dir, self.case_name), export_type="junit-xml") res = testcase.TestCase.EX_OK - except Exception as exc: # pylint: disable=broad-except - LOGGER.error('Error with run: %s', exc) + except Exception: # pylint: disable=broad-except + LOGGER.exception('Error with run:') self.result = 0 res = testcase.TestCase.EX_RUN_ERROR self.stop_time = time.time() diff --git a/functest/opnfv_tests/openstack/refstack/refstack.py b/functest/opnfv_tests/openstack/refstack/refstack.py index 814cb255b..9eb937165 100644 --- a/functest/opnfv_tests/openstack/refstack/refstack.py +++ b/functest/opnfv_tests/openstack/refstack/refstack.py @@ -55,7 +55,7 @@ class Refstack(tempest.TempestCommon): yaml_data2 = "" for line in output.splitlines(): try: - grp = re.search(r'^([^\[]*)(\[.*\])\n*$', line) + grp = re.search(r'^([^\[]*)(\[.*\])\n*$', line.decode()) yaml_data2 = "{}\n{}: {}".format( yaml_data2, grp.group(1), grp.group(2)) except Exception: # pylint: disable=broad-except diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py index 0874e6d61..30e9ce60b 100644 --- a/functest/opnfv_tests/openstack/tempest/tempest.py +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -143,7 +143,7 @@ class TempestCommon(singlevm.VmReady2): stderr=subprocess.STDOUT) for line in proc.stdout: LOGGER.info(line.rstrip()) - new_line = line.replace(' ', '').split('|') + new_line = line.decode().replace(' ', '').split('|') if 'Tests' in new_line: break if 'Testscount' in new_line: @@ -199,7 +199,7 @@ class TempestCommon(singlevm.VmReady2): stdout=subprocess.PIPE, stderr=subprocess.STDOUT) verifier_uuid = proc.stdout.readline().rstrip() - return verifier_uuid + return verifier_uuid.decode() @staticmethod def get_verifier_repo_dir(verifier_id): @@ -235,7 +235,7 @@ class TempestCommon(singlevm.VmReady2): for key, value in sub_conf.items(): rconfig.set(section, key, value) - with open(conf_file, 'wb') as config_file: + with open(conf_file, 'w') as config_file: rconfig.write(config_file) @staticmethod @@ -403,12 +403,12 @@ class TempestCommon(singlevm.VmReady2): with proc.stdout: for line in iter(proc.stdout.readline, b''): - if re.search(r"\} tempest\.", line): + if re.search(r"\} tempest\.", line.decode()): LOGGER.info(line.rstrip()) - elif re.search(r'(?=\(UUID=(.*)\))', line): + elif re.search(r'(?=\(UUID=(.*)\))', line.decode()): self.verification_id = re.search( - r'(?=\(UUID=(.*)\))', line).group(1) - f_stdout.write(line) + r'(?=\(UUID=(.*)\))', line.decode()).group(1) + f_stdout.write(line.decode()) proc.wait() f_stdout.close() @@ -473,7 +473,7 @@ class TempestCommon(singlevm.VmReady2): rconfig.add_section('openstack') rconfig.set('openstack', 'img_name_regex', '^{}$'.format( self.image.name)) - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) def update_default_role(self, rally_conf='/etc/rally/rally.conf'): @@ -486,7 +486,7 @@ class TempestCommon(singlevm.VmReady2): if not rconfig.has_section('openstack'): rconfig.add_section('openstack') rconfig.set('openstack', 'swift_operator_role', role.name) - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) def update_rally_logs(self, rally_conf='/etc/rally/rally.conf'): @@ -499,7 +499,7 @@ class TempestCommon(singlevm.VmReady2): rconfig.set('DEFAULT', 'use_stderr', False) rconfig.set('DEFAULT', 'log-file', 'rally.log') rconfig.set('DEFAULT', 'log_dir', self.res_dir) - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) @staticmethod @@ -519,7 +519,7 @@ class TempestCommon(singlevm.VmReady2): rconfig.remove_option('DEFAULT', 'log-file') if rconfig.has_option('DEFAULT', 'log_dir'): rconfig.remove_option('DEFAULT', 'log_dir') - with open(rally_conf, 'wb') as config_file: + with open(rally_conf, 'w') as config_file: rconfig.write(config_file) def update_network_section(self): @@ -530,7 +530,7 @@ class TempestCommon(singlevm.VmReady2): rconfig.add_section('network') rconfig.set('network', 'public_network_id', self.ext_net.id) rconfig.set('network', 'floating_network_name', self.ext_net.name) - with open(self.conf_file, 'wb') as config_file: + with open(self.conf_file, 'w') as config_file: rconfig.write(config_file) def update_compute_section(self): @@ -540,7 +540,7 @@ class TempestCommon(singlevm.VmReady2): if not rconfig.has_section('compute'): rconfig.add_section('compute') rconfig.set('compute', 'fixed_network_name', self.network.name) - with open(self.conf_file, 'wb') as config_file: + with open(self.conf_file, 'w') as config_file: rconfig.write(config_file) def update_scenario_section(self): @@ -567,7 +567,7 @@ class TempestCommon(singlevm.VmReady2): rconfig.set( 'scenario', 'img_properties', functest_utils.convert_dict_to_ini(extra_properties)) - with open(self.conf_file, 'wb') as config_file: + with open(self.conf_file, 'w') as config_file: rconfig.write(config_file) def configure(self, **kwargs): # pylint: disable=unused-argument diff --git a/functest/opnfv_tests/vnf/epc/juju_epc.py b/functest/opnfv_tests/vnf/epc/juju_epc.py index cbf531b6b..93961ada9 100644 --- a/functest/opnfv_tests/vnf/epc/juju_epc.py +++ b/functest/opnfv_tests/vnf/epc/juju_epc.py @@ -273,7 +273,8 @@ class JujuEpc(singlevm.VmReady2): for i in range(10): output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) self.__logger.info("%s\n%s", " ".join(cmd), output) - ret = re.search(r'(?=workload:({})\))'.format(status), output) + ret = re.search( + r'(?=workload:({})\))'.format(status), output.decode()) if ret: self.__logger.info("%s workload is %s", name, status) break diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py index c5f554cbd..67f1e0d6d 100644 --- a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py +++ b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py @@ -110,7 +110,7 @@ class SshClient(object): # pylint: disable=too-many-instance-attributes cmd) break - res_buff += res + res_buff += res.decode() self.logger.debug("Response : '%s'", res_buff) return res_buff diff --git a/functest/tests/unit/openstack/rally/test_rally.py b/functest/tests/unit/openstack/rally/test_rally.py index 5dc38a20f..1b790a0bf 100644 --- a/functest/tests/unit/openstack/rally/test_rally.py +++ b/functest/tests/unit/openstack/rally/test_rally.py @@ -101,12 +101,12 @@ class OSRallyTesting(unittest.TestCase): mock_os_makedirs.assert_called() def test_get_task_id_default(self): - cmd_raw = 'Task 1: started' + cmd_raw = b'Task 1: started' self.assertEqual(self.rally_base.get_task_id(cmd_raw), '1') def test_get_task_id_missing_id(self): - cmd_raw = '' + cmd_raw = b'' self.assertEqual(self.rally_base.get_task_id(cmd_raw), None) diff --git a/functest/tests/unit/openstack/tempest/test_tempest.py b/functest/tests/unit/openstack/tempest/test_tempest.py index 119959670..a500a37e1 100644 --- a/functest/tests/unit/openstack/tempest/test_tempest.py +++ b/functest/tests/unit/openstack/tempest/test_tempest.py @@ -271,7 +271,7 @@ class OSTempestTesting(unittest.TestCase): with mock.patch('functest.opnfv_tests.openstack.tempest.' 'tempest.subprocess.Popen') as mock_popen: mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': 'test_deploy_id'} + attrs = {'stdout.readline.return_value': b'test_deploy_id'} mock_stdout.configure_mock(**attrs) mock_popen.return_value = mock_stdout @@ -283,7 +283,7 @@ class OSTempestTesting(unittest.TestCase): with mock.patch('functest.opnfv_tests.openstack.tempest.' 'tempest.subprocess.Popen') as mock_popen: mock_stdout = mock.Mock() - attrs = {'stdout.readline.return_value': 'test_deploy_id'} + attrs = {'stdout.readline.return_value': b'test_deploy_id'} mock_stdout.configure_mock(**attrs) mock_popen.return_value = mock_stdout diff --git a/functest/tests/unit/utils/test_functest_utils.py b/functest/tests/unit/utils/test_functest_utils.py index da3b03c80..ba022e377 100644 --- a/functest/tests/unit/utils/test_functest_utils.py +++ b/functest/tests/unit/utils/test_functest_utils.py @@ -15,6 +15,7 @@ import unittest import mock import pkg_resources +import six from functest.utils import functest_utils @@ -97,23 +98,15 @@ class FunctestUtilsTesting(unittest.TestCase): as mock_subproc_open, \ mock.patch('six.moves.builtins.open', mock.mock_open()) as mopen: - - FunctestUtilsTesting.readline = 0 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - + stream = six.BytesIO() + stream.write(self.cmd_readline().encode()) mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 1} + attrs = {'stdout': stream, 'wait.return_value': 1} mock_obj2.configure_mock(**attrs) - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=True, - error_msg=self.error_msg, - verbose=True, - output_file=self.output_file) + resp = functest_utils.execute_command( + self.cmd, info=True, error_msg=self.error_msg, verbose=True, + output_file=self.output_file) self.assertEqual(resp, 1) msg_exec = ("Executing command: '%s'" % self.cmd) mock_logger_info.assert_called_once_with(msg_exec) @@ -126,23 +119,15 @@ class FunctestUtilsTesting(unittest.TestCase): as mock_subproc_open, \ mock.patch('six.moves.builtins.open', mock.mock_open()) as mopen: - - FunctestUtilsTesting.readline = 0 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - + stream = six.BytesIO() + stream.write(self.cmd_readline().encode()) mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 0} + attrs = {'stdout': stream, 'wait.return_value': 0} mock_obj2.configure_mock(**attrs) - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=True, - error_msg=self.error_msg, - verbose=True, - output_file=self.output_file) + resp = functest_utils.execute_command( + self.cmd, info=True, error_msg=self.error_msg, verbose=True, + output_file=self.output_file) self.assertEqual(resp, 0) msg_exec = ("Executing command: '%s'" % self.cmd) mock_logger_info.assert_called_once_with(msg_exec) @@ -153,23 +138,15 @@ class FunctestUtilsTesting(unittest.TestCase): # pylint: disable=unused-argument with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ as mock_subproc_open: - - FunctestUtilsTesting.readline = 2 - - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - + stream = six.BytesIO() + stream.write(self.cmd_readline().encode()) mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 0} + attrs = {'stdout': stream, 'wait.return_value': 0} mock_obj2.configure_mock(**attrs) - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=False, - error_msg="", - verbose=False, - output_file=None) + resp = functest_utils.execute_command( + self.cmd, info=False, error_msg="", verbose=False, + output_file=None) self.assertEqual(resp, 0) @mock.patch('sys.stdout') @@ -177,22 +154,15 @@ class FunctestUtilsTesting(unittest.TestCase): # pylint: disable=unused-argument with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ as mock_subproc_open: - - FunctestUtilsTesting.readline = 2 - mock_obj = mock.Mock() - attrs = {'readline.side_effect': self.cmd_readline()} - mock_obj.configure_mock(**attrs) - + stream = six.BytesIO() + stream.write(self.cmd_readline().encode()) mock_obj2 = mock.Mock() - attrs = {'stdout': mock_obj, 'wait.return_value': 1} + attrs = {'stdout': stream, 'wait.return_value': 1} mock_obj2.configure_mock(**attrs) - mock_subproc_open.return_value = mock_obj2 - - resp = functest_utils.execute_command(self.cmd, info=False, - error_msg="", - verbose=False, - output_file=None) + resp = functest_utils.execute_command( + self.cmd, info=False, error_msg="", verbose=False, + output_file=None) self.assertEqual(resp, 1) def test_get_param_from_yaml_failed(self): diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index 19d5baf92..4bc417d61 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -43,9 +43,9 @@ def execute_command(cmd, info=False, error_msg="", ofd = open(output_file, "w") for line in iter(popen.stdout.readline, b''): if output_file: - ofd.write(line) + ofd.write(line.decode()) else: - line = line.replace('\n', '') + line = line.decode().replace('\n', '') print(line) sys.stdout.flush() if output_file: diff --git a/upper-constraints.txt b/upper-constraints.txt index bb6365e64..7c9f24f20 100644 --- a/upper-constraints.txt +++ b/upper-constraints.txt @@ -10,10 +10,10 @@ git+https://gerrit.opnfv.org/gerrit/parser#egg=nfv-heattranslator&subdirectory=t git+https://gerrit.opnfv.org/gerrit/parser#egg=nfv-toscaparser&subdirectory=tosca2heat/tosca-parser -e git+https://gerrit.opnfv.org/gerrit/parser#egg=nfv-parser cloudify-rest-client===4.3.3 -robotframework===3.0.2 +robotframework===3.1.1 robotframework-httplibrary===0.4.2 -robotframework-requests===0.4.7 -robotframework-sshlibrary===2.1.3;python_version=='2.7' +robotframework-requests===0.5.0 +robotframework-sshlibrary===3.3.0 ansible===2.3.2.0 xtesting===0.62.0 git+https://git.openstack.org/openstack/networking-bgpvpn#egg=networking_bgpvpn |