Merge "install and start elasticsearch on openSUSE"
diff --git a/.zuul.yaml b/.zuul.yaml
index 87eb8c5..c8bb337 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -287,7 +287,7 @@
         DEBUG_LIBVIRT_COREDUMPS: True
         NOVA_VNC_ENABLED: true
         VNCSERVER_LISTEN: 0.0.0.0
-        VNCSERVER_PROXYCLIENT_ADDRESS: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
+        VNCSERVER_PROXYCLIENT_ADDRESS: $HOST_IP
       devstack_local_conf:
         post-config:
           $NEUTRON_CONF:
@@ -381,6 +381,9 @@
           # Subnode specific settings
           GLANCE_HOSTPORT: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}:9292"
           Q_HOST: "{{ hostvars['controller']['nodepool']['private_ipv4'] }}"
+          NOVA_VNC_ENABLED: true
+          VNCSERVER_LISTEN: 0.0.0.0
+          VNCSERVER_PROXYCLIENT_ADDRESS: $HOST_IP
 
 - job:
     name: devstack-multinode
diff --git a/files/rpms/cinder b/files/rpms/cinder
index e6addc6..058c235 100644
--- a/files/rpms/cinder
+++ b/files/rpms/cinder
@@ -1,5 +1,5 @@
 iscsi-initiator-utils
 lvm2
 qemu-img
-scsi-target-utils # not:rhel7,f25,f26,f27 NOPRIME
-targetcli # dist:rhel7,f25,f26,f27 NOPRIME
+scsi-target-utils # not:rhel7,f25,f26,f27,f28 NOPRIME
+targetcli # dist:rhel7,f25,f26,f27,f28 NOPRIME
diff --git a/files/rpms/general b/files/rpms/general
index 5d9a4ad..c7863e4 100644
--- a/files/rpms/general
+++ b/files/rpms/general
@@ -9,9 +9,9 @@
 graphviz # needed only for docs
 httpd
 httpd-devel
-iptables-services  # NOPRIME f25,f26,f27
+iptables-services  # NOPRIME f25,f26,f27,f28
 java-1.7.0-openjdk-headless  # NOPRIME rhel7
-java-1.8.0-openjdk-headless  # NOPRIME f25,f26,f27
+java-1.8.0-openjdk-headless  # NOPRIME f25,f26,f27,f28
 libffi-devel
 libjpeg-turbo-devel # Pillow 3.0.0
 libxml2-devel # lxml
diff --git a/files/rpms/nova b/files/rpms/nova
index 9fb7282..4140cd7 100644
--- a/files/rpms/nova
+++ b/files/rpms/nova
@@ -7,7 +7,7 @@
 genisoimage # required for config_drive
 iptables
 iputils
-kernel-modules # dist:f25,f26,f27
+kernel-modules # dist:f25,f26,f27,f28
 kpartx
 libxml2-python
 m2crypto
diff --git a/files/rpms/swift b/files/rpms/swift
index be0db14..f2f5de6 100644
--- a/files/rpms/swift
+++ b/files/rpms/swift
@@ -2,7 +2,7 @@
 liberasurecode-devel
 memcached
 pyxattr
-rsync-daemon # dist:f25,f26,f27
+rsync-daemon # dist:f25,f26,f27,f28
 sqlite
 xfsprogs
 xinetd
diff --git a/functions-common b/functions-common
index b1b0995..25e28bd 100644
--- a/functions-common
+++ b/functions-common
@@ -2128,7 +2128,7 @@
 function python3_version {
     local python3_version
     python3_version=$(_get_python_version python3)
-    echo "python${python_version}"
+    echo "python${python3_version}"
 }
 
 
diff --git a/lib/horizon b/lib/horizon
index fab41bb..293a627 100644
--- a/lib/horizon
+++ b/lib/horizon
@@ -87,7 +87,7 @@
     _horizon_config_set $local_settings "" WEBROOT \"$HORIZON_APACHE_ROOT/\"
 
     _horizon_config_set $local_settings "" COMPRESS_OFFLINE True
-    _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_DEFAULT_ROLE \"Member\"
+    _horizon_config_set $local_settings "" OPENSTACK_KEYSTONE_DEFAULT_ROLE \"member\"
 
     _horizon_config_set $local_settings "" OPENSTACK_HOST \"${KEYSTONE_SERVICE_HOST}\"
 
diff --git a/lib/keystone b/lib/keystone
index 696e351..7978fea 100644
--- a/lib/keystone
+++ b/lib/keystone
@@ -309,30 +309,32 @@
 # service              --         --
 # --                   --         service
 # --                   --         ResellerAdmin
-# --                   --         Member
+# --                   --         member
 # demo                 admin      admin
-# demo                 demo       Member, anotherrole
+# demo                 demo       member, anotherrole
 # alt_demo             admin      admin
-# alt_demo             alt_demo   Member, anotherrole
-# invisible_to_admin   demo       Member
+# alt_demo             alt_demo   member, anotherrole
+# invisible_to_admin   demo       member
 
 # Group                Users            Roles                 Project
 # ------------------------------------------------------------------
 # admins               admin            admin                 admin
-# nonadmins            demo, alt_demo   Member, anotherrole   demo, alt_demo
+# nonadmins            demo, alt_demo   member, anotherrole   demo, alt_demo
 
 
 # Migrated from keystone_data.sh
 function create_keystone_accounts {
 
-    # The keystone bootstrapping process (performed via keystone-manage bootstrap)
-    # creates an admin user, admin role and admin project. As a sanity check
-    # we exercise the CLI to retrieve the IDs for these values.
+    # The keystone bootstrapping process (performed via keystone-manage
+    # bootstrap) creates an admin user, admin role, member role, and admin
+    # project. As a sanity check we exercise the CLI to retrieve the IDs for
+    # these values.
     local admin_project
     admin_project=$(openstack project show "admin" -f value -c id)
     local admin_user
     admin_user=$(openstack user show "admin" -f value -c id)
     local admin_role="admin"
+    local member_role="member"
 
     get_or_add_user_domain_role $admin_role $admin_user default
 
@@ -349,17 +351,6 @@
     # role is also configurable in swift-proxy.conf
     get_or_create_role ResellerAdmin
 
-    # The Member role is used by Horizon and Swift so we need to keep it:
-    local member_role="member"
-
-    # Capital Member role is legacy hard coded in Horizon / Swift
-    # configs. Keep it around.
-    get_or_create_role "Member"
-
-    # The reality is that the rest of the roles listed below honestly
-    # should work by symbolic names.
-    get_or_create_role $member_role
-
     # another_role demonstrates that an arbitrary role may be created and used
     # TODO(sleepsonthefloor): show how this can be used for rbac in the future!
     local another_role="anotherrole"
diff --git a/lib/neutron b/lib/neutron
index 3cad80a..9f9b132 100644
--- a/lib/neutron
+++ b/lib/neutron
@@ -325,25 +325,28 @@
 }
 
 # Make Neutron-required changes to nova.conf
+# Takes a single optional argument which is the config file to update,
+# if not passed $NOVA_CONF is used.
 function configure_neutron_nova_new {
-    iniset $NOVA_CONF DEFAULT use_neutron True
-    iniset $NOVA_CONF neutron auth_type "password"
-    iniset $NOVA_CONF neutron auth_url "$KEYSTONE_SERVICE_URI"
-    iniset $NOVA_CONF neutron username neutron
-    iniset $NOVA_CONF neutron password "$SERVICE_PASSWORD"
-    iniset $NOVA_CONF neutron user_domain_name "Default"
-    iniset $NOVA_CONF neutron project_name "$SERVICE_TENANT_NAME"
-    iniset $NOVA_CONF neutron project_domain_name "Default"
-    iniset $NOVA_CONF neutron auth_strategy $NEUTRON_AUTH_STRATEGY
-    iniset $NOVA_CONF neutron region_name "$REGION_NAME"
+    local conf=${1:-$NOVA_CONF}
+    iniset $conf DEFAULT use_neutron True
+    iniset $conf neutron auth_type "password"
+    iniset $conf neutron auth_url "$KEYSTONE_SERVICE_URI"
+    iniset $conf neutron username neutron
+    iniset $conf neutron password "$SERVICE_PASSWORD"
+    iniset $conf neutron user_domain_name "Default"
+    iniset $conf neutron project_name "$SERVICE_TENANT_NAME"
+    iniset $conf neutron project_domain_name "Default"
+    iniset $conf neutron auth_strategy $NEUTRON_AUTH_STRATEGY
+    iniset $conf neutron region_name "$REGION_NAME"
 
-    iniset $NOVA_CONF DEFAULT firewall_driver nova.virt.firewall.NoopFirewallDriver
+    iniset $conf DEFAULT firewall_driver nova.virt.firewall.NoopFirewallDriver
 
     # optionally set options in nova_conf
-    neutron_plugin_create_nova_conf
+    neutron_plugin_create_nova_conf $conf
 
     if is_service_enabled neutron-metadata-agent; then
-        iniset $NOVA_CONF neutron service_metadata_proxy "True"
+        iniset $conf neutron service_metadata_proxy "True"
     fi
 
 }
@@ -568,9 +571,23 @@
 function configure_neutron_nova {
     if is_neutron_legacy_enabled; then
         # Call back to old function
-        create_nova_conf_neutron "$@"
+        create_nova_conf_neutron $NOVA_CONF
+        if [[ "${CELLSV2_SETUP}" == "superconductor" ]]; then
+            for i in $(seq 1 $NOVA_NUM_CELLS); do
+                local conf
+                conf=$(conductor_conf $i)
+                create_nova_conf_neutron $conf
+            done
+        fi
     else
-        configure_neutron_nova_new "$@"
+        configure_neutron_nova_new $NOVA_CONF
+        if [[ "${CELLSV2_SETUP}" == "superconductor" ]]; then
+            for i in $(seq 1 $NOVA_NUM_CELLS); do
+                local conf
+                conf=$(conductor_conf $i)
+                configure_neutron_nova_new $conf
+            done
+        fi
     fi
 }
 
diff --git a/lib/neutron-legacy b/lib/neutron-legacy
index 0cd7e31..bee032a 100644
--- a/lib/neutron-legacy
+++ b/lib/neutron-legacy
@@ -366,31 +366,32 @@
 }
 
 function create_nova_conf_neutron {
-    iniset $NOVA_CONF DEFAULT use_neutron True
-    iniset $NOVA_CONF neutron auth_type "password"
-    iniset $NOVA_CONF neutron auth_url "$KEYSTONE_AUTH_URI"
-    iniset $NOVA_CONF neutron username "$Q_ADMIN_USERNAME"
-    iniset $NOVA_CONF neutron password "$SERVICE_PASSWORD"
-    iniset $NOVA_CONF neutron user_domain_name "$SERVICE_DOMAIN_NAME"
-    iniset $NOVA_CONF neutron project_name "$SERVICE_PROJECT_NAME"
-    iniset $NOVA_CONF neutron project_domain_name "$SERVICE_DOMAIN_NAME"
-    iniset $NOVA_CONF neutron auth_strategy "$Q_AUTH_STRATEGY"
-    iniset $NOVA_CONF neutron region_name "$REGION_NAME"
+    local conf=${1:-$NOVA_CONF}
+    iniset $conf DEFAULT use_neutron True
+    iniset $conf neutron auth_type "password"
+    iniset $conf neutron auth_url "$KEYSTONE_AUTH_URI"
+    iniset $conf neutron username "$Q_ADMIN_USERNAME"
+    iniset $conf neutron password "$SERVICE_PASSWORD"
+    iniset $conf neutron user_domain_name "$SERVICE_DOMAIN_NAME"
+    iniset $conf neutron project_name "$SERVICE_PROJECT_NAME"
+    iniset $conf neutron project_domain_name "$SERVICE_DOMAIN_NAME"
+    iniset $conf neutron auth_strategy "$Q_AUTH_STRATEGY"
+    iniset $conf neutron region_name "$REGION_NAME"
 
     if [[ "$Q_USE_SECGROUP" == "True" ]]; then
         LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
-        iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
+        iniset $conf DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
     fi
 
     # optionally set options in nova_conf
-    neutron_plugin_create_nova_conf
+    neutron_plugin_create_nova_conf $conf
 
     if is_service_enabled q-meta; then
-        iniset $NOVA_CONF neutron service_metadata_proxy "True"
+        iniset $conf neutron service_metadata_proxy "True"
     fi
 
-    iniset $NOVA_CONF DEFAULT vif_plugging_is_fatal "$VIF_PLUGGING_IS_FATAL"
-    iniset $NOVA_CONF DEFAULT vif_plugging_timeout "$VIF_PLUGGING_TIMEOUT"
+    iniset $conf DEFAULT vif_plugging_is_fatal "$VIF_PLUGGING_IS_FATAL"
+    iniset $conf DEFAULT vif_plugging_timeout "$VIF_PLUGGING_TIMEOUT"
 }
 
 # create_mutnauq_accounts() - Set up common required neutron accounts
diff --git a/lib/neutron_plugins/nuage b/lib/neutron_plugins/nuage
index 1c04aaa..f39c7c4 100644
--- a/lib/neutron_plugins/nuage
+++ b/lib/neutron_plugins/nuage
@@ -8,10 +8,11 @@
 set +o xtrace
 
 function neutron_plugin_create_nova_conf {
+    local conf="$1"
     NOVA_OVS_BRIDGE=${NOVA_OVS_BRIDGE:-"br-int"}
-    iniset $NOVA_CONF neutron ovs_bridge $NOVA_OVS_BRIDGE
+    iniset $conf neutron ovs_bridge $NOVA_OVS_BRIDGE
     LIBVIRT_FIREWALL_DRIVER=nova.virt.firewall.NoopFirewallDriver
-    iniset $NOVA_CONF DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
+    iniset $conf DEFAULT firewall_driver $LIBVIRT_FIREWALL_DRIVER
 }
 
 function neutron_plugin_install_agent_packages {
diff --git a/lib/nova_plugins/hypervisor-fake b/lib/nova_plugins/hypervisor-fake
index 49c8dee..87ee49f 100644
--- a/lib/nova_plugins/hypervisor-fake
+++ b/lib/nova_plugins/hypervisor-fake
@@ -38,18 +38,7 @@
 function configure_nova_hypervisor {
     iniset $NOVA_CONF DEFAULT compute_driver "fake.FakeDriver"
     # Disable arbitrary limits
-    iniset $NOVA_CONF DEFAULT quota_instances -1
-    iniset $NOVA_CONF DEFAULT quota_cores -1
-    iniset $NOVA_CONF DEFAULT quota_ram -1
-    iniset $NOVA_CONF DEFAULT quota_floating_ips -1
-    iniset $NOVA_CONF DEFAULT quota_fixed_ips -1
-    iniset $NOVA_CONF DEFAULT quota_metadata_items -1
-    iniset $NOVA_CONF DEFAULT quota_injected_files -1
-    iniset $NOVA_CONF DEFAULT quota_injected_file_path_length -1
-    iniset $NOVA_CONF DEFAULT quota_security_groups -1
-    iniset $NOVA_CONF DEFAULT quota_security_group_rules -1
-    iniset $NOVA_CONF DEFAULT quota_key_pairs -1
-    iniset $NOVA_CONF filter_scheduler enabled_filters "RetryFilter,AvailabilityZoneFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,CoreFilter,RamFilter,DiskFilter"
+    iniset $NOVA_CONF quota driver nova.quota.NoopQuotaDriver
 }
 
 # install_nova_hypervisor() - Install external components
diff --git a/lib/placement b/lib/placement
index 2343ac9..a1602ba 100644
--- a/lib/placement
+++ b/lib/placement
@@ -44,8 +44,6 @@
 
 # The placement service can optionally use a separate database
 # connection. Set PLACEMENT_DB_ENABLED to True to use it.
-# NOTE(cdent): This functionality depends on some code that is not
-# yet merged in nova but is coming soon.
 PLACEMENT_DB_ENABLED=$(trueorfalse False PLACEMENT_DB_ENABLED)
 
 if is_service_enabled tls-proxy; then
@@ -151,9 +149,9 @@
 function init_placement {
     if [ "$PLACEMENT_DB_ENABLED" != False ]; then
         recreate_database placement
-        time_start "dbsync"
-        $NOVA_BIN_DIR/nova-manage --config-file $NOVA_CONF api_db sync
-        time_stop "dbsync"
+        # Database migration will be handled when nova does an api_db sync
+        # TODO(cdent): When placement is extracted we'll do our own sync
+        # here.
     fi
     create_placement_accounts
 }
diff --git a/roles/write-devstack-local-conf/README.rst b/roles/write-devstack-local-conf/README.rst
index bfce9c9..e9739cd 100644
--- a/roles/write-devstack-local-conf/README.rst
+++ b/roles/write-devstack-local-conf/README.rst
@@ -22,11 +22,12 @@
 
    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.
+   ``required-projects`` list defined by the job plus the project of
+   the change under test.  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
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 9728fef..bba7e31 100644
--- a/roles/write-devstack-local-conf/library/devstack_local_conf.py
+++ b/roles/write-devstack-local-conf/library/devstack_local_conf.py
@@ -207,12 +207,13 @@
 class LocalConf(object):
 
     def __init__(self, localrc, localconf, base_services, services, plugins,
-                 base_dir, projects):
+                 base_dir, projects, project):
         self.localrc = []
         self.meta_sections = {}
         self.plugin_deps = {}
         self.base_dir = base_dir
         self.projects = projects
+        self.project = project
         if plugins:
             self.handle_plugins(plugins)
         if services or base_services:
@@ -249,11 +250,15 @@
                 if k == 'LIBS_FROM_GIT':
                     lfg = True
 
-        if not lfg and self.projects:
+        if not lfg and (self.projects or self.project):
             required_projects = []
-            for project_name, project_info in self.projects.items():
-                if project_info.get('required'):
-                    required_projects.append(project_info['short_name'])
+            if self.projects:
+                for project_name, project_info in self.projects.items():
+                    if project_info.get('required'):
+                        required_projects.append(project_info['short_name'])
+            if self.project:
+                if self.project['short_name'] not in required_projects:
+                    required_projects.append(self.project['short_name'])
             if required_projects:
                 self.localrc.append('LIBS_FROM_GIT={}'.format(
                     ','.join(required_projects)))
@@ -291,6 +296,7 @@
             base_dir=dict(type='path'),
             path=dict(type='str'),
             projects=dict(type='dict'),
+            project=dict(type='dict'),
         )
     )
 
@@ -301,7 +307,8 @@
                    p.get('services'),
                    p.get('plugins'),
                    p.get('base_dir'),
-                   p.get('projects'))
+                   p.get('projects'),
+                   p.get('project'))
     lc.write(p['path'])
 
     module.exit_json()
diff --git a/roles/write-devstack-local-conf/library/test.py b/roles/write-devstack-local-conf/library/test.py
index 7ccb68f..791552d 100644
--- a/roles/write-devstack-local-conf/library/test.py
+++ b/roles/write-devstack-local-conf/library/test.py
@@ -57,7 +57,8 @@
                        p.get('services'),
                        p.get('plugins'),
                        p.get('base_dir'),
-                       p.get('projects'))
+                       p.get('projects'),
+                       p.get('project'))
         lc.write(p['path'])
 
         plugins = []
@@ -120,17 +121,22 @@
                 'short_name': 'devstack-plugin',
             },
         }
+        project = {
+            'short_name': 'glance',
+        }
         p = dict(base_services=[],
                  base_dir='./test',
                  path=os.path.join(self.tmpdir, 'test.local.conf'),
-                 projects=projects)
+                 projects=projects,
+                 project=project)
         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'))
+                       p.get('projects'),
+                       p.get('project'))
         lc.write(p['path'])
 
         lfg = None
@@ -138,7 +144,7 @@
             for line in f:
                 if line.startswith('LIBS_FROM_GIT'):
                     lfg = line.strip().split('=')[1]
-        self.assertEqual('nova,oslo.messaging', lfg)
+        self.assertEqual('nova,oslo.messaging,glance', lfg)
 
     def test_overridelibs_from_git(self):
         "Test that LIBS_FROM_GIT can be overridden"
@@ -168,7 +174,8 @@
                        p.get('services'),
                        p.get('plugins'),
                        p.get('base_dir'),
-                       p.get('projects'))
+                       p.get('projects'),
+                       p.get('project'))
         lc.write(p['path'])
 
         lfg = None
diff --git a/roles/write-devstack-local-conf/tasks/main.yaml b/roles/write-devstack-local-conf/tasks/main.yaml
index a294cae..9a6b083 100644
--- a/roles/write-devstack-local-conf/tasks/main.yaml
+++ b/roles/write-devstack-local-conf/tasks/main.yaml
@@ -10,3 +10,4 @@
     local_conf: "{{ devstack_local_conf|default(omit) }}"
     base_dir: "{{ devstack_base_dir|default(omit) }}"
     projects: "{{ zuul.projects }}"
+    project: "{{ zuul.project }}"
\ No newline at end of file
diff --git a/stack.sh b/stack.sh
index 2528e2b..56e00bf 100755
--- a/stack.sh
+++ b/stack.sh
@@ -894,6 +894,8 @@
     stack_install_service neutron
 fi
 
+# Nova configuration is used by placement so we need to create nova.conf
+# first.
 if is_service_enabled nova; then
     # Compute service
     stack_install_service nova
@@ -1184,6 +1186,13 @@
     init_cinder
 fi
 
+# Placement Service
+# ---------------
+
+if is_service_enabled placement; then
+    echo_summary "Configuring placement"
+    init_placement
+fi
 
 # Compute Service
 # ---------------
@@ -1202,11 +1211,6 @@
     init_nova_cells
 fi
 
-if is_service_enabled placement; then
-    echo_summary "Configuring placement"
-    init_placement
-fi
-
 
 # Extras Configuration
 # ====================
diff --git a/stackrc b/stackrc
index 3c4e437..4861819 100644
--- a/stackrc
+++ b/stackrc
@@ -800,12 +800,7 @@
 # the memory used where there are a large number of CPUs present
 # (the default number of workers for many services is the number of CPUs)
 # Also sets the minimum number of workers to 2.
-if [[ "$VIRT_DRIVER" = 'fake' ]]; then
-    # we need more workers for the large ops job
-    API_WORKERS=${API_WORKERS:=$(( ($(nproc)/2)<2 ? 2 : ($(nproc)/2) ))}
-else
-    API_WORKERS=${API_WORKERS:=$(( ($(nproc)/4)<2 ? 2 : ($(nproc)/4) ))}
-fi
+API_WORKERS=${API_WORKERS:=$(( ($(nproc)/4)<2 ? 2 : ($(nproc)/4) ))}
 
 # Service startup timeout
 SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}
@@ -819,11 +814,14 @@
 # Service graceful shutdown timeout
 WORKER_TIMEOUT=${WORKER_TIMEOUT:-90}
 
-# Support alternative yum -- in future Fedora 'dnf' will become the
-# only supported installer, but for now 'yum' and 'dnf' are both
-# available in parallel with compatible CLIs.  Allow manual switching
-# till we get to the point we need to handle this automatically
-YUM=${YUM:-yum}
+# Choose DNF on RedHat/Fedora platforms with it, or otherwise default
+# to YUM.  Can remove this when only dnf is supported (i.e. centos7
+# disappears)
+if [[ -e /usr/bin/dnf ]]; then
+    YUM=${YUM:-dnf}
+else
+    YUM=${YUM:-yum}
+fi
 
 # Common Configuration
 # --------------------