crowbar/crowbar-openstack

View on GitHub
chef/cookbooks/neutron/recipes/server.rb

Summary

Maintainability
C
1 day
Test Coverage
# Copyright 2011 Dell, 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.
#

hyperv_compute_node = search(:node, "roles:nova-compute-hyperv") || []
use_hyperv = node[:neutron][:networking_plugin] == "ml2" && !hyperv_compute_node.empty?
zvm_compute_node = search(:node, "roles:nova-compute-zvm") || []
use_zvm = node[:neutron][:networking_plugin] == "ml2" && !zvm_compute_node.empty?
use_vmware_dvs = node[:neutron][:networking_plugin] == "ml2" &&
  node[:neutron][:ml2_mechanism_drivers].include?("vmware_dvs")
octavia_nodes = search(:node, "roles:octavia-api") || []
use_octavia = !octavia_nodes.empty?

pkgs = node[:neutron][:platform][:pkgs] + node[:neutron][:platform][:pkgs_fwaas]
pkgs += node[:neutron][:platform][:pkgs_lbaas] if node[:neutron][:use_lbaas]
pkgs += node[:neutron][:platform][:infoblox_pkgs] if node[:neutron][:use_infoblox]

if use_hyperv
  pkgs << node[:neutron][:platform][:hyperv_pkg]
end
if use_zvm
  pkgs << node[:neutron][:platform][:zvm_agent_pkg]
end
use_vmware_dvs && pkgs << node[:neutron][:platform][:vmware_vsphere_pkg]

pkgs.each { |p| package p }

include_recipe "neutron::database"

if node[:neutron][:api][:protocol] == "https"
  ssl_setup "setting up ssl for neutron" do
    generate_certs node[:neutron][:ssl][:generate_certs]
    certfile node[:neutron][:ssl][:certfile]
    keyfile node[:neutron][:ssl][:keyfile]
    group node[:neutron][:group]
    fqdn node[:fqdn]
    cert_required node[:neutron][:ssl][:cert_required]
    ca_certs node[:neutron][:ssl][:ca_certs]
  end
end

include_recipe "neutron::common_config"

# remove unused plugin config snippets
all_plugin_snippets = [node[:neutron][:ml2_config_file], node[:neutron][:nsx_config_file]]
used_plugin_snippets = []
if node[:neutron][:networking_plugin] == "vmware"
  used_plugin_snippets << node[:neutron][:nsx_config_file]
else
  used_plugin_snippets << node[:neutron][:ml2_config_file]
end

(all_plugin_snippets - used_plugin_snippets).each do |config_file|
  file config_file do
    action :delete
    notifies :restart, "service[#{node[:neutron][:platform][:service_name]}]"
  end
end

# Empty the config file that is explicitly passed to neutron-server.
# This allows overriding of plugin settings using config snippets.
# NOTE: if plugin.ini is a symlink it will not replace it with regular file
#       but this is OK since all possible target files will be empty too.
file "/etc/neutron/plugin.ini" do
  owner "root"
  group node[:neutron][:platform][:group]
  mode "0640"
  content "# Please use config file snippets in /etc/neutron/neutron.conf.d/.\n" \
          "# See /etc/neutron/README.config for more details.\n"
end

directory "/var/cache/neutron" do
  owner node[:neutron][:user]
  group node[:neutron][:group]
  mode 0755
  action :create
  only_if { node[:platform_family] == "debian" }
end

# accessing the network definition directly, since the node is not using this
# network
fixed_net_def = Barclamp::Inventory.get_network_definition(node, "nova_fixed")
vlan_start = fixed_net_def["vlan"]
num_vlans = node[:neutron][:num_vlans]
vlan_end = [vlan_start + num_vlans - 1, 4094].min

gre_start = [node[:neutron][:gre][:tunnel_id_start], 1].max
gre_end = [node[:neutron][:gre][:tunnel_id_end], 2147483647].min

vni_start = [node[:neutron][:vxlan][:vni_start], 0].max
vni_end = [node[:neutron][:vxlan][:vni_end], 16777215].min

directory "/etc/neutron/plugins/ml2" do
  mode 0755
  action :create
  only_if { node[:platform_family] == "debian" }
end

# NOTE(toabctl): tenant_network types should have as first element 'ml2_type_drivers_default_tenant_network' and then the rest of the selected type drivers.
#                when creating tenant networks, it's not possible to manually select the network type. Neutron just tries every selected type until success
#                so the order is important
tenant_network_types = [[node[:neutron][:ml2_type_drivers_default_tenant_network]] + node[:neutron][:ml2_type_drivers]].flatten.uniq

interface_driver = "openvswitch"

ironic_net = Barclamp::Inventory.get_network_definition(node, "ironic")

octavia_net = Barclamp::Inventory.get_network_by_type(node, "octavia")
# get_network_by_type returns the admin network if the Octavia network isn't yet enabled
# on this node (even if the network definition exists).
has_octavia_net = !octavia_net.nil? && octavia_net.name != "admin"

case node[:neutron][:networking_plugin]
when "ml2"
  # Find out which physical interfaces we need to define in the config (depends
  # on whether one of the external networks will share the physical interface
  # with "nova_fixed".
  external_networks = ["nova_floating"]

  # Add ironic to external_networks if ironic network is configured
  external_networks << "ironic" if ironic_net

  # Add octavia to external_networks if octavia network is configured and enabled
  external_networks << "octavia" if has_octavia_net

  external_networks.concat(node[:neutron][:additional_external_networks])
  network_node = NeutronHelper.get_network_node_from_neutron_attributes(node)
  physnet_map = NeutronHelper.get_neutron_physnets(network_node, external_networks)
  physnets = physnet_map.values

  if use_zvm
    physnets.push(node[:neutron][:zvm][:zvm_xcat_mgt_vswitch])
  end

  os_sdn_net = Barclamp::Inventory.get_network_definition(node, "os_sdn")
  mtu_value = os_sdn_net.nil? ? 1500 : os_sdn_net["mtu"].to_i

  ml2_extension_drivers = ["dns_domain_ports", "port_security"]
  ml2_type_drivers = node[:neutron][:ml2_type_drivers]
  ml2_mechanism_drivers = node[:neutron][:ml2_mechanism_drivers].dup
  if use_hyperv
    ml2_mechanism_drivers.push("hyperv")
  end
  if use_zvm
    ml2_mechanism_drivers.push("zvm")
  end
  if node[:neutron][:use_l2pop] &&
      (ml2_type_drivers.include?("gre") || ml2_type_drivers.include?("vxlan"))
    ml2_mechanism_drivers.push("l2population")
  end
  if use_vmware_dvs
    # If enabled, vmware_dvs needs to come before all others, otherwise the wrong
    # type of VIF will be used when launching server instances
    ml2_mechanism_drivers.unshift(ml2_mechanism_drivers.delete("vmware_dvs"))
  end

  ml2_mech_drivers = node[:neutron][:ml2_mechanism_drivers]
  if ml2_mech_drivers.include?("linuxbridge")
    interface_driver = "linuxbridge"
  end

  # Empty the config file that is explicitly passed to neutron-server (via plugin.ini
  # symlink). This allows overriding of ml2_conf.ini settings using config snippets.
  file "/etc/neutron/plugins/ml2/ml2_conf.ini" do
    owner "root"
    group node[:neutron][:platform][:group]
    mode "0640"
    content "# Please use config file snippets in /etc/neutron/neutron.conf.d/.\n" \
            "# See /etc/neutron/README.config for more details.\n"
  end

  template node[:neutron][:ml2_config_file] do
    source "ml2_conf.ini.erb"
    owner "root"
    group node[:neutron][:platform][:group]
    mode "0640"
    variables(
      ml2_mechanism_drivers: ml2_mechanism_drivers,
      ml2_extension_drivers: ml2_extension_drivers,
      ml2_type_drivers: ml2_type_drivers,
      tenant_network_types: tenant_network_types,
      vlan_start: vlan_start,
      vlan_end: vlan_end,
      use_octavia: use_octavia,
      gre_start: gre_start,
      gre_end: gre_end,
      vxlan_start: vni_start,
      vxlan_end: vni_end,
      vxlan_mcast_group: node[:neutron][:vxlan][:multicast_group],
      external_networks: physnets,
      mtu_value: mtu_value,
      l2pop_agent_boot_time: node[:neutron][:l2pop][:agent_boot_time],
      vmware_dvs_config: node[:neutron][:vmware_dvs]
    )
    notifies :restart, "service[#{node[:neutron][:platform][:service_name]}]"
  end
when "vmware"
  directory "/etc/neutron/plugins/vmware/" do
     mode 00755
     owner "root"
     group node[:neutron][:platform][:group]
     action :create
     recursive true
     not_if { node[:platform_family] == "suse" }
  end

  # Empty the config file that is explicitly passed to neutron-server (via plugin.ini
  # symlink). This allows overriding of nsx.ini settings using config snippets.
  file "/etc/neutron/plugins/vmware/nsx.ini" do
    owner "root"
    group node[:neutron][:platform][:group]
    mode "0640"
    content "# Please use config file snippets in /etc/neutron/neutron.conf.d/.\n" \
            "# See /etc/neutron/README.config for more details.\n"
  end

  template node[:neutron][:nsx_config_file] do
    cookbook "neutron"
    source "nsx.ini.erb"
    owner "root"
    group node[:neutron][:platform][:group]
    mode "0640"
    variables(
      vmware_config: node[:neutron][:vmware]
    )
    notifies :restart, "service[#{node[:neutron][:platform][:service_name]}]"
  end
end

if node[:neutron][:networking_plugin] == "ml2"
  if node[:neutron][:ml2_mechanism_drivers].include?("cisco_nexus")
    include_recipe "neutron::cisco_support"
  elsif node[:neutron][:ml2_mechanism_drivers].include?("cisco_apic_ml2") ||
      node[:neutron][:ml2_mechanism_drivers].include?("apic_gbp")
    include_recipe "neutron::cisco_apic_support"
  end
end

if node[:neutron][:use_lbaas]
  template node[:neutron][:lbaas_service_file] do
    source "services_lbaas.conf.erb"
    owner "root"
    group node[:neutron][:platform][:group]
    mode "0640"
    variables(
      interface_driver: interface_driver
    )
    notifies :restart, "service[#{node[:neutron][:platform][:service_name]}]"
  end
end

ha_enabled = node[:neutron][:ha][:server][:enabled]

# use an increased timeout here because there are plenty of db syncs
# inside of the sync mark and also a neutron-server start/stop
crowbar_pacemaker_sync_mark "wait sync mark for neutron db sync" do
  mark "neutron_db_sync"
  action :wait
  timeout 300
  only_if { ha_enabled }
end

execute "neutron-db-manage migrate" do
  user node[:neutron][:user]
  group node[:neutron][:group]
  command "neutron-db-manage --config-file /etc/neutron/neutron.conf upgrade head"
  # We only do the sync the first time, and only if we're not doing HA or if we
  # are the founder of the HA cluster (so that it's really only done once).
  only_if { !node[:neutron][:db_synced] && (!ha_enabled || CrowbarPacemakerHelper.is_cluster_founder?(node)) }
end

if ha_enabled && CrowbarPacemakerHelper.is_cluster_founder?(node) && !node[:neutron][:db_synced]
  # Unfortunately, on first start, neutron populates the database. This is racy
  # in the HA case and causes failures to start. So we work around this by
  # quickly starting and stopping the service.
  # https://bugs.launchpad.net/neutron/+bug/1326634
  # https://bugzilla.novell.com/show_bug.cgi?id=889325
  service "workaround for races in initial db population" do
    service_name node[:neutron][:platform][:service_name]
    action [:start, :stop]
  end
end

# We want to keep a note that we've done db_sync, so we don't do it again.
# If we were doing that outside a ruby_block, we would add the note in the
# compile phase, before the actual db_sync is done (which is wrong, since it
# could possibly not be reached in case of errors).
ruby_block "mark node for neutron db_sync" do
  block do
    node.set[:neutron][:db_synced] = true
    node.save
  end
  action :nothing
  subscribes :create, "execute[neutron-db-manage migrate]", :immediately
end

# See comments for "neutron-db-manage migrate" above
execute "neutron-db-manage migrate fwaas" do
  user node[:neutron][:user]
  group node[:neutron][:group]
  command "neutron-db-manage --subproject neutron-fwaas upgrade head"
  only_if { !node[:neutron][:db_synced_fwaas] && (!ha_enabled || CrowbarPacemakerHelper.is_cluster_founder?(node)) }
end

ruby_block "mark node for neutron db_sync fwaas" do
  block do
    node.set[:neutron][:db_synced_fwaas] = true
    node.save
  end
  action :nothing
  subscribes :create, "execute[neutron-db-manage migrate fwaas]", :immediately
end

if node[:neutron][:use_lbaas]
  # See comments for "neutron-db-manage migrate" above
  execute "neutron-db-manage migrate lbaas" do
    user node[:neutron][:user]
    group node[:neutron][:group]
    command "neutron-db-manage --subproject neutron-lbaas upgrade head"
    only_if { !node[:neutron][:db_synced_lbaas] && (!ha_enabled || CrowbarPacemakerHelper.is_cluster_founder?(node)) }
  end

  ruby_block "mark node for neutron db_sync lbaas" do
    block do
      node.set[:neutron][:db_synced_lbaas] = true
      node.save
    end
    action :nothing
    subscribes :create, "execute[neutron-db-manage migrate lbaas]", :immediately
  end
end

if node[:neutron][:networking_plugin] == "ml2"
  if node[:neutron][:ml2_mechanism_drivers].include?("cisco_apic_ml2")
    # See comments for "neutron-db-manage migrate" above
    db_synced = node[:neutron][:db_synced_apic_ml2]
    is_founder = CrowbarPacemakerHelper.is_cluster_founder?(node)
    execute "apic-ml2-db-manage upgrade head" do
      user node[:neutron][:user]
      group node[:neutron][:group]
      command "apic-ml2-db-manage --config-dir /etc/neutron/neutron.conf.d upgrade head"
      only_if { !db_synced && (!ha_enabled || is_founder) }
    end

    ruby_block "mark node for apic-ml2-db-manage upgrade head" do
      block do
        node.set[:neutron][:db_synced_apic_ml2] = true
        node.save
      end
      action :nothing
      subscribes :create, "execute[apic-ml2-db-manage upgrade head]", :immediately
    end
  elsif node[:neutron][:ml2_mechanism_drivers].include?("apic_gbp")
    # See comments for "neutron-db-manage migrate" above
    db_synced = node[:neutron][:db_synced_apic_gbp]
    is_founder = CrowbarPacemakerHelper.is_cluster_founder?(node)
    execute "gbp-db-manage upgrade head" do
      user node[:neutron][:user]
      group node[:neutron][:group]
      command "gbp-db-manage --config-dir /etc/neutron/neutron.conf.d upgrade head"
      only_if { !db_synced && (!ha_enabled || is_founder) }
    end

    ruby_block "mark node for gbp-db-manage upgrade head" do
      block do
        node.set[:neutron][:db_synced_apic_gbp] = true
        node.save
      end
      action :nothing
      subscribes :create, "execute[gbp-db-manage upgrade head]", :immediately
    end
  end
end

crowbar_pacemaker_sync_mark "create-neutron_db_sync" if ha_enabled


service node[:neutron][:platform][:service_name] do
  supports status: true, restart: true
  action [:enable, :start]
  subscribes :restart, resources(template: node[:neutron][:config_file])
  if node[:neutron][:use_lbaas]
    subscribes :restart, resources(template: node[:neutron][:lbaas_config_file])
  end
end
utils_systemd_service_restart node[:neutron][:platform][:service_name] do
  action :enable
end
# neutron-server must be restarted immediately if keystone settings have changed,
# otherwise neutron requests in recipes will fail
if node[:keystone][:endpoint_changed]
  service node[:neutron][:platform][:service_name] do
    subscribes :restart, resources(template: node[:neutron][:config_file]), :immediately
  end
end

if node[:neutron][:use_infoblox]
  service node[:neutron][:platform][:infoblox_agent_name] do
    supports status: true, restart: true
    action [:enable, :start]
    subscribes :restart, resources(template: node[:neutron][:config_file])
  end
  utils_systemd_service_restart node[:neutron][:platform][:infoblox_agent_name] do
    action :enable
  end
end

include_recipe "neutron::api_register"

if ha_enabled
  log "HA support for neutron is enabled"
  include_recipe "neutron::server_ha"
else
  log "HA support for neutron is disabled"
end

# The post_install_conf recipe includes a few execute resources like this:
#
# execute "create_router" do
#   command "#{neutron_cmd} router-create router-floating"
#   not_if "out=$(#{neutron_cmd} router-list); ..."
#   action :nothing
# end
#
if node[:neutron][:create_default_networks]
  # If this runs simulatiously on multiple nodes (e.g. in a HA setup). It might
  # be that one node creates the router after the other did the "not_if" check.
  # In that case the router will be created twice (as it is perfectly fine to
  # have multiple routers with the same name). To avoid this race-condition we
  # make sure that the post_install_conf recipe is only executed on a single node
  # of the cluster.
  if !ha_enabled || CrowbarPacemakerHelper.is_cluster_founder?(node)
    include_recipe "neutron::post_install_conf"
  end

  # All non-founder nodes should wait until the founder node is done
  # evaluating if the default networks need to be created, as this can
  # take a long time. Otherwise, the founder node will be delayed and
  # might produce pacemaker sync timeouts in other recipes where such
  # a big delay isn't expected (e.g. the network_agents recipe).
  crowbar_pacemaker_sync_mark "sync mark for neutron default networks" do
    mark "neutron_default_networks"
    action :sync
    timeout 180
    only_if { ha_enabled }
  end
end