summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/ml2/mech_driver_v2.py
blob: 6fc199b71473bca121e9693d93e9f05ad1f69202 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Copyright (c) 2013-2014 OpenStack Foundation
# All Rights Reserved.
#
#    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.
import copy

from oslo_config import cfg
from oslo_log import log as logging

from neutron.db import api as db_api
from neutron.plugins.ml2 import driver_api as api

from networking_odl.common import callback
from networking_odl.common import config as odl_conf
from networking_odl.db import db
from networking_odl.journal import journal
from networking_odl.ml2 import port_binding

LOG = logging.getLogger(__name__)


class OpenDaylightMechanismDriver(api.MechanismDriver):
    """OpenDaylight Python Driver for Neutron.

    This code is the backend implementation for the OpenDaylight ML2
    MechanismDriver for OpenStack Neutron.
    """

    def initialize(self):
        LOG.debug("Initializing OpenDaylight ML2 driver")
        cfg.CONF.register_opts(odl_conf.odl_opts, "ml2_odl")
        self.sg_handler = callback.OdlSecurityGroupsHandler(self)
        self.journal = journal.OpendaylightJournalThread()
        self.port_binding_controller = port_binding.PortBindingManager.create()

    def create_network_precommit(self, context):
        db.create_pending_row(context._plugin_context.session, 'network',
                              context.current['id'], 'create', context.current)

    def create_subnet_precommit(self, context):
        db.create_pending_row(context._plugin_context.session, 'subnet',
                              context.current['id'], 'create', context.current)

    def create_port_precommit(self, context):
        dbcontext = context._plugin_context
        groups = [context._plugin.get_security_group(dbcontext, sg)
                  for sg in context.current['security_groups']]
        new_context = copy.deepcopy(context.current)
        new_context['security_groups'] = groups
        # NOTE(yamahata): work around for port creation for router
        # tenant_id=''(empty string) is passed when port is created
        # by l3 plugin internally for router.
        # On the other hand, ODL doesn't accept empty string for tenant_id.
        # In that case, deduce tenant_id from network_id for now.
        # Right fix: modify Neutron so that don't allow empty string
        # for tenant_id even for port for internal use.
        # TODO(yamahata): eliminate this work around when neutron side
        # is fixed
        # assert port['tenant_id'] != ''
        if ('tenant_id' not in context.current or
                context.current['tenant_id'] == ''):
            tenant_id = context._network_context._network['tenant_id']
            new_context['tenant_id'] = tenant_id
        db.create_pending_row(context._plugin_context.session, 'port',
                              context.current['id'], 'create', new_context)

    def update_network_precommit(self, context):
        db.create_pending_row(context._plugin_context.session, 'network',
                              context.current['id'], 'update', context.current)

    def update_subnet_precommit(self, context):
        db.create_pending_row(context._plugin_context.session, 'subnet',
                              context.current['id'], 'update', context.current)

    def update_port_precommit(self, context):
        port = context._plugin.get_port(context._plugin_context,
                                        context.current['id'])
        dbcontext = context._plugin_context
        new_context = copy.deepcopy(context.current)
        groups = [context._plugin.get_security_group(dbcontext, sg)
                  for sg in port['security_groups']]
        new_context['security_groups'] = groups
        # Add the network_id in for validation
        new_context['network_id'] = port['network_id']
        # NOTE(yamahata): work around for port creation for router
        # tenant_id=''(empty string) is passed when port is created
        # by l3 plugin internally for router.
        # On the other hand, ODL doesn't accept empty string for tenant_id.
        # In that case, deduce tenant_id from network_id for now.
        # Right fix: modify Neutron so that don't allow empty string
        # for tenant_id even for port for internal use.
        # TODO(yamahata): eliminate this work around when neutron side
        # is fixed
        # assert port['tenant_id'] != ''
        if ('tenant_id' not in context.current or
                context.current['tenant_id'] == ''):
            port['tenant_id'] = context._network_context._network['tenant_id']
        db.create_pending_row(context._plugin_context.session, 'port',
                              context.current['id'], 'update', new_context)

    def delete_network_precommit(self, context):
        db.create_pending_row(context._plugin_context.session, 'network',
                              context.current['id'], 'delete', None)

    def delete_subnet_precommit(self, context):
        # Use the journal row's data field to store parent object
        # uuids. This information is required for validation checking
        # when deleting parent objects.
        new_context = [context.current['network_id']]
        db.create_pending_row(context._plugin_context.session, 'subnet',
                              context.current['id'], 'delete', new_context)

    def delete_port_precommit(self, context):
        # Use the journal row's data field to store parent object
        # uuids. This information is required for validation checking
        # when deleting parent objects.
        new_context = [context.current['network_id']]
        for subnet in context.current['fixed_ips']:
            new_context.append(subnet['subnet_id'])
        db.create_pending_row(context._plugin_context.session, 'port',
                              context.current['id'], 'delete', new_context)

    @journal.call_thread_on_end
    def sync_from_callback(self, operation, res_type_uri, res_id,
                           resource_dict):
        object_type = res_type_uri.replace('-', '_')[:-1]
        object_uuid = (resource_dict[object_type]['id']
                       if operation == 'create' else res_id)
        if resource_dict is not None:
            resource_dict = resource_dict[object_type]
        db.create_pending_row(db_api.get_session(), object_type, object_uuid,
                              operation, resource_dict)

    def _postcommit(self, context):
        self.journal.set_sync_event()

    create_network_postcommit = _postcommit
    create_subnet_postcommit = _postcommit
    create_port_postcommit = _postcommit
    update_network_postcommit = _postcommit
    update_subnet_postcommit = _postcommit
    update_port_postcommit = _postcommit
    delete_network_postcommit = _postcommit
    delete_subnet_postcommit = _postcommit
    delete_port_postcommit = _postcommit

    def bind_port(self, port_context):
        """Set binding for a valid segments

        """
        return self.port_binding_controller.bind_port(port_context)