Merge "Remove [placement]/os_region_name usage"
diff --git a/.gitignore b/.gitignore
index d2c127d..8553b3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
*.log
*.log.[1-9]
*.pem
+*.pyc
.localrc.auto
.localrc.password
.prereqs
diff --git a/.zuul.yaml b/.zuul.yaml
index 07d8f48..a979fa4 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -116,10 +116,6 @@
ERROR_ON_CLONE: true
# Gate jobs can't deal with nested virt. Disable it.
LIBVIRT_TYPE: qemu
- # NOTE(dims): etcd 3.x is not available in debian/ubuntu
- # etc. As a stop gap measure, devstack uses wget to download
- # from the location below for all the CI jobs.
- ETCD_DOWNLOAD_URL: http://tarballs.openstack.org/etcd/
devstack_services:
# Ignore any default set by devstack. Emit a "disable_all_services".
base: false
@@ -183,7 +179,6 @@
NOVNC_FROM_PACKAGE: true
ERROR_ON_CLONE: true
LIBVIRT_TYPE: qemu
- ETCD_DOWNLOAD_URL: http://tarballs.openstack.org/etcd/
devstack_services:
base: false
pre-run: playbooks/pre.yaml
@@ -199,9 +194,48 @@
- ^.*/locale/.*po$
- job:
- name: devstack
+ name: devstack-minimal
parent: devstack-base
description: |
+ Minimal devstack base job, intended for use by jobs that need
+ less than the normal minimum set of required-projects.
+ nodeset: openstack-single-node
+ required-projects:
+ - openstack/requirements
+ vars:
+ devstack_localrc:
+ # Multinode specific settings
+ SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+ HOST_IP: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+ PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
+ devstack_services:
+ # Shared services
+ dstat: true
+ etcd3: true
+ mysql: true
+ peakmem_tracker: true
+ rabbit: true
+ group-vars:
+ subnode:
+ devstack_services:
+ # Shared services
+ dstat: true
+ peakmem_tracker: true
+ devstack_localrc:
+ # Multinode specific settings
+ HOST_IP: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
+ SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+ PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
+ # Subnode specific settings
+ DATABASE_TYPE: mysql
+ RABBIT_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+ DATABASE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+
+
+- job:
+ name: devstack
+ parent: devstack-minimal
+ description: |
Base devstack job for integration gate.
This base job can be used for single node and multinode devstack jobs.
@@ -231,7 +265,6 @@
- openstack/keystone
- openstack/neutron
- openstack/nova
- - openstack/requirements
- openstack/swift
timeout: 7200
vars:
@@ -245,10 +278,6 @@
NOVA_VNC_ENABLED: true
VNCSERVER_LISTEN: 0.0.0.0
VNCSERVER_PROXYCLIENT_ADDRESS: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
- # Multinode specific settings
- SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- HOST_IP: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
devstack_local_conf:
post-config:
$NEUTRON_CONF:
@@ -339,16 +368,9 @@
# integrated gate, so specifying the services has not effect.
# ceilometer-*: false
devstack_localrc:
- # Multinode specific settings
- HOST_IP: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
- SERVICE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- PUBLIC_BRIDGE_MTU: "{{ external_bridge_mtu }}"
# Subnode specific settings
- DATABASE_TYPE: mysql
GLANCE_HOSTPORT: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}:9292"
Q_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- RABBIT_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- DATABASE_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
- job:
name: devstack-multinode
@@ -471,8 +493,15 @@
# being experimental any more, so we can keep this list somewhat
# pruned.
#
+ # * nova-cells-v1: maintained by nova for cells v1 (nova-cells service);
+ # nova gates on this job, it's in experimental for testing cells v1
+ # changes to devstack w/o gating on it for all devstack changes.
# * nova-next: maintained by nova for unreleased/undefaulted
# things like cellsv2 and placement-api
experimental:
jobs:
+ - nova-cells-v1:
+ irrelevant-files:
+ - ^.*\.rst$
+ - ^doc/.*$
- nova-next
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index fc0a620..7efe4d6 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -656,7 +656,7 @@
Cells
~~~~~
-`Cells <http://wiki.openstack.org/blueprint-nova-compute-cells>`__ is
+`Cells <https://wiki.openstack.org/wiki/Blueprint-nova-compute-cells>`__ is
an alternative scaling option. To setup a cells environment add the
following to your ``localrc`` section:
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index ed9b4da..efb315c 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -18,6 +18,57 @@
Your best choice is probably to choose a `distribution of OpenStack
<https://www.openstack.org/marketplace/distros/>`__.
+Can I use DevStack as a development environment?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sure, you can. That said, there are a couple of things you should note before
+doing so:
+
+- DevStack makes a lot of configuration changes to your system and should not
+ be run in your main development environment.
+
+- All the repositories that DevStack clones when deploying are considered
+ volatile by default and thus are subject to hard resets. This is necessary to
+ keep you in sync with the latest upstream, which is what you want in a CI
+ situation, but it can result in branches being overwritten and files being
+ removed.
+
+ The corollary of this is that if you are working on a specific project, using
+ the DevStack project repository (defaulted to ``/opt/stack/<project>``) as
+ the single master repository for storing all your work is not recommended.
+ This behavior can be overridden by setting the ``RECLONE`` config option to
+ ``no``. Alternatively, you can avoid running ``stack.sh`` to redeploy by
+ restarting services manually. In any case, you should generally ensure work
+ in progress is pushed to Gerrit or otherwise backed up before running
+ ``stack.sh``.
+
+- If you use DevStack within a VM, you may wish to mount a local OpenStack
+ directory, such as ``~/src/openstack``, inside the VM and configure DevStack
+ to use this as the clone location using the ``{PROJECT}_REPO`` config
+ variables. For example, assuming you're using Vagrant and sharing your home
+ directory, you should place the following in ``local.conf``:
+
+ .. code-block:: shell
+
+ NEUTRON_REPO=/home/vagrant/src/neutron
+ NOVA_REPO=/home/vagrant/src/nova
+ KEYSTONE_REPO=/home/vagrant/src/keystone
+ GLANCE_REPO=/home/vagrant/src/glance
+ SWIFT_REPO=/home/vagrant/src/swift
+ HORIZON_REPO=/home/vagrant/src/horizon
+ CINDER_REPO=/home/vagrant/src/cinder
+ HEAT_REPO=/home/vagrant/src/heat
+ TEMPEST_REPO=/home/vagrant/src/tempest
+ HEATCLIENT_REPO=/home/vagrant/src/python-heatclient
+ GLANCECLIENT_REPO=/home/vagrant/src/python-glanceclient
+ NOVACLIENT_REPO=/home/vagrant/src/python-novaclient
+ NEUTRONCLIENT_REPO=/home/vagrant/src/python-neutronclient
+ OPENSTACKCLIENT_REPO=/home/vagrant/src/python-openstackclient
+ HEAT_CFNTOOLS_REPO=/home/vagrant/src/heat-cfntools
+ HEAT_TEMPLATES_REPO=/home/vagrant/src/heat-templates
+ NEUTRON_FWAAS_REPO=/home/vagrant/src/neutron-fwaas
+ # ...
+
Why a shell script, why not chef/puppet/...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/source/guides.rst b/doc/source/guides.rst
index c2c7b91..82e0dd6 100644
--- a/doc/source/guides.rst
+++ b/doc/source/guides.rst
@@ -20,6 +20,7 @@
guides/devstack-with-nested-kvm
guides/nova
guides/devstack-with-lbaas-v2
+ guides/devstack-with-ldap
All-In-One Single VM
--------------------
@@ -66,3 +67,8 @@
--------------------------------
Guide to working with nova features :doc:`Nova and devstack <guides/nova>`.
+
+Deploying DevStack with LDAP
+----------------------------
+
+Guide to setting up :doc:`DevStack with LDAP <guides/devstack-with-ldap>`.
diff --git a/doc/source/guides/devstack-with-lbaas-v2.rst b/doc/source/guides/devstack-with-lbaas-v2.rst
index 3592844..df3c7ce 100644
--- a/doc/source/guides/devstack-with-lbaas-v2.rst
+++ b/doc/source/guides/devstack-with-lbaas-v2.rst
@@ -2,7 +2,7 @@
=================================
Starting in the OpenStack Liberty release, the
-`neutron LBaaS v2 API <http://developer.openstack.org/api-ref-networking-v2-ext.html>`_
+`neutron LBaaS v2 API <https://developer.openstack.org/api-ref/network/v2/index.html>`_
is now stable while the LBaaS v1 API has been deprecated. The LBaaS v2 reference
driver is based on Octavia.
@@ -15,7 +15,7 @@
Install devstack
- ::
+::
git clone https://git.openstack.org/openstack-dev/devstack
cd devstack
@@ -23,7 +23,7 @@
Edit your ``local.conf`` to look like
- ::
+::
[[local|localrc]]
# Load the external LBaaS plugin.
@@ -60,7 +60,7 @@
Run stack.sh and do some sanity checks
- ::
+::
./stack.sh
. ./openrc
@@ -69,7 +69,7 @@
Create two nova instances that we can use as test http servers:
- ::
+::
#create nova instances on private network
nova boot --image $(nova image-list | awk '/ cirros-.*-x86_64-uec / {print $2}') --flavor 1 --nic net-id=$(openstack network list | awk '/ private / {print $2}') node1
@@ -83,7 +83,7 @@
Set up a simple web server on each of these instances. ssh into each instance (username 'cirros', password 'cubswin:)') and run
- ::
+::
MYIP=$(ifconfig eth0|grep 'inet addr'|awk -F: '{print $2}'| awk '{print $1}')
while true; do echo -e "HTTP/1.0 200 OK\r\n\r\nWelcome to $MYIP" | sudo nc -l -p 80 ; done&
@@ -91,7 +91,7 @@
Phase 2: Create your load balancers
------------------------------------
- ::
+::
neutron lbaas-loadbalancer-create --name lb1 private-subnet
neutron lbaas-loadbalancer-show lb1 # Wait for the provisioning_status to be ACTIVE.
diff --git a/doc/source/guides/devstack-with-ldap.rst b/doc/source/guides/devstack-with-ldap.rst
new file mode 100644
index 0000000..ec41141
--- /dev/null
+++ b/doc/source/guides/devstack-with-ldap.rst
@@ -0,0 +1,174 @@
+============================
+Deploying DevStack with LDAP
+============================
+
+The OpenStack Identity service has the ability to integrate with LDAP. The goal
+of this guide is to walk you through setting up an LDAP-backed OpenStack
+development environment.
+
+Introduction
+============
+
+LDAP support in keystone is read-only. You can use it to back an entire
+OpenStack deployment to a single LDAP server, or you can use it to back
+separate LDAP servers to specific keystone domains. Users within those domains
+will can authenticate against keystone, assume role assignments, and interact
+with other OpenStack services.
+
+Configuration
+=============
+
+To deploy an OpenLDAP server, make sure ``ldap`` is added to the list of
+``ENABLED_SERVICES``::
+
+ enable_service ldap
+
+Devstack will require a password to set up an LDAP administrator. This
+administrative user is also the bind user specified in keystone's configuration
+files, similar to a ``keystone`` user for MySQL databases.
+
+Devstack will prompt you for a password when running ``stack.sh`` if
+``LDAP_PASSWORD`` is not set. You can add the following to your
+``local.conf``::
+
+ LDAP_PASSWORD=super_secret_password
+
+At this point, devstack should have everything it needs to deploy OpenLDAP,
+bootstrap it with a minimal set of users, and configure it to back to a domain
+in keystone::
+
+ ./stack.sh
+
+Once ``stack.sh`` completes, you should have a running keystone deployment with
+a basic set of users. It is important to note that not all users will live
+within LDAP. Instead, keystone will back different domains to different
+identity sources. For example, the ``default`` domain will be backed by MySQL.
+This is usually where you'll find your administrative and services users. If
+you query keystone for a list of domains, you should see a domain called
+``Users``. This domain is set up by devstack and points to OpenLDAP.
+
+User Management
+===============
+
+Initially, there will only be two users in the LDAP server. The ``Manager``
+user is used by keystone to talk to OpenLDAP. The ``demo`` user is a generic
+user that you should be able to see if you query keystone for users within the
+``Users`` domain. Both of these users were added to LDAP using basic LDAP
+utilities installed by devstack (e.g. ``ldap-utils``) and LDIFs. The LDIFs used
+to create these users can be found in ``devstack/files/ldap/``.
+
+Listing Users
+-------------
+
+To list all users in LDAP directly, you can use ``ldapsearch`` with the LDAP
+user bootstrapped by devstack::
+
+ ldapsearch -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+ -H ldap://localhost -b dc=openstack,dc=org
+
+As you can see, devstack creates an OpenStack domain called ``openstack.org``
+as a container for the ``Manager`` and ``demo`` users.
+
+Creating Users
+--------------
+
+Since keystone's LDAP integration is read-only, users must be added directly to
+LDAP. Users added directly to OpenLDAP will automatically be placed into the
+``Users`` domain.
+
+LDIFs can be used to add users via the command line. The following is an
+example LDIF that can be used to create a new LDAP user, let's call it
+``peter.ldif.in``::
+
+ dn: cn=peter,ou=Users,dc=openstack,dc=org
+ cn: peter
+ displayName: Peter Quill
+ givenName: Peter Quill
+ mail: starlord@openstack.org
+ objectClass: inetOrgPerson
+ objectClass: top
+ sn: peter
+ uid: peter
+ userPassword: im-a-better-pilot-than-rocket
+
+Now, we use the ``Manager`` user to create a user for Peter in LDAP::
+
+ ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+ -H ldap://localhost -c -f peter.ldif.in
+
+We should be able to assign Peter roles on projects. After Peter has some level
+of authorization, he should be able to login to Horizon by specifying the
+``Users`` domain and using his ``peter`` username and password. Authorization
+can be given to Peter by creating a project within the ``Users`` domain and
+giving him a role assignment on that project::
+
+ $ openstack project create --domain Users awesome-mix-vol-1
+ +-------------+----------------------------------+
+ | Field | Value |
+ +-------------+----------------------------------+
+ | description | |
+ | domain_id | 61a2de23107c46bea2d758167af707b9 |
+ | enabled | True |
+ | id | 7d422396d54945cdac8fe1e8e32baec4 |
+ | is_domain | False |
+ | name | awesome-mix-vol-1 |
+ | parent_id | 61a2de23107c46bea2d758167af707b9 |
+ | tags | [] |
+ +-------------+----------------------------------+
+ $ openstack role add --user peter --user-domain Users \
+ --project awesome-mix-vol-1 --project-domain Users admin
+
+
+Deleting Users
+--------------
+
+We can use the same basic steps to remove users from LDAP, but instead of using
+LDIFs, we can just pass the ``dn`` of the user we want to delete::
+
+ ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+ -H ldap://localhost cn=peter,ou=Users,dc=openstack,dc=org
+
+Group Management
+================
+
+Like users, groups are considered specific identities. This means that groups
+also fall under the same read-only constraints as users and they can be managed
+directly with LDAP in the same way users are with LDIFs.
+
+Adding Groups
+-------------
+
+Let's define a specific group with the following LDIF::
+
+ dn: cn=guardians,ou=UserGroups,dc=openstack,dc=org
+ objectClass: groupOfNames
+ cn: guardians
+ description: Guardians of the Galaxy
+ member: cn=peter,dc=openstack,dc=org
+ member: cn=gamora,dc=openstack,dc=org
+ member: cn=drax,dc=openstack,dc=org
+ member: cn=rocket,dc=openstack,dc=org
+ member: cn=groot,dc=openstack,dc=org
+
+We can create the group using the same ``ldapadd`` command as we did with
+users::
+
+ ldapadd -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+ -H ldap://localhost -c -f guardian-group.ldif.in
+
+If we check the group membership in Horizon, we'll see that only Peter is a
+member of the ``guardians`` group, despite the whole crew being specified in
+the LDIF. Once those accounts are created in LDAP, they will automatically be
+added to the ``guardians`` group. They will also assume any role assignments
+given to the ``guardians`` group.
+
+Deleting Groups
+---------------
+
+Just like users, groups can be deleted using the ``dn``::
+
+ ldapdelete -x -w LDAP_PASSWORD -D cn=Manager,dc=openstack,dc=org \
+ -H ldap://localhost cn=guardians,ou=UserGroups,dc=openstack,dc=org
+
+Note that this operation will not remove users within that group. It will only
+remove the group itself and the memberships any users had with that group.
diff --git a/doc/source/guides/neutron.rst b/doc/source/guides/neutron.rst
index 092809a..1b8dccd 100644
--- a/doc/source/guides/neutron.rst
+++ b/doc/source/guides/neutron.rst
@@ -396,7 +396,7 @@
In this configuration we are defining IPV4_ADDRS_SAFE_TO_USE to be a
publicly routed IPv4 subnet. In this specific instance we are using
-the special TEST-NET-3 subnet defined in `RFC 5737 <http://tools.ietf.org/html/rfc5737>`_,
+the special TEST-NET-3 subnet defined in `RFC 5737 <https://tools.ietf.org/html/rfc5737>`_,
which is used for documentation. In your DevStack setup, IPV4_ADDRS_SAFE_TO_USE
would be a public IP address range that you or your organization has
allocated to you, so that you could access your instances from the
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
index 04b7698..c21e0ef 100644
--- a/doc/source/plugin-registry.rst
+++ b/doc/source/plugin-registry.rst
@@ -35,7 +35,7 @@
ceilometer `git://git.openstack.org/openstack/ceilometer <https://git.openstack.org/cgit/openstack/ceilometer>`__
ceilometer-powervm `git://git.openstack.org/openstack/ceilometer-powervm <https://git.openstack.org/cgit/openstack/ceilometer-powervm>`__
cloudkitty `git://git.openstack.org/openstack/cloudkitty <https://git.openstack.org/cgit/openstack/cloudkitty>`__
-collectd-ceilometer-plugin `git://git.openstack.org/openstack/collectd-ceilometer-plugin <https://git.openstack.org/cgit/openstack/collectd-ceilometer-plugin>`__
+collectd-openstack-plugins `git://git.openstack.org/openstack/collectd-openstack-plugins <https://git.openstack.org/cgit/openstack/collectd-openstack-plugins>`__
congress `git://git.openstack.org/openstack/congress <https://git.openstack.org/cgit/openstack/congress>`__
cyborg `git://git.openstack.org/openstack/cyborg <https://git.openstack.org/cgit/openstack/cyborg>`__
designate `git://git.openstack.org/openstack/designate <https://git.openstack.org/cgit/openstack/designate>`__
@@ -147,13 +147,13 @@
octavia `git://git.openstack.org/openstack/octavia <https://git.openstack.org/cgit/openstack/octavia>`__
octavia-dashboard `git://git.openstack.org/openstack/octavia-dashboard <https://git.openstack.org/cgit/openstack/octavia-dashboard>`__
omni `git://git.openstack.org/openstack/omni <https://git.openstack.org/cgit/openstack/omni>`__
+openstacksdk `git://git.openstack.org/openstack/openstacksdk <https://git.openstack.org/cgit/openstack/openstacksdk>`__
os-xenapi `git://git.openstack.org/openstack/os-xenapi <https://git.openstack.org/cgit/openstack/os-xenapi>`__
osprofiler `git://git.openstack.org/openstack/osprofiler <https://git.openstack.org/cgit/openstack/osprofiler>`__
oswin-tempest-plugin `git://git.openstack.org/openstack/oswin-tempest-plugin <https://git.openstack.org/cgit/openstack/oswin-tempest-plugin>`__
panko `git://git.openstack.org/openstack/panko <https://git.openstack.org/cgit/openstack/panko>`__
patrole `git://git.openstack.org/openstack/patrole <https://git.openstack.org/cgit/openstack/patrole>`__
picasso `git://git.openstack.org/openstack/picasso <https://git.openstack.org/cgit/openstack/picasso>`__
-python-openstacksdk `git://git.openstack.org/openstack/python-openstacksdk <https://git.openstack.org/cgit/openstack/python-openstacksdk>`__
qinling `git://git.openstack.org/openstack/qinling <https://git.openstack.org/cgit/openstack/qinling>`__
rally `git://git.openstack.org/openstack/rally <https://git.openstack.org/cgit/openstack/rally>`__
rally-openstack `git://git.openstack.org/openstack/rally-openstack <https://git.openstack.org/cgit/openstack/rally-openstack>`__
diff --git a/files/rpms-suse/general b/files/rpms-suse/general
index 0b69cb1..b870d72 100644
--- a/files/rpms-suse/general
+++ b/files/rpms-suse/general
@@ -11,7 +11,6 @@
iputils
libffi-devel # pyOpenSSL
libjpeg8-devel # Pillow 3.0.0
-libmysqlclient-devel # MySQL-python
libopenssl-devel # to rebuild pyOpenSSL if needed
libxslt-devel # lxml
lsof # useful when debugging
diff --git a/files/rpms-suse/n-cpu b/files/rpms-suse/n-cpu
index 9ece115..d0c572e 100644
--- a/files/rpms-suse/n-cpu
+++ b/files/rpms-suse/n-cpu
@@ -1,7 +1,7 @@
cryptsetup
-genisoimage
libosinfo
lvm2
+mkisofs
open-iscsi
sg3_utils
# Stuff for diablo volumes
diff --git a/files/rpms-suse/nova b/files/rpms-suse/nova
index ae115d2..4103a40 100644
--- a/files/rpms-suse/nova
+++ b/files/rpms-suse/nova
@@ -4,7 +4,6 @@
dnsmasq-utils # dist:opensuse-12.3,opensuse-13.1
ebtables
gawk
-genisoimage # required for config_drive
iptables
iputils
kpartx
@@ -12,6 +11,7 @@
libvirt # NOPRIME
libvirt-python # NOPRIME
mariadb # NOPRIME
+mkisofs # required for config_drive
parted
polkit
# qemu as fallback if kvm cannot be used
diff --git a/inc/python b/inc/python
index e074ea4..96be107 100644
--- a/inc/python
+++ b/inc/python
@@ -411,12 +411,6 @@
function lib_installed_from_git {
local name=$1
local safe_name
- # TODO(mordred) This is a special case for python-openstacksdk, where the
- # repo name and the pip name do not match. We should either add systemic
- # support for providing aliases, or we should rename the git repo.
- if [[ $name == 'python-openstacksdk' ]] ; then
- name=openstacksdk
- fi
safe_name=$(python -c "from pkg_resources import safe_name; \
print(safe_name('${name}'))")
# Note "pip freeze" doesn't always work here, because it tries to
@@ -435,22 +429,6 @@
[[ -n $(pip list --format=columns 2>/dev/null | awk "/^$safe_name/ {print \$3}") ]]
}
-# check that everything that's in LIBS_FROM_GIT was actually installed
-# correctly, this helps double check issues with library fat fingering.
-function check_libs_from_git {
- local lib=""
- local not_installed=""
- for lib in $(echo ${LIBS_FROM_GIT} | tr "," " "); do
- if ! lib_installed_from_git "$lib"; then
- not_installed+=" $lib"
- fi
- done
- # if anything is not installed, say what it is.
- if [[ -n "$not_installed" ]]; then
- die $LINENO "The following LIBS_FROM_GIT were not installed correct: $not_installed"
- fi
-}
-
# setup a library by name. If we are trying to use the library from
# git, we'll do a git based install, otherwise we'll punt and the
# library should be installed by a requirements pull from another
@@ -561,6 +539,13 @@
setup_package $project_dir "$flags" $extras
+ # If this project is in LIBS_FROM_GIT, verify it was actually installed
+ # correctly. This helps catch errors caused by constraints mismatches.
+ if use_library_from_git "$project_dir"; then
+ if ! lib_installed_from_git "$project_dir"; then
+ die $LINENO "The following LIBS_FROM_GIT was not installed correctly: $project_dir"
+ fi
+ fi
}
# ``pip install -e`` the package, which processes the dependencies
diff --git a/lib/cinder b/lib/cinder
index 3a8097f..f6cad9d 100644
--- a/lib/cinder
+++ b/lib/cinder
@@ -349,7 +349,7 @@
get_or_create_endpoint \
"block-storage" \
"$REGION_NAME" \
- "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/"
+ "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST:$CINDER_SERVICE_PORT/v3/\$(project_id)s"
get_or_create_endpoint \
"volume" \
@@ -371,7 +371,7 @@
get_or_create_endpoint \
"block-storage" \
"$REGION_NAME" \
- "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/"
+ "$CINDER_SERVICE_PROTOCOL://$CINDER_SERVICE_HOST/volume/v3/\$(project_id)s"
get_or_create_endpoint \
"volume" \
diff --git a/lib/glance b/lib/glance
index 95d2450..6a0e719 100644
--- a/lib/glance
+++ b/lib/glance
@@ -111,11 +111,9 @@
# Server is configured through this function and not init_glance.
create_glance_cache_dir
- # Copy over our glance configurations and update them
- cp $GLANCE_DIR/etc/glance-registry.conf $GLANCE_REGISTRY_CONF
+ # Set non-default configuration options for registry
iniset $GLANCE_REGISTRY_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
iniset $GLANCE_REGISTRY_CONF DEFAULT bind_host $GLANCE_SERVICE_LISTEN_ADDRESS
- inicomment $GLANCE_REGISTRY_CONF DEFAULT log_file
local dburl
dburl=`database_connection_url glance`
iniset $GLANCE_REGISTRY_CONF database connection $dburl
@@ -126,8 +124,8 @@
iniset_rpc_backend glance $GLANCE_REGISTRY_CONF
iniset $GLANCE_REGISTRY_CONF DEFAULT graceful_shutdown_timeout "$SERVICE_GRACEFUL_SHUTDOWN_TIMEOUT"
+ # Set non-default configuration options for the API server
iniset $GLANCE_API_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
- inicomment $GLANCE_API_CONF DEFAULT log_file
iniset $GLANCE_API_CONF database connection $dburl
iniset $GLANCE_API_CONF DEFAULT use_syslog $SYSLOG
iniset $GLANCE_API_CONF DEFAULT image_cache_dir $GLANCE_CACHE_DIR/
@@ -185,11 +183,6 @@
iniset $GLANCE_SWIFT_STORE_CONF ref1 auth_address $KEYSTONE_SERVICE_URI/v3
fi
iniset $GLANCE_SWIFT_STORE_CONF ref1 auth_version 3
-
- # commenting is not strictly necessary but it's confusing to have bad values in conf
- inicomment $GLANCE_API_CONF glance_store swift_store_user
- inicomment $GLANCE_API_CONF glance_store swift_store_key
- inicomment $GLANCE_API_CONF glance_store swift_store_auth_address
fi
# We need to tell glance what it's public endpoint is so that the version
@@ -215,18 +208,13 @@
cp -p $GLANCE_DIR/etc/glance-registry-paste.ini $GLANCE_REGISTRY_PASTE_INI
cp -p $GLANCE_DIR/etc/glance-api-paste.ini $GLANCE_API_PASTE_INI
- cp $GLANCE_DIR/etc/glance-cache.conf $GLANCE_CACHE_CONF
+ # Set non-default configuration options for the glance-cache
iniset $GLANCE_CACHE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
- inicomment $GLANCE_CACHE_CONF DEFAULT log_file
iniset $GLANCE_CACHE_CONF DEFAULT use_syslog $SYSLOG
iniset $GLANCE_CACHE_CONF DEFAULT image_cache_dir $GLANCE_CACHE_DIR/
- iniuncomment $GLANCE_CACHE_CONF DEFAULT auth_url
iniset $GLANCE_CACHE_CONF DEFAULT auth_url $KEYSTONE_AUTH_URI
- iniuncomment $GLANCE_CACHE_CONF DEFAULT auth_tenant_name
iniset $GLANCE_CACHE_CONF DEFAULT admin_tenant_name $SERVICE_PROJECT_NAME
- iniuncomment $GLANCE_CACHE_CONF DEFAULT auth_user
iniset $GLANCE_CACHE_CONF DEFAULT admin_user glance
- iniuncomment $GLANCE_CACHE_CONF DEFAULT auth_password
iniset $GLANCE_CACHE_CONF DEFAULT admin_password $SERVICE_PASSWORD
iniset $GLANCE_CACHE_CONF DEFAULT registry_host $GLANCE_SERVICE_HOST
diff --git a/lib/keystone b/lib/keystone
index 714f089..696e351 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -202,7 +202,7 @@
sudo install -d -o $STACK_USER $KEYSTONE_CONF_DIR
if [[ "$KEYSTONE_CONF_DIR" != "$KEYSTONE_DIR/etc" ]]; then
- install -m 600 $KEYSTONE_DIR/etc/keystone.conf.sample $KEYSTONE_CONF
+ install -m 600 /dev/null $KEYSTONE_CONF
if [[ -f "$KEYSTONE_DIR/etc/keystone-paste.ini" ]]; then
cp -p "$KEYSTONE_DIR/etc/keystone-paste.ini" "$KEYSTONE_PASTE_INI"
fi
@@ -220,7 +220,7 @@
inidelete $KEYSTONE_PASTE_INI composite:admin \\/v2.0
fi
- # Rewrite stock ``keystone.conf``
+ # Populate ``keystone.conf``
if is_service_enabled ldap; then
iniset $KEYSTONE_CONF identity domain_config_dir "$KEYSTONE_CONF_DIR/domains"
iniset $KEYSTONE_CONF identity domain_specific_drivers_enabled "True"
diff --git a/lib/libraries b/lib/libraries
index 6d52f64..b4f3c31 100644
--- a/lib/libraries
+++ b/lib/libraries
@@ -28,6 +28,7 @@
GITDIR["cursive"]=$DEST/cursive
GITDIR["debtcollector"]=$DEST/debtcollector
GITDIR["futurist"]=$DEST/futurist
+GITDIR["openstacksdk"]=$DEST/openstacksdk
GITDIR["os-client-config"]=$DEST/os-client-config
GITDIR["osc-lib"]=$DEST/osc-lib
GITDIR["osc-placement"]=$DEST/osc-placement
@@ -51,7 +52,6 @@
GITDIR["oslo.vmware"]=$DEST/oslo.vmware
GITDIR["osprofiler"]=$DEST/osprofiler
GITDIR["pycadf"]=$DEST/pycadf
-GITDIR["python-openstacksdk"]=$DEST/python-openstacksdk
GITDIR["stevedore"]=$DEST/stevedore
GITDIR["taskflow"]=$DEST/taskflow
GITDIR["tooz"]=$DEST/tooz
@@ -91,6 +91,7 @@
_install_lib_from_source "cursive"
_install_lib_from_source "debtcollector"
_install_lib_from_source "futurist"
+ _install_lib_from_source "openstacksdk"
_install_lib_from_source "osc-lib"
_install_lib_from_source "osc-placement"
_install_lib_from_source "os-client-config"
@@ -114,7 +115,6 @@
_install_lib_from_source "oslo.vmware"
_install_lib_from_source "osprofiler"
_install_lib_from_source "pycadf"
- _install_lib_from_source "python-openstacksdk"
_install_lib_from_source "stevedore"
_install_lib_from_source "taskflow"
_install_lib_from_source "tooz"
diff --git a/lib/neutron_plugins/ovs_base b/lib/neutron_plugins/ovs_base
index 50b9ae5..36e2ed2 100644
--- a/lib/neutron_plugins/ovs_base
+++ b/lib/neutron_plugins/ovs_base
@@ -72,7 +72,14 @@
if [[ $DISTRO == "sle12" ]] && [[ $os_RELEASE -lt 12.2 ]]; then
restart_service openvswitch-switch
else
- restart_service openvswitch
+ # workaround for https://bugzilla.suse.com/show_bug.cgi?id=1085971
+ if [[ $DISTRO =~ "tumbleweed" ]]; then
+ sudo sed -i -e "s,^OVS_USER_ID=.*,OVS_USER_ID='root:root'," /etc/sysconfig/openvswitch
+ fi
+ restart_service openvswitch || {
+ journalctl -xe || :
+ systemctl status openvswitch
+ }
fi
fi
}
diff --git a/lib/nova b/lib/nova
index 56e3093..939806f 100644
--- a/lib/nova
+++ b/lib/nova
@@ -506,6 +506,12 @@
if [ "$FORCE_CONFIG_DRIVE" != "False" ]; then
iniset $NOVA_CONF DEFAULT force_config_drive "$FORCE_CONFIG_DRIVE"
fi
+
+ # nova defaults to genisoimage but only mkisofs is available for 15.0+
+ if is_suse; then
+ iniset $NOVA_CONF DEFAULT mkisofs_cmd /usr/bin/mkisofs
+ fi
+
# Format logging
setup_logging $NOVA_CONF
@@ -685,7 +691,7 @@
$NOVA_BIN_DIR/nova-manage cell create --name=child --cell_type=child --username=$RABBIT_USERID --hostname=$RABBIT_HOST --port=5672 --password=$RABBIT_PASSWORD --virtual_host=child_cell --woffset=0 --wscale=1
# Creates the single cells v2 cell for the child cell (v1) nova db.
- nova-manage --config-file $NOVA_CELLS_CONF cell_v2 create_cell \
+ $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CELLS_CONF cell_v2 create_cell \
--transport-url $(get_transport_url child_cell) --name 'cell1'
fi
}
@@ -729,7 +735,7 @@
# this needs to come after the api_db sync happens. We also want to run
# this before the db sync below since that will migrate both the nova
# and nova_cell0 databases.
- nova-manage cell_v2 map_cell0 --database_connection `database_connection_url nova_cell0`
+ $NOVA_BIN_DIR/nova-manage cell_v2 map_cell0 --database_connection `database_connection_url nova_cell0`
# (Re)create nova databases
for i in $(seq 1 $NOVA_NUM_CELLS); do
@@ -750,7 +756,7 @@
# create the cell1 cell for the main nova db where the hosts live
for i in $(seq 1 $NOVA_NUM_CELLS); do
- nova-manage --config-file $NOVA_CONF --config-file $(conductor_conf $i) cell_v2 create_cell --name "cell$i"
+ $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CONF --config-file $(conductor_conf $i) cell_v2 create_cell --name "cell$i"
done
fi
@@ -1015,7 +1021,7 @@
if is_service_enabled n-api; then
# dump the cell mapping to ensure life is good
echo "Dumping cells_v2 mapping"
- nova-manage cell_v2 list_cells --verbose
+ $NOVA_BIN_DIR/nova-manage cell_v2 list_cells --verbose
fi
}
diff --git a/lib/swift b/lib/swift
index 6cda9c8..933af10 100644
--- a/lib/swift
+++ b/lib/swift
@@ -37,6 +37,7 @@
# Set up default directories
GITDIR["python-swiftclient"]=$DEST/python-swiftclient
+SWIFT_DIR=$DEST/swift
# Swift virtual environment
if [[ ${USE_VENV} = True ]]; then
@@ -46,8 +47,6 @@
SWIFT_BIN_DIR=$(get_python_exec_prefix)
fi
-
-SWIFT_DIR=$DEST/swift
SWIFT_AUTH_CACHE_DIR=${SWIFT_AUTH_CACHE_DIR:-/var/cache/swift}
SWIFT_APACHE_WSGI_DIR=${SWIFT_APACHE_WSGI_DIR:-/var/www/swift}
SWIFT3_DIR=$DEST/swift3
@@ -341,7 +340,7 @@
local user_group
# Make sure to kill all swift processes first
- swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
sudo install -d -o ${STACK_USER} ${SWIFT_CONF_DIR}
sudo install -d -o ${STACK_USER} ${SWIFT_CONF_DIR}/{object,container,account}-server
@@ -704,7 +703,7 @@
function init_swift {
local node_number
# Make sure to kill all swift processes first
- swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
# Forcibly re-create the backing filesystem
create_swift_disk
@@ -715,9 +714,9 @@
rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz
- swift-ring-builder object.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
- swift-ring-builder container.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
- swift-ring-builder account.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+ $SWIFT_BIN_DIR/swift-ring-builder object.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+ $SWIFT_BIN_DIR/swift-ring-builder container.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
+ $SWIFT_BIN_DIR/swift-ring-builder account.builder create ${SWIFT_PARTITION_POWER_SIZE} ${SWIFT_REPLICAS} 1
# The ring will be created on each node, and because the order of
# nodes is identical we can use a seed for rebalancing, making it
@@ -728,26 +727,26 @@
node_number=1
for node in ${SWIFT_STORAGE_IPS}; do
- swift-ring-builder object.builder add z${node_number}-${node}:${OBJECT_PORT_BASE}/sdb1 1
- swift-ring-builder container.builder add z${node_number}-${node}:${CONTAINER_PORT_BASE}/sdb1 1
- swift-ring-builder account.builder add z${node_number}-${node}:${ACCOUNT_PORT_BASE}/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder object.builder add z${node_number}-${node}:${OBJECT_PORT_BASE}/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder container.builder add z${node_number}-${node}:${CONTAINER_PORT_BASE}/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder account.builder add z${node_number}-${node}:${ACCOUNT_PORT_BASE}/sdb1 1
let "node_number=node_number+1"
done
else
for node_number in ${SWIFT_REPLICAS_SEQ}; do
- swift-ring-builder object.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
- swift-ring-builder container.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
- swift-ring-builder account.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder object.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( OBJECT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder container.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( CONTAINER_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
+ $SWIFT_BIN_DIR/swift-ring-builder account.builder add z${node_number}-${SWIFT_SERVICE_LOCAL_HOST}:$(( ACCOUNT_PORT_BASE + 10 * (node_number - 1) ))/sdb1 1
done
fi
# We use a seed for rebalancing. Doing this allows us to create
# identical rings on multiple nodes if SWIFT_STORAGE_IPS is the same
- swift-ring-builder object.builder rebalance 42
- swift-ring-builder container.builder rebalance 42
- swift-ring-builder account.builder rebalance 42
+ $SWIFT_BIN_DIR/swift-ring-builder object.builder rebalance 42
+ $SWIFT_BIN_DIR/swift-ring-builder container.builder rebalance 42
+ $SWIFT_BIN_DIR/swift-ring-builder account.builder rebalance 42
} && popd >/dev/null
# Create cache dir
@@ -803,7 +802,7 @@
# Apache should serve the "PACO" a.k.a "main" services
restart_apache_server
# The rest of the services should be started in backgroud
- swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
return 0
fi
@@ -827,7 +826,7 @@
done
if [[ "$SWIFT_START_ALL_SERVICES" == "True" ]]; then
- swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest start
else
# The container-sync daemon is strictly needed to pass the container
# sync Tempest tests.
@@ -835,8 +834,8 @@
run_process s-container-sync "$SWIFT_BIN_DIR/swift-container-sync ${SWIFT_CONF_DIR}/container-server/1.conf"
fi
else
- swift-init --run-dir=${SWIFT_DATA_DIR}/run all restart || true
- swift-init --run-dir=${SWIFT_DATA_DIR}/run proxy stop || true
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all restart || true
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run proxy stop || true
fi
if is_service_enabled tls-proxy; then
@@ -863,12 +862,12 @@
local type
if [ "$SWIFT_USE_MOD_WSGI" == "True" ]; then
- swift-init --run-dir=${SWIFT_DATA_DIR}/run rest stop && return 0
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run rest stop && return 0
fi
# screen normally killed by ``unstack.sh``
- if type -p swift-init >/dev/null; then
- swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
+ if type -p $SWIFT_BIN_DIR/swift-init >/dev/null; then
+ $SWIFT_BIN_DIR/swift-init --run-dir=${SWIFT_DATA_DIR}/run all stop || true
fi
# Dump all of the servers
# Maintain the iteration as stop_process() has some desirable side-effects
diff --git a/lib/tempest b/lib/tempest
index 0605ffb..e1c1f6c 100644
--- a/lib/tempest
+++ b/lib/tempest
@@ -303,6 +303,10 @@
# as this is supported in Queens and beyond.
iniset $TEMPEST_CONFIG identity-feature-enabled project_tags True
+ # In Queens and later, application credentials are enabled by default
+ # so remove this once Tempest no longer supports Pike.
+ iniset $TEMPEST_CONFIG identity-feature-enabled application_credentials True
+
# Image
# We want to be able to override this variable in the gate to avoid
# doing an external HTTP fetch for this test.
@@ -646,7 +650,7 @@
function install_tempest_plugins {
pushd $TEMPEST_DIR
if [[ $TEMPEST_PLUGINS != 0 ]] ; then
- tox -evenv-tempest -- pip install $TEMPEST_PLUGINS
+ tox -evenv-tempest -- pip install -c $REQUIREMENTS_DIR/upper-constraints.txt $TEMPEST_PLUGINS
echo "Checking installed Tempest plugins:"
tox -evenv-tempest -- tempest list-plugins
fi
diff --git a/roles/write-devstack-local-conf/README.rst b/roles/write-devstack-local-conf/README.rst
index 73f9f0d..bfce9c9 100644
--- a/roles/write-devstack-local-conf/README.rst
+++ b/roles/write-devstack-local-conf/README.rst
@@ -20,6 +20,14 @@
bash shell variables, and will be ordered so that variables used by
later entries appear first.
+ As a special case, the variable ``LIBS_FROM_GIT`` will be
+ constructed automatically from the projects which appear in the
+ ``required-projects`` list defined by the job. To instruct
+ devstack to install a library from source rather than pypi, simply
+ add that library to the job's ``required-projects`` list. To
+ override the automatically-generated value, set ``LIBS_FROM_GIT``
+ in ``devstack_localrc`` to the desired value.
+
.. zuul:rolevar:: devstack_local_conf
:type: dict
@@ -75,3 +83,7 @@
A dictionary mapping a plugin name to a git repo location. If the
location is a non-empty string, then an ``enable_plugin`` line will
be emmitted for the plugin name.
+
+ If a plugin declares a dependency on another plugin (via
+ ``plugin_requires`` in the plugin's settings file), this role will
+ automatically emit ``enable_plugin`` lines in the correct order.
diff --git a/roles/write-devstack-local-conf/library/devstack_local_conf.py b/roles/write-devstack-local-conf/library/devstack_local_conf.py
index 55ba4af..9728fef 100644
--- a/roles/write-devstack-local-conf/library/devstack_local_conf.py
+++ b/roles/write-devstack-local-conf/library/devstack_local_conf.py
@@ -14,16 +14,69 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import os
import re
-class VarGraph(object):
+class DependencyGraph(object):
# This is based on the JobGraph from Zuul.
+ def __init__(self):
+ self._names = set()
+ self._dependencies = {} # dependent_name -> set(parent_names)
+
+ def add(self, name, dependencies):
+ # Append the dependency information
+ self._dependencies.setdefault(name, set())
+ try:
+ for dependency in dependencies:
+ # Make sure a circular dependency is never created
+ ancestors = self._getParentNamesRecursively(
+ dependency, soft=True)
+ ancestors.add(dependency)
+ if name in ancestors:
+ raise Exception("Dependency cycle detected in {}".
+ format(name))
+ self._dependencies[name].add(dependency)
+ except Exception:
+ del self._dependencies[name]
+ raise
+
+ def getDependenciesRecursively(self, parent):
+ dependencies = []
+
+ current_dependencies = self._dependencies[parent]
+ for current in current_dependencies:
+ if current not in dependencies:
+ dependencies.append(current)
+ for dep in self.getDependenciesRecursively(current):
+ if dep not in dependencies:
+ dependencies.append(dep)
+ return dependencies
+
+ def _getParentNamesRecursively(self, dependent, soft=False):
+ all_parent_items = set()
+ items_to_iterate = set([dependent])
+ while len(items_to_iterate) > 0:
+ current_item = items_to_iterate.pop()
+ current_parent_items = self._dependencies.get(current_item)
+ if current_parent_items is None:
+ if soft:
+ current_parent_items = set()
+ else:
+ raise Exception("Dependent item {} not found: ".format(
+ dependent))
+ new_parent_items = current_parent_items - all_parent_items
+ items_to_iterate |= new_parent_items
+ all_parent_items |= new_parent_items
+ return all_parent_items
+
+
+class VarGraph(DependencyGraph):
def __init__(self, vars):
+ super(VarGraph, self).__init__()
self.vars = {}
self._varnames = set()
- self._dependencies = {} # dependent_var_name -> set(parent_var_names)
for k, v in vars.items():
self._varnames.add(k)
for k, v in vars.items():
@@ -38,28 +91,21 @@
raise Exception("Variable {} already added".format(key))
self.vars[key] = value
# Append the dependency information
- self._dependencies.setdefault(key, set())
+ dependencies = set()
+ for dependency in self.getDependencies(value):
+ if dependency == key:
+ # A variable is allowed to reference itself; no
+ # dependency link needed in that case.
+ continue
+ if dependency not in self._varnames:
+ # It's not necessary to create a link for an
+ # external variable.
+ continue
+ dependencies.add(dependency)
try:
- for dependency in self.getDependencies(value):
- if dependency == key:
- # A variable is allowed to reference itself; no
- # dependency link needed in that case.
- continue
- if dependency not in self._varnames:
- # It's not necessary to create a link for an
- # external variable.
- continue
- # Make sure a circular dependency is never created
- ancestor_vars = self._getParentVarNamesRecursively(
- dependency, soft=True)
- ancestor_vars.add(dependency)
- if any((key == anc_var) for anc_var in ancestor_vars):
- raise Exception("Dependency cycle detected in var {}".
- format(key))
- self._dependencies[key].add(dependency)
+ self.add(key, dependencies)
except Exception:
del self.vars[key]
- del self._dependencies[key]
raise
def getVars(self):
@@ -67,59 +113,117 @@
keys = sorted(self.vars.keys())
seen = set()
for key in keys:
- dependencies = self.getDependentVarsRecursively(key)
+ dependencies = self.getDependenciesRecursively(key)
for var in dependencies + [key]:
if var not in seen:
ret.append((var, self.vars[var]))
seen.add(var)
return ret
- def getDependentVarsRecursively(self, parent_var):
- dependent_vars = []
- current_dependent_vars = self._dependencies[parent_var]
- for current_var in current_dependent_vars:
- if current_var not in dependent_vars:
- dependent_vars.append(current_var)
- for dep in self.getDependentVarsRecursively(current_var):
- if dep not in dependent_vars:
- dependent_vars.append(dep)
- return dependent_vars
+class PluginGraph(DependencyGraph):
+ def __init__(self, base_dir, plugins):
+ super(PluginGraph, self).__init__()
+ # The dependency trees expressed by all the plugins we found
+ # (which may be more than those the job is using).
+ self._plugin_dependencies = {}
+ self.loadPluginNames(base_dir)
- def _getParentVarNamesRecursively(self, dependent_var, soft=False):
- all_parent_vars = set()
- vars_to_iterate = set([dependent_var])
- while len(vars_to_iterate) > 0:
- current_var = vars_to_iterate.pop()
- current_parent_vars = self._dependencies.get(current_var)
- if current_parent_vars is None:
- if soft:
- current_parent_vars = set()
- else:
- raise Exception("Dependent var {} not found: ".format(
- dependent_var))
- new_parent_vars = current_parent_vars - all_parent_vars
- vars_to_iterate |= new_parent_vars
- all_parent_vars |= new_parent_vars
- return all_parent_vars
+ self.plugins = {}
+ self._pluginnames = set()
+ for k, v in plugins.items():
+ self._pluginnames.add(k)
+ for k, v in plugins.items():
+ self._addPlugin(k, str(v))
+
+ def loadPluginNames(self, base_dir):
+ if base_dir is None:
+ return
+ git_roots = []
+ for root, dirs, files in os.walk(base_dir):
+ if '.git' not in dirs:
+ continue
+ # Don't go deeper than git roots
+ dirs[:] = []
+ git_roots.append(root)
+ for root in git_roots:
+ devstack = os.path.join(root, 'devstack')
+ if not (os.path.exists(devstack) and os.path.isdir(devstack)):
+ continue
+ settings = os.path.join(devstack, 'settings')
+ if not (os.path.exists(settings) and os.path.isfile(settings)):
+ continue
+ self.loadDevstackPluginInfo(settings)
+
+ define_re = re.compile(r'^define_plugin\s+(\w+).*')
+ require_re = re.compile(r'^plugin_requires\s+(\w+)\s+(\w+).*')
+ def loadDevstackPluginInfo(self, fn):
+ name = None
+ reqs = set()
+ with open(fn) as f:
+ for line in f:
+ m = self.define_re.match(line)
+ if m:
+ name = m.group(1)
+ m = self.require_re.match(line)
+ if m:
+ if name == m.group(1):
+ reqs.add(m.group(2))
+ if name and reqs:
+ self._plugin_dependencies[name] = reqs
+
+ def getDependencies(self, value):
+ return self._plugin_dependencies.get(value, [])
+
+ def _addPlugin(self, key, value):
+ if key in self.plugins:
+ raise Exception("Plugin {} already added".format(key))
+ self.plugins[key] = value
+ # Append the dependency information
+ dependencies = set()
+ for dependency in self.getDependencies(key):
+ if dependency == key:
+ continue
+ dependencies.add(dependency)
+ try:
+ self.add(key, dependencies)
+ except Exception:
+ del self.plugins[key]
+ raise
+
+ def getPlugins(self):
+ ret = []
+ keys = sorted(self.plugins.keys())
+ seen = set()
+ for key in keys:
+ dependencies = self.getDependenciesRecursively(key)
+ for plugin in dependencies + [key]:
+ if plugin not in seen:
+ ret.append((plugin, self.plugins[plugin]))
+ seen.add(plugin)
+ return ret
class LocalConf(object):
- def __init__(self, localrc, localconf, base_services, services, plugins):
+ def __init__(self, localrc, localconf, base_services, services, plugins,
+ base_dir, projects):
self.localrc = []
self.meta_sections = {}
+ self.plugin_deps = {}
+ self.base_dir = base_dir
+ self.projects = projects
if plugins:
self.handle_plugins(plugins)
if services or base_services:
self.handle_services(base_services, services or {})
- if localrc:
- self.handle_localrc(localrc)
+ self.handle_localrc(localrc)
if localconf:
self.handle_localconf(localconf)
def handle_plugins(self, plugins):
- for k, v in plugins.items():
+ pg = PluginGraph(self.base_dir, plugins)
+ for k, v in pg.getPlugins():
if v:
self.localrc.append('enable_plugin {} {}'.format(k, v))
@@ -137,9 +241,22 @@
self.localrc.append('enable_service {}'.format(k))
def handle_localrc(self, localrc):
- vg = VarGraph(localrc)
- for k, v in vg.getVars():
- self.localrc.append('{}={}'.format(k, v))
+ lfg = False
+ if localrc:
+ vg = VarGraph(localrc)
+ for k, v in vg.getVars():
+ self.localrc.append('{}={}'.format(k, v))
+ if k == 'LIBS_FROM_GIT':
+ lfg = True
+
+ if not lfg and self.projects:
+ required_projects = []
+ for project_name, project_info in self.projects.items():
+ if project_info.get('required'):
+ required_projects.append(project_info['short_name'])
+ if required_projects:
+ self.localrc.append('LIBS_FROM_GIT={}'.format(
+ ','.join(required_projects)))
def handle_localconf(self, localconf):
for phase, phase_data in localconf.items():
@@ -171,7 +288,9 @@
services=dict(type='dict'),
localrc=dict(type='dict'),
local_conf=dict(type='dict'),
+ base_dir=dict(type='path'),
path=dict(type='str'),
+ projects=dict(type='dict'),
)
)
@@ -180,14 +299,19 @@
p.get('local_conf'),
p.get('base_services'),
p.get('services'),
- p.get('plugins'))
+ p.get('plugins'),
+ p.get('base_dir'),
+ p.get('projects'))
lc.write(p['path'])
module.exit_json()
-from ansible.module_utils.basic import * # noqa
-from ansible.module_utils.basic import AnsibleModule
+try:
+ from ansible.module_utils.basic import * # noqa
+ from ansible.module_utils.basic import AnsibleModule
+except ImportError:
+ pass
if __name__ == '__main__':
main()
diff --git a/roles/write-devstack-local-conf/library/test.py b/roles/write-devstack-local-conf/library/test.py
new file mode 100644
index 0000000..7ccb68f
--- /dev/null
+++ b/roles/write-devstack-local-conf/library/test.py
@@ -0,0 +1,228 @@
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# 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.
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from devstack_local_conf import LocalConf
+from collections import OrderedDict
+
+class TestDevstackLocalConf(unittest.TestCase):
+ def setUp(self):
+ self.tmpdir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+
+ def test_plugins(self):
+ "Test that plugins without dependencies work"
+ localrc = {'test_localrc': '1'}
+ local_conf = {'install':
+ {'nova.conf':
+ {'main':
+ {'test_conf': '2'}}}}
+ services = {'cinder': True}
+ # We use ordereddict here to make sure the plugins are in the
+ # *wrong* order for testing.
+ plugins = OrderedDict([
+ ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+ ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+ ('baz', 'git://git.openstack.org/openstack/baz-plugin'),
+ ])
+ p = dict(localrc=localrc,
+ local_conf=local_conf,
+ base_services=[],
+ services=services,
+ plugins=plugins,
+ base_dir='./test',
+ path=os.path.join(self.tmpdir, 'test.local.conf'))
+ lc = LocalConf(p.get('localrc'),
+ p.get('local_conf'),
+ p.get('base_services'),
+ p.get('services'),
+ p.get('plugins'),
+ p.get('base_dir'),
+ p.get('projects'))
+ lc.write(p['path'])
+
+ plugins = []
+ with open(p['path']) as f:
+ for line in f:
+ if line.startswith('enable_plugin'):
+ plugins.append(line.split()[1])
+ self.assertEqual(['bar', 'baz', 'foo'], plugins)
+
+
+ def test_plugin_deps(self):
+ "Test that plugins with dependencies work"
+ os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', 'devstack'))
+ os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', '.git'))
+ os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', 'devstack'))
+ os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', '.git'))
+ with open(os.path.join(
+ self.tmpdir,
+ 'foo-plugin', 'devstack', 'settings'), 'w') as f:
+ f.write('define_plugin foo\n')
+ with open(os.path.join(
+ self.tmpdir,
+ 'bar-plugin', 'devstack', 'settings'), 'w') as f:
+ f.write('define_plugin bar\n')
+ f.write('plugin_requires bar foo\n')
+
+ localrc = {'test_localrc': '1'}
+ local_conf = {'install':
+ {'nova.conf':
+ {'main':
+ {'test_conf': '2'}}}}
+ services = {'cinder': True}
+ # We use ordereddict here to make sure the plugins are in the
+ # *wrong* order for testing.
+ plugins = OrderedDict([
+ ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+ ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+ ])
+ p = dict(localrc=localrc,
+ local_conf=local_conf,
+ base_services=[],
+ services=services,
+ plugins=plugins,
+ base_dir=self.tmpdir,
+ path=os.path.join(self.tmpdir, 'test.local.conf'))
+
+ def test_libs_from_git(self):
+ "Test that LIBS_FROM_GIT is auto-generated"
+ projects = {
+ 'git.openstack.org/openstack/nova': {
+ 'required': True,
+ 'short_name': 'nova',
+ },
+ 'git.openstack.org/openstack/oslo.messaging': {
+ 'required': True,
+ 'short_name': 'oslo.messaging',
+ },
+ 'git.openstack.org/openstack/devstack-plugin': {
+ 'required': False,
+ 'short_name': 'devstack-plugin',
+ },
+ }
+ p = dict(base_services=[],
+ base_dir='./test',
+ path=os.path.join(self.tmpdir, 'test.local.conf'),
+ projects=projects)
+ lc = LocalConf(p.get('localrc'),
+ p.get('local_conf'),
+ p.get('base_services'),
+ p.get('services'),
+ p.get('plugins'),
+ p.get('base_dir'),
+ p.get('projects'))
+ lc.write(p['path'])
+
+ lfg = None
+ with open(p['path']) as f:
+ for line in f:
+ if line.startswith('LIBS_FROM_GIT'):
+ lfg = line.strip().split('=')[1]
+ self.assertEqual('nova,oslo.messaging', lfg)
+
+ def test_overridelibs_from_git(self):
+ "Test that LIBS_FROM_GIT can be overridden"
+ localrc = {'LIBS_FROM_GIT': 'oslo.db'}
+ projects = {
+ 'git.openstack.org/openstack/nova': {
+ 'required': True,
+ 'short_name': 'nova',
+ },
+ 'git.openstack.org/openstack/oslo.messaging': {
+ 'required': True,
+ 'short_name': 'oslo.messaging',
+ },
+ 'git.openstack.org/openstack/devstack-plugin': {
+ 'required': False,
+ 'short_name': 'devstack-plugin',
+ },
+ }
+ p = dict(localrc=localrc,
+ base_services=[],
+ base_dir='./test',
+ path=os.path.join(self.tmpdir, 'test.local.conf'),
+ projects=projects)
+ lc = LocalConf(p.get('localrc'),
+ p.get('local_conf'),
+ p.get('base_services'),
+ p.get('services'),
+ p.get('plugins'),
+ p.get('base_dir'),
+ p.get('projects'))
+ lc.write(p['path'])
+
+ lfg = None
+ with open(p['path']) as f:
+ for line in f:
+ if line.startswith('LIBS_FROM_GIT'):
+ lfg = line.strip().split('=')[1]
+ self.assertEqual('oslo.db', lfg)
+
+ def test_plugin_circular_deps(self):
+ "Test that plugins with circular dependencies fail"
+ os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', 'devstack'))
+ os.makedirs(os.path.join(self.tmpdir, 'foo-plugin', '.git'))
+ os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', 'devstack'))
+ os.makedirs(os.path.join(self.tmpdir, 'bar-plugin', '.git'))
+ with open(os.path.join(
+ self.tmpdir,
+ 'foo-plugin', 'devstack', 'settings'), 'w') as f:
+ f.write('define_plugin foo\n')
+ f.write('plugin_requires foo bar\n')
+ with open(os.path.join(
+ self.tmpdir,
+ 'bar-plugin', 'devstack', 'settings'), 'w') as f:
+ f.write('define_plugin bar\n')
+ f.write('plugin_requires bar foo\n')
+
+ localrc = {'test_localrc': '1'}
+ local_conf = {'install':
+ {'nova.conf':
+ {'main':
+ {'test_conf': '2'}}}}
+ services = {'cinder': True}
+ # We use ordereddict here to make sure the plugins are in the
+ # *wrong* order for testing.
+ plugins = OrderedDict([
+ ('bar', 'git://git.openstack.org/openstack/bar-plugin'),
+ ('foo', 'git://git.openstack.org/openstack/foo-plugin'),
+ ])
+ p = dict(localrc=localrc,
+ local_conf=local_conf,
+ base_services=[],
+ services=services,
+ plugins=plugins,
+ base_dir=self.tmpdir,
+ path=os.path.join(self.tmpdir, 'test.local.conf'))
+ with self.assertRaises(Exception):
+ lc = LocalConf(p.get('localrc'),
+ p.get('local_conf'),
+ p.get('base_services'),
+ p.get('services'),
+ p.get('plugins'),
+ p.get('base_dir'))
+ lc.write(p['path'])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/roles/write-devstack-local-conf/tasks/main.yaml b/roles/write-devstack-local-conf/tasks/main.yaml
index cc21426..a294cae 100644
--- a/roles/write-devstack-local-conf/tasks/main.yaml
+++ b/roles/write-devstack-local-conf/tasks/main.yaml
@@ -8,3 +8,5 @@
services: "{{ devstack_services|default(omit) }}"
localrc: "{{ devstack_localrc|default(omit) }}"
local_conf: "{{ devstack_local_conf|default(omit) }}"
+ base_dir: "{{ devstack_base_dir|default(omit) }}"
+ projects: "{{ zuul.projects }}"
diff --git a/stack.sh b/stack.sh
index badc1a1..6899fa0 100755
--- a/stack.sh
+++ b/stack.sh
@@ -221,7 +221,7 @@
# Warn users who aren't on an explicitly supported distro, but allow them to
# override check and attempt installation with ``FORCE=yes ./stack``
-if [[ ! ${DISTRO} =~ (xenial|zesty|artful|stretch|jessie|f25|f26|f27|opensuse-42.3|opensuse-tumbleweed|rhel7) ]]; then
+if [[ ! ${DISTRO} =~ (xenial|artful|bionic|stretch|jessie|f25|f26|f27|opensuse-42.3|opensuse-tumbleweed|rhel7) ]]; then
echo "WARNING: this script has not been tested on $DISTRO"
if [[ "$FORCE" != "yes" ]]; then
die $LINENO "If you wish to run this script anyway run with FORCE=yes"
@@ -1398,11 +1398,6 @@
# Check the status of running services
service_check
-# ensure that all the libraries we think we installed from git,
-# actually were.
-check_libs_from_git
-
-
# Configure nova cellsv2
# ----------------------
diff --git a/stackrc b/stackrc
index e8c35f4..3c4e437 100644
--- a/stackrc
+++ b/stackrc
@@ -133,7 +133,7 @@
# base name of the directory from which they are installed. See
# enable_python3_package to edit this variable and use_python3_for to
# test membership.
-export ENABLED_PYTHON3_PACKAGES="nova,glance,cinder,uwsgi,python-openstackclient,python-openstacksdk"
+export ENABLED_PYTHON3_PACKAGES="nova,glance,cinder,uwsgi,python-openstackclient,openstacksdk"
# Explicitly list services not to run under Python 3. See
# disable_python3_package to edit this variable.
@@ -525,6 +525,10 @@
GITBRANCH["ceilometermiddleware"]=${CEILOMETERMIDDLEWARE_BRANCH:-$TARGET_BRANCH}
GITDIR["ceilometermiddleware"]=$DEST/ceilometermiddleware
+# openstacksdk OpenStack Python SDK
+GITREPO["openstacksdk"]=${OPENSTACKSDK_REPO:-${GIT_BASE}/openstack/openstacksdk.git}
+GITBRANCH["openstacksdk"]=${OPENSTACKSDK_BRANCH:-$TARGET_BRANCH}
+
# os-brick library to manage local volume attaches
GITREPO["os-brick"]=${OS_BRICK_REPO:-${GIT_BASE}/openstack/os-brick.git}
GITBRANCH["os-brick"]=${OS_BRICK_BRANCH:-$TARGET_BRANCH}
@@ -542,10 +546,6 @@
GITREPO["osc-lib"]=${OSC_LIB_REPO:-${GIT_BASE}/openstack/osc-lib.git}
GITBRANCH["osc-lib"]=${OSC_LIB_BRANCH:-$TARGET_BRANCH}
-# python-openstacksdk OpenStack Python SDK
-GITREPO["python-openstacksdk"]=${OPENSTACKSDK_REPO:-${GIT_BASE}/openstack/python-openstacksdk.git}
-GITBRANCH["python-openstacksdk"]=${OPENSTACKSDK_BRANCH:-$TARGET_BRANCH}
-
# ironic common lib
GITREPO["ironic-lib"]=${IRONIC_LIB_REPO:-${GIT_BASE}/openstack/ironic-lib.git}
GITBRANCH["ironic-lib"]=${IRONIC_LIB_BRANCH:-$TARGET_BRANCH}
@@ -625,12 +625,7 @@
case "$VIRT_DRIVER" in
ironic|libvirt)
LIBVIRT_TYPE=${LIBVIRT_TYPE:-kvm}
- # If ENABLE_VOLUME_MULTIATTACH is True, the Ubuntu Cloud Archive can't
- # be used until it provides libvirt>=3.10, and with older versions of
- # Ubuntu the group is "libvirtd".
- # TODO(mriedem): Remove the ENABLE_VOLUME_MULTIATTACH check when
- # UCA has libvirt>=3.10.
- if [[ "$os_VENDOR" =~ (Debian|Ubuntu) && "${ENABLE_VOLUME_MULTIATTACH}" == "False" ]]; then
+ if [[ "$os_VENDOR" =~ (Debian|Ubuntu) ]]; then
# The groups change with newer libvirt. Older Ubuntu used
# 'libvirtd', but now uses libvirt like Debian. Do a quick check
# to see if libvirtd group already exists to handle grenade's case.
@@ -737,11 +732,11 @@
EXTRA_CACHE_URLS=""
# etcd3 defaults
-ETCD_VERSION=${ETCD_VERSION:-v3.1.10}
-ETCD_SHA256_AMD64=${ETCD_SHA256_AMD64:-"2d335f298619c6fb02b1124773a56966e448ad9952b26fea52909da4fe80d2be"}
-# NOTE(sdague): etcd v3.1.10 doesn't have anything for these architectures, though 3.2.x does.
-ETCD_SHA256_ARM64=${ETCD_SHA256_ARM64:-""}
-ETCD_SHA256_PPC64=${ETCD_SHA256_PPC64:-""}
+ETCD_VERSION=${ETCD_VERSION:-v3.2.17}
+ETCD_SHA256_AMD64=${ETCD_SHA256_AMD64:-"0a75e794502e2e76417b19da2807a9915fa58dcbf0985e397741d570f4f305cd"}
+ETCD_SHA256_ARM64=${ETCD_SHA256_ARM64:-"0ab4621c44c79d17d94e43bd184d0f23b763a3669056ce4ae2d0b2942410a98f"}
+ETCD_SHA256_PPC64=${ETCD_SHA256_PPC64:-"69e1279c4a2a52256b78d2a8dd23346ac46b836e678b971a459f2afaef3c275e"}
+# etcd v3.2.x doesn't have anything for s390x
ETCD_SHA256_S390X=${ETCD_SHA256_S390X:-""}
# Make sure etcd3 downloads the correct architecture
if is_arch "x86_64"; then
diff --git a/tests/test_libs_from_pypi.sh b/tests/test_libs_from_pypi.sh
index a544b56..c3b4457 100755
--- a/tests/test_libs_from_pypi.sh
+++ b/tests/test_libs_from_pypi.sh
@@ -38,7 +38,7 @@
ALL_LIBS+=" oslo.serialization"
ALL_LIBS+=" python-openstackclient osc-lib osc-placement"
ALL_LIBS+=" os-client-config oslo.rootwrap"
-ALL_LIBS+=" oslo.i18n oslo.utils python-openstacksdk python-swiftclient"
+ALL_LIBS+=" oslo.i18n oslo.utils openstacksdk python-swiftclient"
ALL_LIBS+=" python-neutronclient tooz ceilometermiddleware oslo.policy"
ALL_LIBS+=" debtcollector os-brick os-traits automaton futurist oslo.service"
ALL_LIBS+=" oslo.cache oslo.reports osprofiler cursive"
diff --git a/tests/test_write_devstack_local_conf_role.sh b/tests/test_write_devstack_local_conf_role.sh
new file mode 100755
index 0000000..b2bc0a2
--- /dev/null
+++ b/tests/test_write_devstack_local_conf_role.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+TOP=$(cd $(dirname "$0")/.. && pwd)
+
+# Import common functions
+source $TOP/functions
+source $TOP/tests/unittest.sh
+
+python ./roles/write-devstack-local-conf/library/test.py
diff --git a/tools/cap-pip.txt b/tools/cap-pip.txt
index c280267..f5278d7 100644
--- a/tools/cap-pip.txt
+++ b/tools/cap-pip.txt
@@ -1 +1 @@
-pip!=8
+pip!=8,<10
diff --git a/tools/fixup_stuff.sh b/tools/fixup_stuff.sh
index 90b2c8b..9147932 100755
--- a/tools/fixup_stuff.sh
+++ b/tools/fixup_stuff.sh
@@ -77,28 +77,23 @@
# Make it possible to switch this based on an environment variable as
# libvirt 2.5.0 doesn't handle nested virtualization quite well and this
# is required for the trove development environment.
-# The Pike UCA has qemu 2.10 but libvirt 3.6, therefore if
-# ENABLE_VOLUME_MULTIATTACH is True, we can't use the Pike UCA
-# because multiattach won't work with those package versions.
-# We can remove this check when the UCA has libvirt>=3.10.
function fixup_uca {
- if [[ "${ENABLE_UBUNTU_CLOUD_ARCHIVE}" == "False" || "$DISTRO" != "xenial" || \
- "${ENABLE_VOLUME_MULTIATTACH}" == "True" ]]; then
+ if [[ "${ENABLE_UBUNTU_CLOUD_ARCHIVE}" == "False" || "$DISTRO" != "xenial" ]]; then
return
fi
# This pulls in apt-add-repository
install_package "software-properties-common"
- # Use UCA for newer libvirt. Should give us libvirt 2.5.0.
+ # Use UCA for newer libvirt.
if [[ -f /etc/ci/mirror_info.sh ]] ; then
# If we are on a nodepool provided host and it has told us about where
# we can find local mirrors then use that mirror.
source /etc/ci/mirror_info.sh
- sudo apt-add-repository -y "deb $NODEPOOL_UCA_MIRROR xenial-updates/pike main"
+ sudo apt-add-repository -y "deb $NODEPOOL_UCA_MIRROR xenial-updates/queens main"
else
# Otherwise use upstream UCA
- sudo add-apt-repository -y cloud-archive:pike
+ sudo add-apt-repository -y cloud-archive:queens
fi
# Disable use of libvirt wheel since a cached wheel build might be