[Newton only] Do not overwrite current {neutron,nova}::host value.

In newton we are setting both nova::host and neutron::host values
explicitly to the fqdn.

This can cause problem during upgrade from Mitaka.  The previous host
value (defaulting to python socket.gethostname) could return only the
hostname[0].  It means that during upgrade we are changing this
identifier.  At restart nova/neutron creates *new* agents.  Those
agents are then unaware of existing workload.

For neutron, the problem is that due to [1] and the fact the L3 agents
are in HA mode, the previous defined workloads on those agent get lost
and FIPs become unreachable.

For nova it's no longer possible to send commands to (before upgrade)
existing vm anymore.

This patch checks the current live value of the host parameter through
a fact and set the nova::host and neutron::host value to it if we are
not in a deployment (upgrade/update)

For nova, we directly use nova-manage to get the current live value.
Using the mysql parameter directly has the advantage that it's defined
on all types of node (controller *and* compute).  As a matter of fact
the required auth parameters are usually not defined on compute node.

For neutron, when auth is available in the configuration (on
Controller) we use that.  There is no neutron-manage equivalent here
so we use the nova value when auth is unavailable.  When host is unset
they both use python.gethostname, so it should be the same value.
Using auth on controller add another level of confidence though.  And
the controller are where the l3 agents are, so better be safe than
sorry.

This patch is newton only as it's where we are setting for the first
time this parameter.  After that (ocata on) we use[2] to make sure
that those parameters are never rewritten.

[0] https://bugzilla.redhat.com/show_bug.cgi?id=1499201
[1] https://review.openstack.org/#/c/560855/
[2] need to be backported to ocata https://review.openstack.org/#/q/I8f075a5ad869ef0dc72a700dcb7be0b6efca787a

Partial-Bug: #1763322
Change-Id: Ieb92ff161d1684c214382c5eb6b5949efc3fe75c
This commit is contained in:
Sofer Athlan-Guyot 2018-04-19 12:38:31 +02:00
parent f6d398a7da
commit 6938b5d1f2
8 changed files with 206 additions and 1 deletions

View File

@ -0,0 +1,64 @@
def get_auth(component)
provider = Object.const_get "Puppet::Provider::#{component.capitalize}"
auth_func = "#{component}_credentials"
if provider.respond_to?(auth_func)
begin
q = provider.send(auth_func)
rescue Puppet::Error
q = {}
end
authenv = {
'OS_AUTH_URL' => q['auth_url'],
'OS_USERNAME' => q['username'],
'OS_PROJECT_NAME' => q['project_name'],
'OS_PASSWORD' => q['password']
}
if q.key?('region_name')
authenv['OS_REGION_NAME'] = q['region_name']
end
authenv
end
end
def get_live_value_from_auth(component)
# The path is correct for this tripleo version.
provider_file = "/etc/puppet/modules/#{component}/lib/puppet/provider/#{component}.rb"
if File.exists?(provider_file)
require_relative(provider_file)
auth_env = get_auth(component)
host = if auth_env
Facter::Core::Execution.with_env(auth_env) do
# We want to find if the current host value is the fqdn
# or the hostname. We are sure that it will be at
# least the hostname so the grep will work.
Facter::Core::Execution.execute(
"#{component} agent-list -c host -f value 2>/dev/null | grep #{Facter.value(:hostname)} 2>/dev/null",
{:on_fail => ''}
).split("\n").first
end
end
host || ''
end
end
def get_nova_live_value
Tempfile.open('get-nova-host') do |nova_stdin|
File.open(nova_stdin, 'w') do |nova_cmd|
nova_cmd.puts("import nova.conf\nprint nova.conf.CONF.host")
end
Facter::Core::Execution.execute("nova-manage shell python 2>/dev/null < #{nova_stdin} | sed -e 's/^[> ]*//'")
end
end
['nova', 'neutron'].each do |component|
Facter.add("current_#{component}_host") do
confine kernel: 'Linux'
setcode do
if component == 'nova'
get_nova_live_value.strip
else
get_live_value_from_auth(component).strip
end
end
end
end

View File

@ -63,8 +63,25 @@ class tripleo::profile::base::neutron (
elsif $dhcp_agent_count > 0 {
$dhcp_agents_per_net = $dhcp_agent_count
}
if hiera('stack_action', undef) == 'UPDATE' {
if !empty($::current_neutron_host) {
$host_real = $::current_neutron_host
}
# Try nova value as we could be on a compute node without all
# the necessary neutron definition. The host value will be identical.
elsif !empty($::current_nova_host) {
$host_real = $::current_nova_host
} else {
# We fail instead of blindly changing that value as it can
# break the overcloud.
fail("We couldn't get the live value of the neutron agent, please contact support.")
}
} else {
$host_real = hiera('neutron::host')
}
class { '::neutron' :
host => $host_real,
rabbit_hosts => $rabbit_endpoints,
dhcp_agents_per_network => $dhcp_agents_per_net,
}

View File

@ -89,8 +89,23 @@ class tripleo::profile::base::nova (
$migration_ssh_localaddrs_real = unique($migration_ssh_localaddrs)
if $step >= 4 or ($step >= 3 and $sync_db) {
if hiera('stack_action', undef) == 'UPDATE' {
if empty($::current_nova_host) {
# We fail instead of blindly changing that value as it can
# break the overcloud.
fail("We couldn't get the live value of the nova agent, please contact support.")
} else {
$host_real = $::current_nova_host
}
} else {
$host_real = hiera('nova::host')
}
$rabbit_endpoints = suffix(any2array(normalize_ip_for_uri($rabbit_hosts)), ":${rabbit_port}")
class { '::nova' :
host => $host_real,
rabbit_hosts => $rabbit_endpoints,
}
include ::nova::config

View File

@ -0,0 +1,78 @@
#
# 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.
#
require 'spec_helper'
describe 'tripleo::profile::base::neutron' do
let :params do
{ :step => 5}
end
shared_examples_for 'tripleo::profile::base::neutron' do
before :each do
facts.merge!({ :step => params[:step] })
end
context "during deployment" do
let(:facts) { super().merge({:current_neutron_host => ''})}
it 'should set neutron::host to fqdn_canonical' do
is_expected.to contain_class('neutron').with(:host => 'node.example.com')
end
end
context 'during upgrade' do
let(:facts) { super().merge({:heat_stack_action => '_update'})}
context 'when current host is different from fqdn' do
let(:facts) { super().merge({:current_neutron_host => 'node'})}
it "should use the current_host" do
is_expected.to contain_class('neutron').with(
:host => 'node',
)
end
end
context 'when current neutron host cannot be found but nova host can' do
let(:facts) { super().merge({:current_neutron_host => '',
:current_nova_host => 'node'})}
it "should use the current_nova_host" do
is_expected.to contain_class('neutron').with(
:host => 'node',
)
end
end
context 'when both nova and neutron current host cannot be found' do
let(:facts) { super().merge({:current_neutron_host => '',
:current_nova_host => '',
})}
it "fails" do
is_expected.to compile.and_raise_error(/live value of the neutron/)
end
end
end
end
on_supported_os.each do |os, facts|
context "on #{os}" do
let(:facts) do
facts.merge({
:hostname => 'node.example.com',
:current_neutron_host => 'node.example.com'
})
end
it_behaves_like 'tripleo::profile::base::neutron'
end
end
end

View File

@ -43,6 +43,7 @@ describe 'tripleo::profile::base::nova' do
it {
is_expected.to contain_class('tripleo::profile::base::nova')
is_expected.to contain_class('nova').with(
:host => 'node.example.com',
:rabbit_hosts => ['127.0.0.1:5672']
)
@ -496,13 +497,35 @@ describe 'tripleo::profile::base::nova' do
}
end
context 'during upgrade' do
let(:params) { {
:step => 4,
} }
let(:facts) { super().merge({:heat_stack_action => '_update'})}
context 'when current nova host is different from fqdn' do
let(:facts) { super().merge({:current_nova_host => 'node'})}
it "should use the current_host" do
is_expected.to contain_class('nova').with(
:host => 'node',
)
end
end
context 'when current nova host cannot be found' do
let(:facts) { super().merge({:current_nova_host => ''})}
it "fails" do
is_expected.to compile.and_raise_error(/live value of the nova/)
end
end
end
end
on_supported_os.each do |os, facts|
context "on #{os}" do
let(:facts) do
facts.merge({ :hostname => 'node.example.com' })
facts.merge({:hostname => 'node.example.com'})
end
it_behaves_like 'tripleo::profile::base::nova'

View File

@ -5,3 +5,4 @@
:datadir: './spec/fixtures/hieradata'
:hierarchy:
- default
- 'host_param%{::heat_stack_action}'

View File

@ -0,0 +1,3 @@
---
neutron::host: 'node.example.com'
nova::host: 'node.example.com'

View File

@ -0,0 +1,4 @@
---
stack_action: 'UPDATE'
neutron::host: 'node.example.com'
nova::host: 'node.example.com'