Browse Source

Initial Commit

radhika 2 years ago
commit
1a0d760de9

BIN
cumulus-ml2/mech_driver/.driver.py.swp View File


+ 0
- 0
cumulus-ml2/mech_driver/__init__.py View File


+ 14
- 0
cumulus-ml2/mech_driver/config.py View File

@@ -0,0 +1,14 @@
1
+from oslo_config import cfg
2
+
3
+CUMULUS_DRIVER_OPTS = [
4
+    cfg.StrOpt('scheme',
5
+               default='http',
6
+               help='Scheme for base URL for the Altocumulus API'),
7
+    cfg.IntOpt('protocol_port',
8
+               default='8140',
9
+               help='Protocol port for base URL for the Altocumulus API'),
10
+    cfg.ListOpt('switches', default=[],
11
+                help=_('list of switch name/ip and remote switch port connected to this compute node'))
12
+]
13
+
14
+cfg.CONF.register_opts(CUMULUS_DRIVER_OPTS, 'ml2_cumulus')

+ 189
- 0
cumulus-ml2/mech_driver/driver.py View File

@@ -0,0 +1,189 @@
1
+import json
2
+
3
+from oslo_config import cfg
4
+from oslo_log import log as logging
5
+import requests
6
+from requests.exceptions import HTTPError
7
+
8
+from neutron.extensions import portbindings
9
+from neutron.i18n import _LE, _LI, _LW
10
+from neutron.plugins.ml2.common.exceptions import MechanismDriverError
11
+from neutron.plugins.ml2.driver_api import MechanismDriver
12
+
13
+from mech_driver.ml2 import config
14
+
15
+LOG = logging.getLogger(__name__)
16
+NETWORKS_URL = '{scheme}://{base}:{port}/ml2/v1/networks/{network}'
17
+HOSTS_URL = '{scheme}://{base}:{port}/ml2/v1/networks/{network}/hosts/{host}'
18
+VXLAN_URL = '{scheme}://{base}:{port}/ml2/v1/networks/{network}/vxlan/{vni}'
19
+
20
+"""
21
+
22
+list of switches is required to be configured. Add this config to the ml2_conf.ini config
23
+switche names or IPs must be separated using comma.
24
+
25
+[ml2_cumulus]
26
+switches="192.168.10.10,192.168.20.20"
27
+"""
28
+
29
+class CumulusMechanismDriver(MechanismDriver):
30
+    """
31
+    Mechanism driver for Cumulus Linux that manages connectivity between switches
32
+    and (compute) hosts using the Cumulus API
33
+    """
34
+    def initialize(self):
35
+        self.scheme = cfg.CONF.ml2_cumulus.scheme
36
+        self.protocol_port = cfg.CONF.ml2_cumulus.protocol_port
37
+        self.switches = cfg.CONF.ml2_cumulus.switches
38
+        if self.switches:
39
+            LOG.info(_LI('switches found in ml2_conf files %s'), self.switches)
40
+        else:
41
+            LOG.info(_LI('no switches in ml2_conf files'))
42
+
43
+    def bind_port(self, context):
44
+        if context.binding_levels:
45
+            return  # we've already got a top binding
46
+
47
+        # assign a dynamic vlan
48
+        next_segment = context.allocate_dynamic_segment(
49
+            {'id': context.network.current, 'network_type': 'vlan'}
50
+        )
51
+
52
+        context.continue_binding(
53
+            context.segments_to_bind[0]['id'],
54
+            [next_segment]
55
+        )
56
+
57
+    def delete_network_postcommit(self, context):
58
+        network_id = context.current['id']
59
+        vni = context.current['provider:segmentation_id']
60
+
61
+        # remove vxlan from all hosts - a little unpleasant
62
+        for _switch_ip in self.switches:
63
+            try:
64
+                actions = [
65
+                    VXLAN_URL.format(
66
+                        scheme=self.scheme,
67
+                        base=_switch_ip,
68
+                        port=self.protocol_port,
69
+                        vni=vni
70
+                    ),
71
+                    NETWORKS_URL.format(
72
+                        scheme=self.scheme,
73
+                        base=_switch_ip,
74
+                        port=self.protocol_port,
75
+                        network=network_id
76
+                    )
77
+                ]
78
+
79
+                for action in actions:
80
+                    r = requests.delete(action)
81
+
82
+                    if r.status_code != requests.codes.ok:
83
+                        LOG.info(
84
+                            _LI('Error during %s delete. HTTP Error:%s'),
85
+                            action, r.status_code
86
+                        )
87
+
88
+            except Exception, e:
89
+                # errors might be normal, but should improve this
90
+                LOG.info(_LI('Error during net delete. Error %s'), e)
91
+
92
+    def create_port_postcommit(self, context):
93
+        if context.segments_to_bind:
94
+            self._add_to_switch(context)
95
+
96
+    def update_port_postcommit(self, context):
97
+        if context.host != context.original_host:
98
+            self._remove_from_switch(context.original)
99
+        self._add_to_switch(context)
100
+
101
+    def delete_port_postcommit(self, context):
102
+        self._remove_from_switch(context)
103
+
104
+    def _add_to_switch(self, context):
105
+        if not hasattr(context, 'current'):
106
+            return
107
+        port = context.current
108
+        device_id = port['device_id']
109
+        device_owner = port['device_owner']
110
+        host = port[portbindings.HOST_ID]
111
+        network_id = port['network_id']
112
+        if not hasattr(context, 'top_bound_segment'):
113
+            return
114
+        if not context.top_bound_segment:
115
+            return
116
+        vni = context.top_bound_segment['segmentation_id']
117
+        vlan = context.bottom_bound_segment['segmentation_id']
118
+
119
+        if not (host and device_id and device_owner):
120
+            return
121
+
122
+
123
+        for _switch_ip in self.switches:
124
+            r = requests.put(
125
+                NETWORKS_URL.format(
126
+                    scheme=self.scheme,
127
+                    base=_switch_ip,
128
+                    port=self.protocol_port,
129
+                    network=network_id
130
+                ),
131
+                data=json.dumps({'vlan': vlan})
132
+            )
133
+
134
+            if r.status_code != requests.codes.ok:
135
+                raise MechanismDriverError()
136
+
137
+            actions = [
138
+                HOSTS_URL.format(
139
+                    scheme=self.scheme,
140
+                    base=_switch_ip,
141
+                    port=self.protocol_port,
142
+                    network=network_id,
143
+                    host=host
144
+                ),
145
+            ]
146
+            if context.top_bound_segment != context.bottom_bound_segment:
147
+                actions.append(
148
+                    VXLAN_URL.format(
149
+                        scheme=self.scheme,
150
+                        base=_switch_ip,
151
+                        port=self.protocol_port,
152
+                        network=network_id,
153
+                        vni=vni
154
+                    )
155
+                )
156
+
157
+
158
+            for action in actions:
159
+                r = requests.put(action)
160
+
161
+                if r.status_code != requests.codes.ok:
162
+                    raise MechanismDriverError()
163
+
164
+    def _remove_from_switch(self, context):
165
+        if not hasattr(context, 'current'):
166
+            return
167
+        port = context.current
168
+        host = port[portbindings.HOST_ID]
169
+        network_id = port['network_id']
170
+
171
+        for _switch_ip in self.switches:
172
+
173
+            r = requests.delete(
174
+                HOSTS_URL.format(
175
+                    scheme=self.scheme,
176
+                    base=_switch_ip,
177
+                    port=self.protocol_port,
178
+                    network=network_id,
179
+                    host=host
180
+                )
181
+            )
182
+
183
+            if r.status_code != requests.codes.ok:
184
+                LOG.info(
185
+                    _LI('error (%d) deleting port for %s on switch: %s'),
186
+                    r.status_code,
187
+                    host,
188
+                    _switch_ip
189
+                )

+ 63
- 0
cumulus-ml2/mech_driver/hpb_bridge_agent.py View File

@@ -0,0 +1,63 @@
1
+import sys
2
+
3
+from oslo_config import cfg
4
+from oslo_log import log as logging
5
+
6
+from neutron.common import config as common_config
7
+from neutron.common import utils as neutron_utils
8
+from neutron.i18n import _LE, _LI, _LW
9
+from neutron.plugins.linuxbridge.agent import linuxbridge_neutron_agent as lna
10
+
11
+from utils.discovery import DiscoveryManager
12
+from utils.misc import Shell
13
+
14
+DEFAULT_ROOT_HELPER = 'sudo'
15
+
16
+LOG = logging.getLogger(__name__)
17
+
18
+
19
+class HPBLinuxBridgeNeutronAgentRPC(lna.LinuxBridgeNeutronAgentRPC):
20
+    def __init__(self, interface_mappings, polling_interval):
21
+        super(HPBLinuxBridgeNeutronAgentRPC, self).__init__(
22
+            interface_mappings,
23
+            polling_interval
24
+        )
25
+
26
+        dm = DiscoveryManager(Shell(DEFAULT_ROOT_HELPER))
27
+
28
+        for physnet, interface in interface_mappings.iteritems():
29
+            neighbor = dm.find_neighbor_for_interface(interface)
30
+            if neighbor:
31
+                self.agent_state['configurations']['switch_name'] = \
32
+                    neighbor['name']
33
+                self.agent_state['configurations']['switch_mgmt_ip'] = \
34
+                    neighbor['mgmt-ip']
35
+                break
36
+        else:
37
+            LOG.error(
38
+                _LE('Unable to find %s neighbor for interface %s'),
39
+                physnet,
40
+                interface
41
+            )
42
+
43
+
44
+def main():
45
+    common_config.init(sys.argv[1:])
46
+
47
+    common_config.setup_logging()
48
+    try:
49
+        interface_mappings = neutron_utils.parse_mappings(
50
+            cfg.CONF.LINUX_BRIDGE.physical_interface_mappings)
51
+    except ValueError as e:
52
+        LOG.error(_LE("Parsing physical_interface_mappings failed: %s. "
53
+                      "Agent terminated!"), e)
54
+        sys.exit(1)
55
+    LOG.info(_LI("Interface mappings: %s"), interface_mappings)
56
+
57
+    polling_interval = cfg.CONF.AGENT.polling_interval
58
+    agent = HPBLinuxBridgeNeutronAgentRPC(interface_mappings,
59
+                                          polling_interval)
60
+    LOG.info(_LI("Agent initialized successfully, now running... "))
61
+    agent.daemon_loop()
62
+    sys.exit(0)
63
+

BIN
cumulus-ml2/utils/.discovery.py.swp View File


+ 54
- 0
cumulus-ml2/utils/discovery.py View File

@@ -0,0 +1,54 @@
1
+class DiscoveryError(RuntimeError):
2
+    pass
3
+
4
+class DiscoveryManager(object):
5
+    def __init__(self, shell):
6
+        self.shell = shell
7
+
8
+    def find_interface(self, hostname):
9
+        neighbors = self.fetch_neighbors()
10
+
11
+        for interface, details in neighbors.iteritems():
12
+            if details['chassis']['name'] == hostname:
13
+                return interface
14
+        # This causes a problem where neutron asks for serverX port
15
+        # but it doesn't exist. If in a multi-VM bringup, causes neutron
16
+        # to go nuts so for now just don't do it. If user actually forgot
17
+        # to enable LLDP on server port. Then, too bad. it never gets provisioned
18
+        # Better way to handle this problem will be investigated. For now, always
19
+        # remember to configure LLDP correctly. Suggest even have PTM running
20
+        # to ensure LLDP is configured correctly
21
+        # raise DiscoveryError('Could not find host: {}'.format(hostname))
22
+
23
+    def find_neighbor_for_interface(self, interface):
24
+        neighbors = self.fetch_neighbors()
25
+
26
+        try:
27
+            return neighbors[interface]['chassis']
28
+        except KeyError:
29
+            pass
30
+
31
+    def fetch_neighbors(self):
32
+        output = self.shell.call(['lldpcli', 'show', 'neighbors', '-f', 'keyvalue'])
33
+        parsed = parse_lldpd_output(output)
34
+
35
+        return parsed['lldp']
36
+
37
+def parse_lldpd_output(output):
38
+    """
39
+    http://stackoverflow.com/questions/20577303/parse-lldp-output-with-python
40
+    """
41
+    result = {}
42
+    entries = output.strip().split('\n')
43
+
44
+    for entry in entries:
45
+        path, value = entry.strip().split('=', 1)
46
+        path = path.split('.')
47
+        components, final = path[:-1], path[-1]
48
+
49
+        current = result
50
+        for component in components:
51
+            current = current.setdefault(component, {})
52
+        current[final] = value
53
+
54
+    return result

+ 13
- 0
cumulus-ml2/utils/misc.py View File

@@ -0,0 +1,13 @@
1
+from subprocess import check_output
2
+import yaml
3
+
4
+def load_config(path):
5
+    with open(path) as fp:
6
+        return yaml.load(fp)
7
+
8
+class Shell(object):
9
+    def __init__(self, root_helper):
10
+        self.root_helper = root_helper
11
+
12
+    def call(self, args):
13
+        return check_output([self.root_helper] + args)

+ 5
- 0
debian/changelog View File

@@ -0,0 +1,5 @@
1
+cumulus-ml2 (1.0.0-cl3u1) unstable; urgency=low
2
+
3
+  * Initial release
4
+
5
+ -- dev-support <dev-support@cumulusnetworks.com>  Wed, 22 June 2016 20:10:36 +0000

+ 1
- 0
debian/compat View File

@@ -0,0 +1 @@
1
+8

+ 17
- 0
debian/control View File

@@ -0,0 +1,17 @@
1
+Source: cumulus-ml2
2
+Section: python
3
+Priority: extra
4
+Maintainer: dev-support <dev-support@cumulusnetworks.com>
5
+Build-Depends: debhelper (>= 8.0.0), python-setuptools(>= 0.7), python-dev
6
+Standards-Version: 3.9.3
7
+Homepage: http://github.com/CumulusNetworks/altocumulus
8
+#Vcs-Git: git://git.debian.org/collab-maint/cumulus-ml2.git
9
+#Vcs-Browser: http://git.debian.org/?p=collab-maint/cumulus-ml2.git;a=summary
10
+
11
+Package: cumulus-ml2-driver
12
+Architecture: all
13
+Depends: ${python:Depends}, ${misc:Depends}
14
+Description: Mechanism ML2 Driver for Cumulus ML2 Plugin
15
+ This is installed on the network node where the Neutron server runs.
16
+ The configuration file located in /etc/neutron/plugins/ml2/ml2_cumulus.ini
17
+ has sample configs and short help section.

+ 3
- 0
debian/cumulus-ml2-driver.install View File

@@ -0,0 +1,3 @@
1
+debian/tmp/usr/lib/python2.7/dist-packages/cumulus-ml2/mech_driver/*.py
2
+debian/tmp/usr/lib/python2.7/dist-packages/cumulus-ml2/utils/*.py
3
+debian/tmp/usr/lib/python2.7/dist-packages/cumulus*.egg-info/*

+ 2
- 0
debian/docs View File

@@ -0,0 +1,2 @@
1
+README.md
2
+requirements.txt

+ 16
- 0
debian/rules View File

@@ -0,0 +1,16 @@
1
+#!/usr/bin/make -f
2
+
3
+PYTHONS:=$(shell pyversions -r)
4
+
5
+# -*- makefile -*-
6
+# Sample debian/rules that uses debhelper.
7
+# This file was originally written by Joey Hess and Craig Small.
8
+# As a special exception, when this file is copied by dh-make into a
9
+# dh-make output file, you may use that output file without restriction.
10
+# This special exception was added by Craig Small in version 0.37 of dh-make.
11
+
12
+# Uncomment this to turn on verbose mode.
13
+#export DH_VERBOSE=1
14
+
15
+%:
16
+	dh $@ --with python2

+ 1
- 0
debian/source/format View File

@@ -0,0 +1 @@
1
+3.0 (git)

+ 4
- 0
debian/source/options View File

@@ -0,0 +1,4 @@
1
+#extend-diff-ignore =
2
+#"(.*setuptools.*)|(.*.pot$)|(.*.pth$)|(.*/.eggs/.*)|(.*/pbr.*)|(AUTHORS)|(Changelog)"
3
+extend-diff-ignore = "(.*eggs.*)|(.*pbr.*)|(.*.cfg$)"
4
+

+ 12
- 0
etc/cumulus/config.yaml View File

@@ -0,0 +1,12 @@
1
+bind: 0.0.0.0
2
+port: 8140
3
+debug: False
4
+
5
+#local_bind: 10.40.10.122
6
+#service_node: 10.40.10.1
7
+
8
+# Add the list of inter switch links that
9
+# need to have the vlan included on it by default
10
+# Not needed if doing Hierarchical port binding
11
+#trunk_interfaces: uplink
12
+

+ 8
- 0
etc/neutron/plugins/ml2/ml2_cumulus.ini View File

@@ -0,0 +1,8 @@
1
+[ml2_cumulus]
2
+# Example attribute to set.
3
+# Make sure LLDP is work between the compute and switches since port
4
+# to add to trunk is automatically figured out
5
+# switches = leaf1,leaf2
6
+#---
7
+# switches = 192.168.11.1,192.168.12.1
8
+

+ 136
- 0
rpm/SPECS/cumulus.spec View File

@@ -0,0 +1,136 @@
1
+%define name cumulus-ml2
2
+%define version 1.0.0-cl3u1
3
+%define unmangled_version 1.0.0-cl3u1
4
+%define unmangled_version 1.0.0-cl3u1
5
+%define release 1
6
+
7
+Summary: UNKNOWN
8
+Name: %{name}
9
+Version: %{version}
10
+Release: %{release}
11
+Source0: %{name}-%{unmangled_version}.tar.gz
12
+License: UNKNOWN
13
+Group: Development/Libraries
14
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
15
+Prefix: %{_prefix}
16
+BuildArch: noarch
17
+Vendor: Cumulus Networks <dev-support@cumulusnetworks.com>
18
+
19
+%description
20
+# cumulus-ml2
21
+
22
+**IMPORTANT** This is in development and demo ready.
23
+
24
+Integrate your Cumulus Linux switch with OpenStack Neutron
25
+
26
+Manages VLAN bridges on the switch and L2 connectivity between (compute) hosts and the VLAN bridges. Uses LLDP to perform auto-discovery of hosts and the switchports they are connected to.
27
+
28
+Uses the same conventions as the Linux Bridge agent so that DHCP/L3 agents can theoretically be hosted on the switch.
29
+
30
+This branch includes changes conducted by Ceng to understand strengths and
31
+weakness of plugin for future development and improvement.
32
+
33
+## Usage
34
+
35
+There are two components involved in this project:
36
+
37
+* ML2 mechanism driver (runs on hosts with Neutron server)
38
+* HTTP API server (runs on switches)
39
+
40
+## Requirements
41
+  Openstack Kilo Release. Does not work with Juno or Older releases
42
+  LLDP must be active on all compute nodes and switches. Suggest enabling PTM a
43
+valid topology.dot file in place to verify LLDP status
44
+
45
+## Supported Topology
46
+  Singly attached server to a switch, with single or bond L2 links between
47
+switches.
48
+
49
+## Sample Cumulus Linux Base Configuration
50
+
51
+```
52
+leaf1 | success | rc=0 >>
53
+---------------------------
54
+auto swp32s0
55
+iface swp32s0
56
+        alias connection to server2
57
+auto bond0
58
+iface bond0
59
+        mstpctl-portnetwork yes
60
+        bond-miimon 100
61
+        bond-lacp-rate 1
62
+        bond-min-links 1
63
+        bond-slaves glob swp17-18
64
+        bond-mode 802.3ad
65
+        bond-xmit-hash-policy layer3+4
66
+        mstpctl-bpduguard no
67
+auto lo
68
+iface lo inet loopback
69
+auto eth0
70
+iface eth0 inet dhcp
71
+
72
+
73
+leaf2 | success | rc=0 >>
74
+------------------------
75
+auto swp32s0
76
+iface swp32s0
77
+        alias connection to server1
78
+auto bond0
79
+iface bond0
80
+        mstpctl-portnetwork yes
81
+        bond-miimon 100
82
+        bond-lacp-rate 1
83
+        bond-min-links 1
84
+        bond-slaves glob swp17-18
85
+        bond-mode 802.3ad
86
+        bond-xmit-hash-policy layer3+4
87
+        mstpctl-bpduguard no
88
+auto lo
89
+iface lo inet loopback
90
+auto eth0
91
+iface eth0 inet dhcp
92
+```
93
+
94
+## Installation
95
+
96
+### ML2 mechanism driver
97
+
98
+#### Redhat Openstack
99
+
100
+```bash
101
+# yum install git rpm-build
102
+# ps -ef | grep neutron-server # confirm neutron server is running on this otherwise find the right server
103
+#  git clone http://github.com/CumulusNetworks/altocumulus
104
+# cd altocumulus
105
+# python setup.py bdist_rpm
106
+# rpm -ivh dist/altocumulus-0.1.0.dev13-1.noarch.rpm
107
+
108
+```
109
+
110
+2. Add `cumulus` to the `mechanism_drivers` field in `/etc/neutron/plugins/ml2/ml2_conf.ini`
111
+3. _Append_ the sample ml2_cumulus_ini in this repo to  `/etc/neutron/plugins/ml2/ml2_conf.ini` on the network node.
112
+
113
+### HTTP API server
114
+
115
+A debian package can be build using information included in the debian directory
116
+
117
+## TODO
118
+
119
+* Authentication and Secure Communication (SSL)
120
+
121
+
122
+
123
+%prep
124
+%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version}
125
+
126
+%build
127
+python setup.py build
128
+
129
+%install
130
+python setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
131
+
132
+%clean
133
+rm -rf $RPM_BUILD_ROOT
134
+
135
+%files -f INSTALLED_FILES
136
+%defattr(-,root,root)

+ 25
- 0
setup.cfg View File

@@ -0,0 +1,25 @@
1
+[metadata]
2
+name = cumulus-ml2
3
+version = 0.1
4
+description-file =
5
+	README.md
6
+
7
+[files]
8
+packages =
9
+	cumulus-ml2
10
+data_files =
11
+  /etc/cumulus-ml2 = etc/cumulus-ml2/config.yaml
12
+
13
+[entry_points]
14
+neutron.ml2.mechanism_drivers =
15
+	cumulus = cumulus-ml2.mech-driver.driver:CumulusMechanismDriver
16
+
17
+[global]
18
+setup-hooks =
19
+	pbr.hooks.setup_hook
20
+
21
+[egg_info]
22
+tag_build =
23
+tag_date = 0
24
+tag_svn_revision = 0
25
+

+ 7
- 0
setup.py View File

@@ -0,0 +1,7 @@
1
+#!/usr/bin/env python
2
+import setuptools
3
+
4
+setuptools.setup(
5
+    setup_requires=['pbr'],
6
+    pbr=True,
7
+)

Loading…
Cancel
Save