From 68ddc7a81145999594a4b52d2e59e5874aac0d10 Mon Sep 17 00:00:00 2001
From: Pradeep Kilambi <pkilambi@cisco.com>
Date: Thu, 19 Jun 2014 17:22:23 -0400
Subject: [PATCH] Support for Cisco ML2 Mech Driver

Partially Implements: blueprint support-ml2-nexus-driver

Change-Id: I5e0486c9db22abfcbab205e6f57bbafe8f0b5dd4
---
 manifests/params.pp                           | 10 +--
 manifests/plugins/ml2/cisco/nexus.pp          | 63 +++++++++++++++++
 manifests/plugins/ml2/cisco/nexus_creds.pp    | 30 ++++++++
 .../classes/neutron_plugins_cisco_ml2_spec.rb | 68 +++++++++++++++++++
 templates/ml2_conf_cisco.ini.erb              | 59 ++++++++++++++++
 5 files changed, 226 insertions(+), 4 deletions(-)
 create mode 100644 manifests/plugins/ml2/cisco/nexus.pp
 create mode 100644 manifests/plugins/ml2/cisco/nexus_creds.pp
 create mode 100644 spec/classes/neutron_plugins_cisco_ml2_spec.rb
 create mode 100644 templates/ml2_conf_cisco.ini.erb

diff --git a/manifests/params.pp b/manifests/params.pp
index 88f579e77..914fa4bcd 100644
--- a/manifests/params.pp
+++ b/manifests/params.pp
@@ -19,8 +19,9 @@ class neutron::params {
     $linuxbridge_server_package = 'openstack-neutron-linuxbridge'
     $linuxbridge_config_file    = '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini'
 
-    $cisco_server_package = 'openstack-neutron-cisco'
-    $cisco_config_file    = '/etc/neutron/plugins/cisco/cisco_plugins.ini'
+    $cisco_server_package  = 'openstack-neutron-cisco'
+    $cisco_config_file     = '/etc/neutron/plugins/cisco/cisco_plugins.ini'
+    $cisco_ml2_config_file = '/etc/neutron/plugins/ml2/ml2_conf_cisco.ini'
 
     $nvp_server_package = 'openstack-neutron-nicira'
 
@@ -69,8 +70,9 @@ class neutron::params {
     $linuxbridge_server_package = 'neutron-plugin-linuxbridge'
     $linuxbridge_config_file    = '/etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini'
 
-    $cisco_server_package = 'neutron-plugin-cisco'
-    $cisco_config_file    = '/etc/neutron/plugins/cisco/cisco_plugins.ini'
+    $cisco_server_package  = 'neutron-plugin-cisco'
+    $cisco_config_file     = '/etc/neutron/plugins/cisco/cisco_plugins.ini'
+    $cisco_ml2_config_file = '/etc/neutron/plugins/ml2/ml2_conf_cisco.ini'
 
     $nvp_server_package = 'neutron-plugin-nicira'
 
diff --git a/manifests/plugins/ml2/cisco/nexus.pp b/manifests/plugins/ml2/cisco/nexus.pp
new file mode 100644
index 000000000..079226134
--- /dev/null
+++ b/manifests/plugins/ml2/cisco/nexus.pp
@@ -0,0 +1,63 @@
+#
+# Configure the Mech Driver for cisco neutron plugin
+# More info available here:
+# https://wiki.openstack.org/wiki/Neutron/ML2/MechCiscoNexus
+#
+# === Parameters
+#
+# [*neutron_config*]
+# Neutron switch configuration for ml2_cisco_conf.ini
+# Example nexus config format:
+#  { 'switch_hostname' => {'username' => 'admin',
+#    'ssh_port' => 22,
+#    'password' => "password",
+#    'ip_address' => "172.18.117.28",
+#    'servers' => {
+#      'control01' => "portchannel:20",
+#      'control02' => "portchannel:10"
+#    }}}
+#
+
+class neutron::plugins::ml2::cisco::nexus (
+  $nexus_config = undef,
+)
+{
+  if !$nexus_config {
+    fail('No nexus config specified')
+  }
+
+  # For Ubuntu: This package is not available upstream
+  # Please use the source from:
+  # https://launchpad.net/~cisco-openstack/+archive/python-ncclient
+  # and install it manually
+  package { 'python-ncclient':
+    ensure => installed,
+  } ~> Service['neutron-server']
+
+  Neutron_plugin_ml2<||> ->
+  file { $::neutron::params::cisco_ml2_config_file:
+    owner   => 'root',
+    group   => 'root',
+    content => template('neutron/ml2_conf_cisco.ini.erb'),
+  } ~> Service['neutron-server']
+
+  file {'/var/lib/neutron/.ssh':
+    ensure  => directory,
+    owner   => 'neutron',
+    require => Package['neutron-server']
+  }
+
+  create_resources(neutron::plugins::ml2::cisco::nexus_creds, $nexus_config)
+
+  if $::osfamily == 'Debian' {
+    file_line { '/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG':
+      path    => '/etc/default/neutron-server',
+      match   => '^NEUTRON_PLUGIN_CONFIG=(.*)$',
+      line    => "NEUTRON_PLUGIN_CONFIG=${::neutron::params::cisco_ml2_config_file}",
+      require => [ Package['neutron-server'],
+                    Package['neutron-plugin-ml2']],
+      notify  => Service['neutron-server'],
+    }
+  }
+}
+
diff --git a/manifests/plugins/ml2/cisco/nexus_creds.pp b/manifests/plugins/ml2/cisco/nexus_creds.pp
new file mode 100644
index 000000000..451cfbe54
--- /dev/null
+++ b/manifests/plugins/ml2/cisco/nexus_creds.pp
@@ -0,0 +1,30 @@
+#
+# Configure the Mech Driver for cisco neutron plugin
+# More info available here:
+# https://wiki.openstack.org/wiki/Neutron/ML2/MechCiscoNexus
+#
+#
+# neutron::plugins::ml2::cisco::nexus_creds used by
+# neutron::plugins::ml2::cisco::nexus
+#
+
+define neutron::plugins::ml2::cisco::nexus_creds(
+  $username,
+  $password,
+  $servers,
+  $ip_address,
+  $ssh_port
+) {
+
+  neutron_plugin_cisco_credentials {
+    "${username}/username": value => $username;
+    "${password}/password": value => $password;
+  }
+
+  exec {'nexus_creds':
+    unless  => "/bin/cat /var/lib/neutron/.ssh/known_hosts | /bin/grep ${username}",
+    command => "/usr/bin/ssh-keyscan -t rsa ${username} >> /var/lib/neutron/.ssh/known_hosts",
+    user    => 'neutron',
+    require => Package['neutron-server']
+  }
+}
diff --git a/spec/classes/neutron_plugins_cisco_ml2_spec.rb b/spec/classes/neutron_plugins_cisco_ml2_spec.rb
new file mode 100644
index 000000000..a9a318525
--- /dev/null
+++ b/spec/classes/neutron_plugins_cisco_ml2_spec.rb
@@ -0,0 +1,68 @@
+#
+# Unit tests for neutron::plugins::ml2 class
+#
+
+require 'spec_helper'
+
+describe 'neutron::plugins::ml2::cisco::nexus' do
+
+  let :pre_condition do
+    "class { 'neutron::server': auth_password => 'password'}
+     class { 'neutron':
+      rabbit_password => 'passw0rd',
+      core_plugin     => 'neutron.plugins.ml2.plugin.Ml2Plugin' }"
+  end
+
+  let :default_params do
+    {
+      :nexus_config          => nil
+    }
+  end
+
+  let :params do
+    {}
+  end
+
+  let :facts do
+    { :osfamily => 'Debian' }
+  end
+
+  context 'fail when missing nexus_config' do
+    it 'should fails to configure cisco nexus driver' do
+      expect { subject }.to raise_error(Puppet::Error, /No nexus config specified/)
+    end
+  end
+
+  context 'when using cisco' do
+    let (:nexus_config) do
+      { 'cvf2leaff2' => {'username' => 'prad',
+        "ssh_port" => 22,
+        "password" => "password",
+        "ip_address" => "172.18.117.28",
+        "servers" => {
+          "control02" => "portchannel:20",
+          "control01" => "portchannel:10"
+        }
+      }
+    }
+    end
+
+    before :each do
+      params.merge!(:nexus_config => nexus_config )
+    end
+
+    it 'installs ncclient package' do
+      should contain_package('python-ncclient').with(
+        :ensure => 'installed'
+      )
+    end
+
+    it 'configures /etc/default/neutron-server' do
+      should contain_file_line('/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG').with(
+        :line => 'NEUTRON_PLUGIN_CONFIG=/etc/neutron/plugins/ml2/ml2_conf_cisco.ini',
+        :require => ['Package[neutron-server]', 'Package[neutron-plugin-ml2]']
+      )
+    end
+  end
+
+end
diff --git a/templates/ml2_conf_cisco.ini.erb b/templates/ml2_conf_cisco.ini.erb
new file mode 100644
index 000000000..cb880d9d7
--- /dev/null
+++ b/templates/ml2_conf_cisco.ini.erb
@@ -0,0 +1,59 @@
+[ml2_cisco]
+
+# (StrOpt) A short prefix to prepend to the VLAN number when creating a
+# VLAN interface. For example, if an interface is being created for
+# VLAN 2001 it will be named 'q-2001' using the default prefix.
+#
+# vlan_name_prefix = q-
+# Example: vlan_name_prefix = vnet-
+
+# (BoolOpt) A flag to enable round robin scheduling of routers for SVI.
+# svi_round_robin = False
+
+#
+# (StrOpt) The name of the physical_network managed via the Cisco Nexus Switch.
+# This string value must be present in the ml2_conf.ini network_vlan_ranges
+# variable.
+#
+# managed_physical_network =
+# Example: managed_physical_network = physnet1
+
+# Cisco Nexus Switch configurations.
+# Each switch to be managed by Openstack Neutron must be configured here.
+#
+# Cisco Nexus Switch Format.
+# [ml2_mech_cisco_nexus:<IP address of switch>]
+# <hostname>=<intf_type:port>       (1)
+# ssh_port=<ssh port>               (2)
+# username=<credential username>    (3)
+# password=<credential password>    (4)
+#
+# (1) For each host connected to a port on the switch, specify the hostname
+#     and the Nexus physical port (interface) it is connected to.
+#     Valid intf_type's are 'ethernet' and 'port-channel'.
+#     The default setting for <intf_type:> is 'ethernet' and need not be
+#     added to this setting.
+# (2) The TCP port for connecting via SSH to manage the switch. This is
+#     port number 22 unless the switch has been configured otherwise.
+# (3) The username for logging into the switch to manage it.
+# (4) The password for logging into the switch to manage it.
+#
+# Example:
+# [ml2_mech_cisco_nexus:1.1.1.1]
+# compute1=1/1
+# compute2=ethernet:1/2
+# compute3=port-channel:1
+# ssh_port=22
+# username=admin
+# password=mySecretPassword
+
+<% nexus_config.each do |switch_hostname, switch_data| %>
+[ML2_MECH_CISCO_NEXUS:<%= switch_data['ip_address'] %>]
+<%- switch_data['servers'].each do|host_name, port| -%>
+<%=host_name-%>=<%= port %>
+<%- end -%>
+ssh_port=<%= switch_data['ssh_port'] %>
+username=<%= switch_data['username'] %>
+password=<%= switch_data['password'] %>
+<% end %>
+