summaryrefslogtreecommitdiffstats
path: root/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files
diff options
context:
space:
mode:
authorMichal Ptacek <michalx.ptacek@intel.com>2016-01-28 19:23:18 +0000
committerBilly O'Mahony <billy.o.mahony@intel.com>2016-01-29 21:17:58 +0000
commit5aa5bf29f814741f2aa62f51085c3ff1c9610ce8 (patch)
treea0ff91be428db0e4e7583b22c334d9498db03279 /fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files
parente525200dcf33b91f65790451f01aa92d455ebcea (diff)
Improve linux_net.py patch
Instead of hard coded file, sed parsing script introduced Change-Id: I1a8a9715c35b6a244fbd504cd0bd39600ccca726 Signed-off-by: Michal Ptacek <michalx.ptacek@intel.com> (cherry picked from commit 31694463288aebc06bfd5b4a75d934af4124dd0f)
Diffstat (limited to 'fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files')
-rw-r--r--fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.py1975
-rwxr-xr-xfuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.sh22
2 files changed, 22 insertions, 1975 deletions
diff --git a/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.py b/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.py
deleted file mode 100644
index 10ddff7..0000000
--- a/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.py
+++ /dev/null
@@ -1,1975 +0,0 @@
-# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# 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.
-
-"""Implements vlans, bridges, and iptables rules using linux utilities."""
-
-import calendar
-import inspect
-import os
-import re
-import time
-
-import netaddr
-import netifaces
-from oslo_concurrency import processutils
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_serialization import jsonutils
-from oslo_utils import excutils
-from oslo_utils import fileutils
-from oslo_utils import importutils
-from oslo_utils import timeutils
-import six
-
-from nova import exception
-from nova.i18n import _, _LE, _LW
-from nova import objects
-from nova import paths
-from nova.pci import utils as pci_utils
-from nova import utils
-
-LOG = logging.getLogger(__name__)
-
-
-linux_net_opts = [
- cfg.MultiStrOpt('dhcpbridge_flagfile',
- default=['/etc/nova/nova-dhcpbridge.conf'],
- help='Location of flagfiles for dhcpbridge'),
- cfg.StrOpt('networks_path',
- default=paths.state_path_def('networks'),
- help='Location to keep network config files'),
- cfg.StrOpt('public_interface',
- default='eth0',
- help='Interface for public IP addresses'),
- cfg.StrOpt('dhcpbridge',
- default=paths.bindir_def('nova-dhcpbridge'),
- help='Location of nova-dhcpbridge'),
- cfg.StrOpt('routing_source_ip',
- default='$my_ip',
- help='Public IP of network host'),
- cfg.IntOpt('dhcp_lease_time',
- default=86400,
- help='Lifetime of a DHCP lease in seconds'),
- cfg.MultiStrOpt('dns_server',
- default=[],
- help='If set, uses specific DNS server for dnsmasq. Can'
- ' be specified multiple times.'),
- cfg.BoolOpt('use_network_dns_servers',
- default=False,
- help='If set, uses the dns1 and dns2 from the network ref.'
- ' as dns servers.'),
- cfg.ListOpt('dmz_cidr',
- default=[],
- help='A list of dmz ranges that should be accepted'),
- cfg.MultiStrOpt('force_snat_range',
- default=[],
- help='Traffic to this range will always be snatted to the '
- 'fallback ip, even if it would normally be bridged out '
- 'of the node. Can be specified multiple times.'),
- cfg.StrOpt('dnsmasq_config_file',
- default='',
- help='Override the default dnsmasq settings with this file'),
- cfg.StrOpt('linuxnet_interface_driver',
- default='nova.network.linux_net.LinuxBridgeInterfaceDriver',
- help='Driver used to create ethernet devices.'),
- cfg.StrOpt('linuxnet_ovs_integration_bridge',
- default='br-int',
- help='Name of Open vSwitch bridge used with linuxnet'),
- cfg.BoolOpt('send_arp_for_ha',
- default=False,
- help='Send gratuitous ARPs for HA setup'),
- cfg.IntOpt('send_arp_for_ha_count',
- default=3,
- help='Send this many gratuitous ARPs for HA setup'),
- cfg.BoolOpt('use_single_default_gateway',
- default=False,
- help='Use single default gateway. Only first nic of vm will '
- 'get default gateway from dhcp server'),
- cfg.MultiStrOpt('forward_bridge_interface',
- default=['all'],
- help='An interface that bridges can forward to. If this '
- 'is set to all then all traffic will be forwarded. '
- 'Can be specified multiple times.'),
- cfg.StrOpt('metadata_host',
- default='$my_ip',
- help='The IP address for the metadata API server'),
- cfg.IntOpt('metadata_port',
- default=8775,
- min=1,
- max=65535,
- help='The port for the metadata API port'),
- cfg.StrOpt('iptables_top_regex',
- default='',
- help='Regular expression to match the iptables rule that '
- 'should always be on the top.'),
- cfg.StrOpt('iptables_bottom_regex',
- default='',
- help='Regular expression to match the iptables rule that '
- 'should always be on the bottom.'),
- cfg.StrOpt('iptables_drop_action',
- default='DROP',
- help='The table that iptables to jump to when a packet is '
- 'to be dropped.'),
- cfg.IntOpt('ovs_vsctl_timeout',
- default=120,
- help='Amount of time, in seconds, that ovs_vsctl should wait '
- 'for a response from the database. 0 is to wait forever.'),
- cfg.BoolOpt('fake_network',
- default=False,
- help='If passed, use fake network devices and addresses'),
- cfg.IntOpt('ebtables_exec_attempts',
- default=3,
- help='Number of times to retry ebtables commands on failure.'),
- cfg.FloatOpt('ebtables_retry_interval',
- default=1.0,
- help='Number of seconds to wait between ebtables retries.'),
- ]
-
-CONF = cfg.CONF
-CONF.register_opts(linux_net_opts)
-CONF.import_opt('host', 'nova.netconf')
-CONF.import_opt('use_ipv6', 'nova.netconf')
-CONF.import_opt('my_ip', 'nova.netconf')
-CONF.import_opt('network_device_mtu', 'nova.objects.network')
-
-
-# NOTE(vish): Iptables supports chain names of up to 28 characters, and we
-# add up to 12 characters to binary_name which is used as a prefix,
-# so we limit it to 16 characters.
-# (max_chain_name_length - len('-POSTROUTING') == 16)
-def get_binary_name():
- """Grab the name of the binary we're running in."""
- return os.path.basename(inspect.stack()[-1][1])[:16]
-
-binary_name = get_binary_name()
-
-
-class IptablesRule(object):
- """An iptables rule.
-
- You shouldn't need to use this class directly, it's only used by
- IptablesManager.
-
- """
-
- def __init__(self, chain, rule, wrap=True, top=False):
- self.chain = chain
- self.rule = rule
- self.wrap = wrap
- self.top = top
-
- def __eq__(self, other):
- return ((self.chain == other.chain) and
- (self.rule == other.rule) and
- (self.top == other.top) and
- (self.wrap == other.wrap))
-
- def __ne__(self, other):
- return not self == other
-
- def __repr__(self):
- if self.wrap:
- chain = '%s-%s' % (binary_name, self.chain)
- else:
- chain = self.chain
- # new rules should have a zero [packet: byte] count
- return '[0:0] -A %s %s' % (chain, self.rule)
-
-
-class IptablesTable(object):
- """An iptables table."""
-
- def __init__(self):
- self.rules = []
- self.remove_rules = []
- self.chains = set()
- self.unwrapped_chains = set()
- self.remove_chains = set()
- self.dirty = True
-
- def has_chain(self, name, wrap=True):
- if wrap:
- return name in self.chains
- else:
- return name in self.unwrapped_chains
-
- def add_chain(self, name, wrap=True):
- """Adds a named chain to the table.
-
- The chain name is wrapped to be unique for the component creating
- it, so different components of Nova can safely create identically
- named chains without interfering with one another.
-
- At the moment, its wrapped name is <binary name>-<chain name>,
- so if nova-compute creates a chain named 'OUTPUT', it'll actually
- end up named 'nova-compute-OUTPUT'.
-
- """
- if wrap:
- self.chains.add(name)
- else:
- self.unwrapped_chains.add(name)
- self.dirty = True
-
- def remove_chain(self, name, wrap=True):
- """Remove named chain.
-
- This removal "cascades". All rule in the chain are removed, as are
- all rules in other chains that jump to it.
-
- If the chain is not found, this is merely logged.
-
- """
- if wrap:
- chain_set = self.chains
- else:
- chain_set = self.unwrapped_chains
-
- if name not in chain_set:
- LOG.warning(_LW('Attempted to remove chain %s which does not '
- 'exist'), name)
- return
- self.dirty = True
-
- # non-wrapped chains and rules need to be dealt with specially,
- # so we keep a list of them to be iterated over in apply()
- if not wrap:
- self.remove_chains.add(name)
- chain_set.remove(name)
- if not wrap:
- self.remove_rules += filter(lambda r: r.chain == name, self.rules)
- self.rules = filter(lambda r: r.chain != name, self.rules)
-
- if wrap:
- jump_snippet = '-j %s-%s' % (binary_name, name)
- else:
- jump_snippet = '-j %s' % (name,)
-
- if not wrap:
- self.remove_rules += filter(lambda r: jump_snippet in r.rule,
- self.rules)
- self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules)
-
- def add_rule(self, chain, rule, wrap=True, top=False):
- """Add a rule to the table.
-
- This is just like what you'd feed to iptables, just without
- the '-A <chain name>' bit at the start.
-
- However, if you need to jump to one of your wrapped chains,
- prepend its name with a '$' which will ensure the wrapping
- is applied correctly.
-
- """
- if wrap and chain not in self.chains:
- raise ValueError(_('Unknown chain: %r') % chain)
-
- if '$' in rule:
- rule = ' '.join(map(self._wrap_target_chain, rule.split(' ')))
-
- rule_obj = IptablesRule(chain, rule, wrap, top)
- if rule_obj in self.rules:
- LOG.debug("Skipping duplicate iptables rule addition. "
- "%(rule)r already in %(rules)r",
- {'rule': rule_obj, 'rules': self.rules})
- else:
- self.rules.append(IptablesRule(chain, rule, wrap, top))
- self.dirty = True
-
- def _wrap_target_chain(self, s):
- if s.startswith('$'):
- return '%s-%s' % (binary_name, s[1:])
- return s
-
- def remove_rule(self, chain, rule, wrap=True, top=False):
- """Remove a rule from a chain.
-
- Note: The rule must be exactly identical to the one that was added.
- You cannot switch arguments around like you can with the iptables
- CLI tool.
-
- """
- try:
- self.rules.remove(IptablesRule(chain, rule, wrap, top))
- if not wrap:
- self.remove_rules.append(IptablesRule(chain, rule, wrap, top))
- self.dirty = True
- except ValueError:
- LOG.warning(_LW('Tried to remove rule that was not there:'
- ' %(chain)r %(rule)r %(wrap)r %(top)r'),
- {'chain': chain, 'rule': rule,
- 'top': top, 'wrap': wrap})
-
- def remove_rules_regex(self, regex):
- """Remove all rules matching regex."""
- if isinstance(regex, six.string_types):
- regex = re.compile(regex)
- num_rules = len(self.rules)
- self.rules = filter(lambda r: not regex.match(str(r)), self.rules)
- removed = num_rules - len(self.rules)
- if removed > 0:
- self.dirty = True
- return removed
-
- def empty_chain(self, chain, wrap=True):
- """Remove all rules from a chain."""
- chained_rules = [rule for rule in self.rules
- if rule.chain == chain and rule.wrap == wrap]
- if chained_rules:
- self.dirty = True
- for rule in chained_rules:
- self.rules.remove(rule)
-
-
-class IptablesManager(object):
- """Wrapper for iptables.
-
- See IptablesTable for some usage docs
-
- A number of chains are set up to begin with.
-
- First, nova-filter-top. It's added at the top of FORWARD and OUTPUT. Its
- name is not wrapped, so it's shared between the various nova workers. It's
- intended for rules that need to live at the top of the FORWARD and OUTPUT
- chains. It's in both the ipv4 and ipv6 set of tables.
-
- For ipv4 and ipv6, the built-in INPUT, OUTPUT, and FORWARD filter chains
- are wrapped, meaning that the "real" INPUT chain has a rule that jumps to
- the wrapped INPUT chain, etc. Additionally, there's a wrapped chain named
- "local" which is jumped to from nova-filter-top.
-
- For ipv4, the built-in PREROUTING, OUTPUT, and POSTROUTING nat chains are
- wrapped in the same was as the built-in filter chains. Additionally,
- there's a snat chain that is applied after the POSTROUTING chain.
-
- """
-
- def __init__(self, execute=None):
- if not execute:
- self.execute = _execute
- else:
- self.execute = execute
-
- self.ipv4 = {'filter': IptablesTable(),
- 'nat': IptablesTable(),
- 'mangle': IptablesTable()}
- self.ipv6 = {'filter': IptablesTable()}
-
- self.iptables_apply_deferred = False
-
- # Add a nova-filter-top chain. It's intended to be shared
- # among the various nova components. It sits at the very top
- # of FORWARD and OUTPUT.
- for tables in [self.ipv4, self.ipv6]:
- tables['filter'].add_chain('nova-filter-top', wrap=False)
- tables['filter'].add_rule('FORWARD', '-j nova-filter-top',
- wrap=False, top=True)
- tables['filter'].add_rule('OUTPUT', '-j nova-filter-top',
- wrap=False, top=True)
-
- tables['filter'].add_chain('local')
- tables['filter'].add_rule('nova-filter-top', '-j $local',
- wrap=False)
-
- # Wrap the built-in chains
- builtin_chains = {4: {'filter': ['INPUT', 'OUTPUT', 'FORWARD'],
- 'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING'],
- 'mangle': ['POSTROUTING']},
- 6: {'filter': ['INPUT', 'OUTPUT', 'FORWARD']}}
-
- for ip_version in builtin_chains:
- if ip_version == 4:
- tables = self.ipv4
- elif ip_version == 6:
- tables = self.ipv6
-
- for table, chains in six.iteritems(builtin_chains[ip_version]):
- for chain in chains:
- tables[table].add_chain(chain)
- tables[table].add_rule(chain, '-j $%s' % (chain,),
- wrap=False)
-
- # Add a nova-postrouting-bottom chain. It's intended to be shared
- # among the various nova components. We set it as the last chain
- # of POSTROUTING chain.
- self.ipv4['nat'].add_chain('nova-postrouting-bottom', wrap=False)
- self.ipv4['nat'].add_rule('POSTROUTING', '-j nova-postrouting-bottom',
- wrap=False)
-
- # We add a snat chain to the shared nova-postrouting-bottom chain
- # so that it's applied last.
- self.ipv4['nat'].add_chain('snat')
- self.ipv4['nat'].add_rule('nova-postrouting-bottom', '-j $snat',
- wrap=False)
-
- # And then we add a float-snat chain and jump to first thing in
- # the snat chain.
- self.ipv4['nat'].add_chain('float-snat')
- self.ipv4['nat'].add_rule('snat', '-j $float-snat')
-
- def defer_apply_on(self):
- self.iptables_apply_deferred = True
-
- def defer_apply_off(self):
- self.iptables_apply_deferred = False
- self.apply()
-
- def dirty(self):
- for table in six.itervalues(self.ipv4):
- if table.dirty:
- return True
- if CONF.use_ipv6:
- for table in six.itervalues(self.ipv6):
- if table.dirty:
- return True
- return False
-
- def apply(self):
- if self.iptables_apply_deferred:
- return
- if self.dirty():
- self._apply()
- else:
- LOG.debug("Skipping apply due to lack of new rules")
-
- @utils.synchronized('iptables', external=True)
- def _apply(self):
- """Apply the current in-memory set of iptables rules.
-
- This will blow away any rules left over from previous runs of the
- same component of Nova, and replace them with our current set of
- rules. This happens atomically, thanks to iptables-restore.
-
- """
- s = [('iptables', self.ipv4)]
- if CONF.use_ipv6:
- s += [('ip6tables', self.ipv6)]
-
- for cmd, tables in s:
- all_tables, _err = self.execute('%s-save' % (cmd,), '-c',
- run_as_root=True,
- attempts=5)
- all_lines = all_tables.split('\n')
- for table_name, table in six.iteritems(tables):
- start, end = self._find_table(all_lines, table_name)
- all_lines[start:end] = self._modify_rules(
- all_lines[start:end], table, table_name)
- table.dirty = False
- self.execute('%s-restore' % (cmd,), '-c', run_as_root=True,
- process_input='\n'.join(all_lines),
- attempts=5)
- LOG.debug("IPTablesManager.apply completed with success")
-
- def _find_table(self, lines, table_name):
- if len(lines) < 3:
- # length only <2 when fake iptables
- return (0, 0)
- try:
- start = lines.index('*%s' % table_name) - 1
- except ValueError:
- # Couldn't find table_name
- return (0, 0)
- end = lines[start:].index('COMMIT') + start + 2
- return (start, end)
-
- def _modify_rules(self, current_lines, table, table_name):
- unwrapped_chains = table.unwrapped_chains
- chains = sorted(table.chains)
- remove_chains = table.remove_chains
- rules = table.rules
- remove_rules = table.remove_rules
-
- if not current_lines:
- fake_table = ['#Generated by nova',
- '*' + table_name, 'COMMIT',
- '#Completed by nova']
- current_lines = fake_table
-
- # Remove any trace of our rules
- new_filter = filter(lambda line: binary_name not in line,
- current_lines)
-
- top_rules = []
- bottom_rules = []
-
- if CONF.iptables_top_regex:
- regex = re.compile(CONF.iptables_top_regex)
- temp_filter = filter(lambda line: regex.search(line), new_filter)
- for rule_str in temp_filter:
- new_filter = filter(lambda s: s.strip() != rule_str.strip(),
- new_filter)
- top_rules = temp_filter
-
- if CONF.iptables_bottom_regex:
- regex = re.compile(CONF.iptables_bottom_regex)
- temp_filter = filter(lambda line: regex.search(line), new_filter)
- for rule_str in temp_filter:
- new_filter = filter(lambda s: s.strip() != rule_str.strip(),
- new_filter)
- bottom_rules = temp_filter
-
- seen_chains = False
- rules_index = 0
- for rules_index, rule in enumerate(new_filter):
- if not seen_chains:
- if rule.startswith(':'):
- seen_chains = True
- else:
- if not rule.startswith(':'):
- break
-
- if not seen_chains:
- rules_index = 2
-
- our_rules = top_rules
- bot_rules = []
- for rule in rules:
- rule_str = str(rule)
- if rule.top:
- # rule.top == True means we want this rule to be at the top.
- # Further down, we weed out duplicates from the bottom of the
- # list, so here we remove the dupes ahead of time.
-
- # We don't want to remove an entry if it has non-zero
- # [packet:byte] counts and replace it with [0:0], so let's
- # go look for a duplicate, and over-ride our table rule if
- # found.
-
- # ignore [packet:byte] counts at beginning of line
- if rule_str.startswith('['):
- rule_str = rule_str.split(']', 1)[1]
- dup_filter = filter(lambda s: rule_str.strip() in s.strip(),
- new_filter)
-
- new_filter = filter(lambda s:
- rule_str.strip() not in s.strip(),
- new_filter)
- # if no duplicates, use original rule
- if dup_filter:
- # grab the last entry, if there is one
- dup = dup_filter[-1]
- rule_str = str(dup)
- else:
- rule_str = str(rule)
- rule_str.strip()
-
- our_rules += [rule_str]
- else:
- bot_rules += [rule_str]
-
- our_rules += bot_rules
-
- new_filter[rules_index:rules_index] = our_rules
-
- new_filter[rules_index:rules_index] = [':%s - [0:0]' % (name,)
- for name in unwrapped_chains]
- new_filter[rules_index:rules_index] = [':%s-%s - [0:0]' %
- (binary_name, name,)
- for name in chains]
-
- commit_index = new_filter.index('COMMIT')
- new_filter[commit_index:commit_index] = bottom_rules
- seen_lines = set()
-
- def _weed_out_duplicates(line):
- # ignore [packet:byte] counts at beginning of lines
- if line.startswith('['):
- line = line.split(']', 1)[1]
- line = line.strip()
- if line in seen_lines:
- return False
- else:
- seen_lines.add(line)
- return True
-
- def _weed_out_removes(line):
- # We need to find exact matches here
- if line.startswith(':'):
- # it's a chain, for example, ":nova-billing - [0:0]"
- # strip off everything except the chain name
- line = line.split(':')[1]
- line = line.split('- [')[0]
- line = line.strip()
- for chain in remove_chains:
- if chain == line:
- remove_chains.remove(chain)
- return False
- elif line.startswith('['):
- # it's a rule
- # ignore [packet:byte] counts at beginning of lines
- line = line.split(']', 1)[1]
- line = line.strip()
- for rule in remove_rules:
- # ignore [packet:byte] counts at beginning of rules
- rule_str = str(rule)
- rule_str = rule_str.split(' ', 1)[1]
- rule_str = rule_str.strip()
- if rule_str == line:
- remove_rules.remove(rule)
- return False
-
- # Leave it alone
- return True
-
- # We filter duplicates, letting the *last* occurrence take
- # precedence. We also filter out anything in the "remove"
- # lists.
- new_filter.reverse()
- new_filter = filter(_weed_out_duplicates, new_filter)
- new_filter = filter(_weed_out_removes, new_filter)
- new_filter.reverse()
-
- # flush lists, just in case we didn't find something
- remove_chains.clear()
- for rule in remove_rules:
- remove_rules.remove(rule)
-
- return new_filter
-
-
-# NOTE(jkoelker) This is just a nice little stub point since mocking
-# builtins with mox is a nightmare
-def write_to_file(file, data, mode='w'):
- with open(file, mode) as f:
- f.write(data)
-
-
-def is_pid_cmdline_correct(pid, match):
- """Ensure that the cmdline for a pid seems sane
-
- Because pids are recycled, blindly killing by pid is something to
- avoid. This provides the ability to include a substring that is
- expected in the cmdline as a safety check.
- """
- try:
- with open('/proc/%d/cmdline' % pid) as f:
- cmdline = f.read()
- return match in cmdline
- except EnvironmentError:
- return False
-
-
-def metadata_forward():
- """Create forwarding rule for metadata."""
- if CONF.metadata_host != '127.0.0.1':
- iptables_manager.ipv4['nat'].add_rule('PREROUTING',
- '-s 0.0.0.0/0 -d 169.254.169.254/32 '
- '-p tcp -m tcp --dport 80 -j DNAT '
- '--to-destination %s:%s' %
- (CONF.metadata_host,
- CONF.metadata_port))
- else:
- iptables_manager.ipv4['nat'].add_rule('PREROUTING',
- '-s 0.0.0.0/0 -d 169.254.169.254/32 '
- '-p tcp -m tcp --dport 80 '
- '-j REDIRECT --to-ports %s' %
- CONF.metadata_port)
- iptables_manager.apply()
-
-
-def _iptables_dest(ip):
- if ((netaddr.IPAddress(ip).version == 4 and ip == '127.0.0.1')
- or ip == '::1'):
- return '-m addrtype --dst-type LOCAL'
- else:
- return '-d %s' % ip
-
-
-def metadata_accept():
- """Create the filter accept rule for metadata."""
-
- rule = ('-p tcp -m tcp --dport %s %s -j ACCEPT' %
- (CONF.metadata_port, _iptables_dest(CONF.metadata_host)))
-
- if netaddr.IPAddress(CONF.metadata_host).version == 4:
- iptables_manager.ipv4['filter'].add_rule('INPUT', rule)
- else:
- iptables_manager.ipv6['filter'].add_rule('INPUT', rule)
-
- iptables_manager.apply()
-
-
-def add_snat_rule(ip_range, is_external=False):
- if CONF.routing_source_ip:
- if is_external:
- if CONF.force_snat_range:
- snat_range = CONF.force_snat_range
- else:
- snat_range = []
- else:
- snat_range = ['0.0.0.0/0']
- for dest_range in snat_range:
- rule = ('-s %s -d %s -j SNAT --to-source %s'
- % (ip_range, dest_range, CONF.routing_source_ip))
- if not is_external and CONF.public_interface:
- rule += ' -o %s' % CONF.public_interface
- iptables_manager.ipv4['nat'].add_rule('snat', rule)
- iptables_manager.apply()
-
-
-def init_host(ip_range, is_external=False):
- """Basic networking setup goes here."""
- # NOTE(devcamcar): Cloud public SNAT entries and the default
- # SNAT rule for outbound traffic.
-
- add_snat_rule(ip_range, is_external)
-
- rules = []
- if is_external:
- for snat_range in CONF.force_snat_range:
- rules.append('PREROUTING -p ipv4 --ip-src %s --ip-dst %s '
- '-j redirect --redirect-target ACCEPT' %
- (ip_range, snat_range))
- if rules:
- ensure_ebtables_rules(rules, 'nat')
-
- iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
- '-s %s -d %s/32 -j ACCEPT' %
- (ip_range, CONF.metadata_host))
-
- for dmz in CONF.dmz_cidr:
- iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
- '-s %s -d %s -j ACCEPT' %
- (ip_range, dmz))
-
- iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
- '-s %(range)s -d %(range)s '
- '-m conntrack ! --ctstate DNAT '
- '-j ACCEPT' %
- {'range': ip_range})
- iptables_manager.apply()
-
-
-def send_arp_for_ip(ip, device, count):
- out, err = _execute('arping', '-U', ip,
- '-A', '-I', device,
- '-c', str(count),
- run_as_root=True, check_exit_code=False)
-
- if err:
- LOG.debug('arping error for ip %s', ip)
-
-
-def bind_floating_ip(floating_ip, device):
- """Bind ip to public interface."""
- _execute('ip', 'addr', 'add', str(floating_ip) + '/32',
- 'dev', device,
- run_as_root=True, check_exit_code=[0, 2, 254])
-
- if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0:
- send_arp_for_ip(floating_ip, device, CONF.send_arp_for_ha_count)
-
-
-def unbind_floating_ip(floating_ip, device):
- """Unbind a public ip from public interface."""
- _execute('ip', 'addr', 'del', str(floating_ip) + '/32',
- 'dev', device,
- run_as_root=True, check_exit_code=[0, 2, 254])
-
-
-def ensure_metadata_ip():
- """Sets up local metadata ip."""
- _execute('ip', 'addr', 'add', '169.254.169.254/32',
- 'scope', 'link', 'dev', 'lo',
- run_as_root=True, check_exit_code=[0, 2, 254])
-
-
-def ensure_vpn_forward(public_ip, port, private_ip):
- """Sets up forwarding rules for vlan."""
- iptables_manager.ipv4['filter'].add_rule('FORWARD',
- '-d %s -p udp '
- '--dport 1194 '
- '-j ACCEPT' % private_ip)
- iptables_manager.ipv4['nat'].add_rule('PREROUTING',
- '-d %s -p udp '
- '--dport %s -j DNAT --to %s:1194' %
- (public_ip, port, private_ip))
- iptables_manager.ipv4['nat'].add_rule('OUTPUT',
- '-d %s -p udp '
- '--dport %s -j DNAT --to %s:1194' %
- (public_ip, port, private_ip))
- iptables_manager.apply()
-
-
-def ensure_floating_forward(floating_ip, fixed_ip, device, network):
- """Ensure floating ip forwarding rule."""
- # NOTE(vish): Make sure we never have duplicate rules for the same ip
- regex = '.*\s+%s(/32|\s+|$)' % floating_ip
- num_rules = iptables_manager.ipv4['nat'].remove_rules_regex(regex)
- if num_rules:
- msg = _LW('Removed %(num)d duplicate rules for floating ip %(float)s')
- LOG.warn(msg, {'num': num_rules, 'float': floating_ip})
- for chain, rule in floating_forward_rules(floating_ip, fixed_ip, device):
- iptables_manager.ipv4['nat'].add_rule(chain, rule)
- iptables_manager.apply()
- if device != network['bridge']:
- ensure_ebtables_rules(*floating_ebtables_rules(fixed_ip, network))
-
-
-def remove_floating_forward(floating_ip, fixed_ip, device, network):
- """Remove forwarding for floating ip."""
- for chain, rule in floating_forward_rules(floating_ip, fixed_ip, device):
- iptables_manager.ipv4['nat'].remove_rule(chain, rule)
- iptables_manager.apply()
- if device != network['bridge']:
- remove_ebtables_rules(*floating_ebtables_rules(fixed_ip, network))
-
-
-def floating_ebtables_rules(fixed_ip, network):
- """Makes sure only in-network traffic is bridged."""
- return (['PREROUTING --logical-in %s -p ipv4 --ip-src %s '
- '! --ip-dst %s -j redirect --redirect-target ACCEPT' %
- (network['bridge'], fixed_ip, network['cidr'])], 'nat')
-
-
-def floating_forward_rules(floating_ip, fixed_ip, device):
- rules = []
- rule = '-s %s -j SNAT --to %s' % (fixed_ip, floating_ip)
- if device:
- rules.append(('float-snat', rule + ' -d %s' % fixed_ip))
- rules.append(('float-snat', rule + ' -o %s' % device))
- else:
- rules.append(('float-snat', rule))
- rules.append(
- ('PREROUTING', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)))
- rules.append(
- ('OUTPUT', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)))
- rules.append(('POSTROUTING', '-s %s -m conntrack --ctstate DNAT -j SNAT '
- '--to-source %s' %
- (fixed_ip, floating_ip)))
- return rules
-
-
-def clean_conntrack(fixed_ip):
- try:
- _execute('conntrack', '-D', '-r', fixed_ip, run_as_root=True,
- check_exit_code=[0, 1])
- except processutils.ProcessExecutionError:
- LOG.exception(_LE('Error deleting conntrack entries for %s'), fixed_ip)
-
-
-def _enable_ipv4_forwarding():
- sysctl_key = 'net.ipv4.ip_forward'
- stdout, stderr = _execute('sysctl', '-n', sysctl_key)
- if stdout.strip() is not '1':
- _execute('sysctl', '-w', '%s=1' % sysctl_key, run_as_root=True)
-
-
-@utils.synchronized('lock_gateway', external=True)
-def initialize_gateway_device(dev, network_ref):
- if not network_ref:
- return
-
- _enable_ipv4_forwarding()
-
- # NOTE(vish): The ip for dnsmasq has to be the first address on the
- # bridge for it to respond to requests properly
- try:
- prefix = network_ref.cidr.prefixlen
- except AttributeError:
- prefix = network_ref['cidr'].rpartition('/')[2]
-
- full_ip = '%s/%s' % (network_ref['dhcp_server'], prefix)
- new_ip_params = [[full_ip, 'brd', network_ref['broadcast']]]
- old_ip_params = []
- out, err = _execute('ip', 'addr', 'show', 'dev', dev,
- 'scope', 'global')
- for line in out.split('\n'):
- fields = line.split()
- if fields and fields[0] == 'inet':
- if fields[-2] in ('secondary', 'dynamic'):
- ip_params = fields[1:-2]
- else:
- ip_params = fields[1:-1]
- old_ip_params.append(ip_params)
- if ip_params[0] != full_ip:
- new_ip_params.append(ip_params)
- if not old_ip_params or old_ip_params[0][0] != full_ip:
- old_routes = []
- result = _execute('ip', 'route', 'show', 'dev', dev)
- if result:
- out, err = result
- for line in out.split('\n'):
- fields = line.split()
- if fields and 'via' in fields:
- old_routes.append(fields)
- _execute('ip', 'route', 'del', fields[0],
- 'dev', dev, run_as_root=True)
- for ip_params in old_ip_params:
- _execute(*_ip_bridge_cmd('del', ip_params, dev),
- run_as_root=True, check_exit_code=[0, 2, 254])
- for ip_params in new_ip_params:
- _execute(*_ip_bridge_cmd('add', ip_params, dev),
- run_as_root=True, check_exit_code=[0, 2, 254])
-
- for fields in old_routes:
- _execute('ip', 'route', 'add', *fields,
- run_as_root=True)
- if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0:
- send_arp_for_ip(network_ref['dhcp_server'], dev,
- CONF.send_arp_for_ha_count)
- if CONF.use_ipv6:
- _execute('ip', '-f', 'inet6', 'addr',
- 'change', network_ref['cidr_v6'],
- 'dev', dev, run_as_root=True)
-
-
-def get_dhcp_leases(context, network_ref):
- """Return a network's hosts config in dnsmasq leasefile format."""
- hosts = []
- host = None
- if network_ref['multi_host']:
- host = CONF.host
- for fixedip in objects.FixedIPList.get_by_network(context,
- network_ref,
- host=host):
- # NOTE(cfb): Don't return a lease entry if the IP isn't
- # already leased
- if fixedip.leased:
- hosts.append(_host_lease(fixedip))
-
- return '\n'.join(hosts)
-
-
-def get_dhcp_hosts(context, network_ref, fixedips):
- """Get network's hosts config in dhcp-host format."""
- hosts = []
- macs = set()
- for fixedip in fixedips:
- if fixedip.allocated:
- if fixedip.virtual_interface.address not in macs:
- hosts.append(_host_dhcp(fixedip))
- macs.add(fixedip.virtual_interface.address)
- return '\n'.join(hosts)
-
-
-def get_dns_hosts(context, network_ref):
- """Get network's DNS hosts in hosts format."""
- hosts = []
- for fixedip in objects.FixedIPList.get_by_network(context, network_ref):
- if fixedip.allocated:
- hosts.append(_host_dns(fixedip))
- return '\n'.join(hosts)
-
-
-def _add_dnsmasq_accept_rules(dev):
- """Allow DHCP and DNS traffic through to dnsmasq."""
- table = iptables_manager.ipv4['filter']
- for port in [67, 53]:
- for proto in ['udp', 'tcp']:
- args = {'dev': dev, 'port': port, 'proto': proto}
- table.add_rule('INPUT',
- '-i %(dev)s -p %(proto)s -m %(proto)s '
- '--dport %(port)s -j ACCEPT' % args)
- iptables_manager.apply()
-
-
-def _remove_dnsmasq_accept_rules(dev):
- """Remove DHCP and DNS traffic allowed through to dnsmasq."""
- table = iptables_manager.ipv4['filter']
- for port in [67, 53]:
- for proto in ['udp', 'tcp']:
- args = {'dev': dev, 'port': port, 'proto': proto}
- table.remove_rule('INPUT',
- '-i %(dev)s -p %(proto)s -m %(proto)s '
- '--dport %(port)s -j ACCEPT' % args)
- iptables_manager.apply()
-
-
-# NOTE(russellb) Curious why this is needed? Check out this explanation from
-# markmc: https://bugzilla.redhat.com/show_bug.cgi?id=910619#c6
-def _add_dhcp_mangle_rule(dev):
- table = iptables_manager.ipv4['mangle']
- table.add_rule('POSTROUTING',
- '-o %s -p udp -m udp --dport 68 -j CHECKSUM '
- '--checksum-fill' % dev)
- iptables_manager.apply()
-
-
-def _remove_dhcp_mangle_rule(dev):
- table = iptables_manager.ipv4['mangle']
- table.remove_rule('POSTROUTING',
- '-o %s -p udp -m udp --dport 68 -j CHECKSUM '
- '--checksum-fill' % dev)
- iptables_manager.apply()
-
-
-def get_dhcp_opts(context, network_ref, fixedips):
- """Get network's hosts config in dhcp-opts format."""
- gateway = network_ref['gateway']
- # NOTE(vish): if we are in multi-host mode and we are not sharing
- # addresses, then we actually need to hand out the
- # dhcp server address as the gateway.
- if network_ref['multi_host'] and not (network_ref['share_address'] or
- CONF.share_dhcp_address):
- gateway = network_ref['dhcp_server']
- hosts = []
- if CONF.use_single_default_gateway:
- for fixedip in fixedips:
- if fixedip.allocated:
- vif_id = fixedip.virtual_interface_id
- if fixedip.default_route:
- hosts.append(_host_dhcp_opts(vif_id, gateway))
- else:
- hosts.append(_host_dhcp_opts(vif_id))
- else:
- hosts.append(_host_dhcp_opts(None, gateway))
- return '\n'.join(hosts)
-
-
-def release_dhcp(dev, address, mac_address):
- if device_exists(dev):
- try:
- utils.execute('dhcp_release', dev, address, mac_address,
- run_as_root=True)
- except processutils.ProcessExecutionError:
- raise exception.NetworkDhcpReleaseFailed(address=address,
- mac_address=mac_address)
-
-
-def update_dhcp(context, dev, network_ref):
- conffile = _dhcp_file(dev, 'conf')
- host = None
- if network_ref['multi_host']:
- host = CONF.host
- fixedips = objects.FixedIPList.get_by_network(context,
- network_ref,
- host=host)
- write_to_file(conffile, get_dhcp_hosts(context, network_ref, fixedips))
- restart_dhcp(context, dev, network_ref, fixedips)
-
-
-def update_dns(context, dev, network_ref):
- hostsfile = _dhcp_file(dev, 'hosts')
- host = None
- if network_ref['multi_host']:
- host = CONF.host
- fixedips = objects.FixedIPList.get_by_network(context,
- network_ref,
- host=host)
- write_to_file(hostsfile, get_dns_hosts(context, network_ref))
- restart_dhcp(context, dev, network_ref, fixedips)
-
-
-def update_dhcp_hostfile_with_text(dev, hosts_text):
- conffile = _dhcp_file(dev, 'conf')
- write_to_file(conffile, hosts_text)
-
-
-def kill_dhcp(dev):
- pid = _dnsmasq_pid_for(dev)
- if pid:
- # Check that the process exists and looks like a dnsmasq process
- conffile = _dhcp_file(dev, 'conf')
- if is_pid_cmdline_correct(pid, conffile.split('/')[-1]):
- _execute('kill', '-9', pid, run_as_root=True)
- else:
- LOG.debug('Pid %d is stale, skip killing dnsmasq', pid)
- _remove_dnsmasq_accept_rules(dev)
- _remove_dhcp_mangle_rule(dev)
-
-
-# NOTE(ja): Sending a HUP only reloads the hostfile, so any
-# configuration options (like dchp-range, vlan, ...)
-# aren't reloaded.
-@utils.synchronized('dnsmasq_start')
-def restart_dhcp(context, dev, network_ref, fixedips):
- """(Re)starts a dnsmasq server for a given network.
-
- If a dnsmasq instance is already running then send a HUP
- signal causing it to reload, otherwise spawn a new instance.
-
- """
- conffile = _dhcp_file(dev, 'conf')
-
- optsfile = _dhcp_file(dev, 'opts')
- write_to_file(optsfile, get_dhcp_opts(context, network_ref, fixedips))
- os.chmod(optsfile, 0o644)
-
- _add_dhcp_mangle_rule(dev)
-
- # Make sure dnsmasq can actually read it (it setuid()s to "nobody")
- os.chmod(conffile, 0o644)
-
- pid = _dnsmasq_pid_for(dev)
-
- # if dnsmasq is already running, then tell it to reload
- if pid:
- if is_pid_cmdline_correct(pid, conffile.split('/')[-1]):
- try:
- _execute('kill', '-HUP', pid, run_as_root=True)
- _add_dnsmasq_accept_rules(dev)
- return
- except Exception as exc:
- LOG.error(_LE('kill -HUP dnsmasq threw %s'), exc)
- else:
- LOG.debug('Pid %d is stale, relaunching dnsmasq', pid)
-
- cmd = ['env',
- 'CONFIG_FILE=%s' % jsonutils.dumps(CONF.dhcpbridge_flagfile),
- 'NETWORK_ID=%s' % str(network_ref['id']),
- 'dnsmasq',
- '--strict-order',
- '--bind-interfaces',
- '--conf-file=%s' % CONF.dnsmasq_config_file,
- '--pid-file=%s' % _dhcp_file(dev, 'pid'),
- '--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts'),
- '--listen-address=%s' % network_ref['dhcp_server'],
- '--except-interface=lo',
- '--dhcp-range=set:%s,%s,static,%s,%ss' %
- (network_ref['label'],
- network_ref['dhcp_start'],
- network_ref['netmask'],
- CONF.dhcp_lease_time),
- '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])),
- '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'),
- '--dhcp-script=%s' % CONF.dhcpbridge,
- '--no-hosts',
- '--leasefile-ro']
-
- # dnsmasq currently gives an error for an empty domain,
- # rather than ignoring. So only specify it if defined.
- if CONF.dhcp_domain:
- cmd.append('--domain=%s' % CONF.dhcp_domain)
-
- dns_servers = CONF.dns_server
- if CONF.use_network_dns_servers:
- if network_ref.get('dns1'):
- dns_servers.append(network_ref.get('dns1'))
- if network_ref.get('dns2'):
- dns_servers.append(network_ref.get('dns2'))
- if network_ref['multi_host']:
- cmd.append('--addn-hosts=%s' % _dhcp_file(dev, 'hosts'))
- if dns_servers:
- cmd.append('--no-resolv')
- for dns_server in dns_servers:
- cmd.append('--server=%s' % dns_server)
-
- _execute(*cmd, run_as_root=True)
-
- _add_dnsmasq_accept_rules(dev)
-
-
-@utils.synchronized('radvd_start')
-def update_ra(context, dev, network_ref):
- conffile = _ra_file(dev, 'conf')
- conf_str = """
-interface %s
-{
- AdvSendAdvert on;
- MinRtrAdvInterval 3;
- MaxRtrAdvInterval 10;
- prefix %s
- {
- AdvOnLink on;
- AdvAutonomous on;
- };
-};
-""" % (dev, network_ref['cidr_v6'])
- write_to_file(conffile, conf_str)
-
- # Make sure radvd can actually read it (it setuid()s to "nobody")
- os.chmod(conffile, 0o644)
-
- pid = _ra_pid_for(dev)
-
- # if radvd is already running, then tell it to reload
- if pid:
- if is_pid_cmdline_correct(pid, conffile):
- try:
- _execute('kill', pid, run_as_root=True)
- except Exception as exc:
- LOG.error(_LE('killing radvd threw %s'), exc)
- else:
- LOG.debug('Pid %d is stale, relaunching radvd', pid)
-
- cmd = ['radvd',
- '-C', '%s' % _ra_file(dev, 'conf'),
- '-p', '%s' % _ra_file(dev, 'pid')]
-
- _execute(*cmd, run_as_root=True)
-
-
-def _host_lease(fixedip):
- """Return a host string for an address in leasefile format."""
- timestamp = timeutils.utcnow()
- seconds_since_epoch = calendar.timegm(timestamp.utctimetuple())
- return '%d %s %s %s *' % (seconds_since_epoch + CONF.dhcp_lease_time,
- fixedip.virtual_interface.address,
- fixedip.address,
- fixedip.instance.hostname or '*')
-
-
-def _host_dhcp_network(vif_id):
- return 'NW-%s' % vif_id
-
-
-def _host_dhcp(fixedip):
- """Return a host string for an address in dhcp-host format."""
- # NOTE(cfb): dnsmasq on linux only supports 64 characters in the hostname
- # field (LP #1238910). Since the . counts as a character we need
- # to truncate the hostname to only 63 characters.
- hostname = fixedip.instance.hostname
- if len(hostname) > 63:
- LOG.warning(_LW('hostname %s too long, truncating.') % (hostname))
- hostname = fixedip.instance.hostname[:2] + '-' +\
- fixedip.instance.hostname[-60:]
- if CONF.use_single_default_gateway:
- net = _host_dhcp_network(fixedip.virtual_interface_id)
- return '%s,%s.%s,%s,net:%s' % (fixedip.virtual_interface.address,
- hostname,
- CONF.dhcp_domain,
- fixedip.address,
- net)
- else:
- return '%s,%s.%s,%s' % (fixedip.virtual_interface.address,
- hostname,
- CONF.dhcp_domain,
- fixedip.address)
-
-
-def _host_dns(fixedip):
- return '%s\t%s.%s' % (fixedip.address,
- fixedip.instance.hostname,
- CONF.dhcp_domain)
-
-
-def _host_dhcp_opts(vif_id=None, gateway=None):
- """Return an empty gateway option."""
- values = []
- if vif_id is not None:
- values.append(_host_dhcp_network(vif_id))
- # NOTE(vish): 3 is the dhcp option for gateway.
- values.append('3')
- if gateway:
- values.append('%s' % gateway)
- return ','.join(values)
-
-
-def _execute(*cmd, **kwargs):
- """Wrapper around utils._execute for fake_network."""
- if CONF.fake_network:
- LOG.debug('FAKE NET: %s', ' '.join(map(str, cmd)))
- return 'fake', 0
- else:
- return utils.execute(*cmd, **kwargs)
-
-
-def device_exists(device):
- """Check if ethernet device exists."""
- return os.path.exists('/sys/class/net/%s' % device)
-
-
-def _dhcp_file(dev, kind):
- """Return path to a pid, leases, hosts or conf file for a bridge/device."""
- fileutils.ensure_tree(CONF.networks_path)
- return os.path.abspath('%s/nova-%s.%s' % (CONF.networks_path,
- dev,
- kind))
-
-
-def _ra_file(dev, kind):
- """Return path to a pid or conf file for a bridge/device."""
- fileutils.ensure_tree(CONF.networks_path)
- return os.path.abspath('%s/nova-ra-%s.%s' % (CONF.networks_path,
- dev,
- kind))
-
-
-def _dnsmasq_pid_for(dev):
- """Returns the pid for prior dnsmasq instance for a bridge/device.
-
- Returns None if no pid file exists.
-
- If machine has rebooted pid might be incorrect (caller should check).
-
- """
- pid_file = _dhcp_file(dev, 'pid')
-
- if os.path.exists(pid_file):
- try:
- with open(pid_file, 'r') as f:
- return int(f.read())
- except (ValueError, IOError):
- return None
-
-
-def _ra_pid_for(dev):
- """Returns the pid for prior radvd instance for a bridge/device.
-
- Returns None if no pid file exists.
-
- If machine has rebooted pid might be incorrect (caller should check).
-
- """
- pid_file = _ra_file(dev, 'pid')
-
- if os.path.exists(pid_file):
- with open(pid_file, 'r') as f:
- return int(f.read())
-
-
-def _ip_bridge_cmd(action, params, device):
- """Build commands to add/del ips to bridges/devices."""
- cmd = ['ip', 'addr', action]
- cmd.extend(params)
- cmd.extend(['dev', device])
- return cmd
-
-
-def _set_device_mtu(dev, mtu=None):
- """Set the device MTU."""
-
- if not mtu:
- mtu = CONF.network_device_mtu
- if mtu:
- utils.execute('ip', 'link', 'set', dev, 'mtu',
- mtu, run_as_root=True,
- check_exit_code=[0, 1, 2, 254])
-
-
-def _create_veth_pair(dev1_name, dev2_name):
- """Create a pair of veth devices with the specified names,
- deleting any previous devices with those names.
- """
- for dev in [dev1_name, dev2_name]:
- delete_net_dev(dev)
-
- utils.execute('ip', 'link', 'add', dev1_name, 'type', 'veth', 'peer',
- 'name', dev2_name, run_as_root=True)
- for dev in [dev1_name, dev2_name]:
- utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
- utils.execute('ip', 'link', 'set', dev, 'promisc', 'on',
- run_as_root=True)
- _set_device_mtu(dev)
-
-
-def _ovs_vsctl(args):
- full_args = ['ovs-vsctl', '--timeout=%s' % CONF.ovs_vsctl_timeout] + args
- try:
- return utils.execute(*full_args, run_as_root=True)
- except Exception as e:
- LOG.error(_LE("Unable to execute %(cmd)s. Exception: %(exception)s"),
- {'cmd': full_args, 'exception': e})
- raise exception.AgentError(method=full_args)
-
-
-def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id):
- _ovs_vsctl(['--', '--if-exists', 'del-port', dev, '--',
- 'add-port', bridge, dev,
- '--', 'set', 'Interface', dev,
- 'external-ids:iface-id=%s' % iface_id,
- 'external-ids:iface-status=active',
- 'external-ids:attached-mac=%s' % mac,
- 'external-ids:vm-uuid=%s' % instance_id])
- _set_device_mtu(dev)
-
-
-def delete_ovs_vif_port(bridge, dev):
- _ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev])
- delete_net_dev(dev)
-
-
-def ovs_set_vhostuser_port_type(dev):
- _ovs_vsctl(['--', 'set', 'Interface', dev, 'type=dpdkvhostuser'])
-
-
-def create_ivs_vif_port(dev, iface_id, mac, instance_id):
- utils.execute('ivs-ctl', 'add-port',
- dev, run_as_root=True)
-
-
-def delete_ivs_vif_port(dev):
- utils.execute('ivs-ctl', 'del-port', dev,
- run_as_root=True)
- utils.execute('ip', 'link', 'delete', dev,
- run_as_root=True)
-
-
-def create_tap_dev(dev, mac_address=None):
- if not device_exists(dev):
- try:
- # First, try with 'ip'
- utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
- run_as_root=True, check_exit_code=[0, 2, 254])
- except processutils.ProcessExecutionError:
- # Second option: tunctl
- utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
- if mac_address:
- utils.execute('ip', 'link', 'set', dev, 'address', mac_address,
- run_as_root=True, check_exit_code=[0, 2, 254])
- utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True,
- check_exit_code=[0, 2, 254])
-
-
-def delete_net_dev(dev):
- """Delete a network device only if it exists."""
- if device_exists(dev):
- try:
- utils.execute('ip', 'link', 'delete', dev, run_as_root=True,
- check_exit_code=[0, 2, 254])
- LOG.debug("Net device removed: '%s'", dev)
- except processutils.ProcessExecutionError:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed removing net device: '%s'"), dev)
-
-
-def delete_bridge_dev(dev):
- """Delete a network bridge."""
- if device_exists(dev):
- try:
- utils.execute('ip', 'link', 'set', dev, 'down', run_as_root=True)
- utils.execute('brctl', 'delbr', dev, run_as_root=True)
- except processutils.ProcessExecutionError:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed removing bridge device: '%s'"), dev)
-
-
-# Similar to compute virt layers, the Linux network node
-# code uses a flexible driver model to support different ways
-# of creating ethernet interfaces and attaching them to the network.
-# In the case of a network host, these interfaces
-# act as gateway/dhcp/vpn/etc. endpoints not VM interfaces.
-interface_driver = None
-
-
-def _get_interface_driver():
- global interface_driver
- if not interface_driver:
- interface_driver = importutils.import_object(
- CONF.linuxnet_interface_driver)
- return interface_driver
-
-
-def plug(network, mac_address, gateway=True):
- return _get_interface_driver().plug(network, mac_address, gateway)
-
-
-def unplug(network):
- return _get_interface_driver().unplug(network)
-
-
-def get_dev(network):
- return _get_interface_driver().get_dev(network)
-
-
-class LinuxNetInterfaceDriver(object):
- """Abstract class that defines generic network host API
- for all Linux interface drivers.
- """
-
- def plug(self, network, mac_address):
- """Create Linux device, return device name."""
- raise NotImplementedError()
-
- def unplug(self, network):
- """Destroy Linux device, return device name."""
- raise NotImplementedError()
-
- def get_dev(self, network):
- """Get device name."""
- raise NotImplementedError()
-
-
-# plugs interfaces using Linux Bridge
-class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
-
- def plug(self, network, mac_address, gateway=True):
- vlan = network.get('vlan')
- if vlan is not None:
- iface = CONF.vlan_interface or network['bridge_interface']
- LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
- vlan,
- network['bridge'],
- iface,
- network,
- mac_address,
- network.get('mtu'))
- iface = 'vlan%s' % vlan
- else:
- iface = CONF.flat_interface or network['bridge_interface']
- LinuxBridgeInterfaceDriver.ensure_bridge(
- network['bridge'],
- iface,
- network, gateway)
-
- if network['share_address'] or CONF.share_dhcp_address:
- isolate_dhcp_address(iface, network['dhcp_server'])
- # NOTE(vish): applying here so we don't get a lock conflict
- iptables_manager.apply()
- return network['bridge']
-
- def unplug(self, network, gateway=True):
- vlan = network.get('vlan')
- if vlan is not None:
- iface = 'vlan%s' % vlan
- LinuxBridgeInterfaceDriver.remove_vlan_bridge(vlan,
- network['bridge'])
- else:
- iface = CONF.flat_interface or network['bridge_interface']
- LinuxBridgeInterfaceDriver.remove_bridge(network['bridge'],
- gateway)
-
- if network['share_address'] or CONF.share_dhcp_address:
- remove_isolate_dhcp_address(iface, network['dhcp_server'])
-
- iptables_manager.apply()
- return self.get_dev(network)
-
- def get_dev(self, network):
- return network['bridge']
-
- @staticmethod
- def ensure_vlan_bridge(vlan_num, bridge, bridge_interface,
- net_attrs=None, mac_address=None,
- mtu=None):
- """Create a vlan and bridge unless they already exist."""
- interface = LinuxBridgeInterfaceDriver.ensure_vlan(vlan_num,
- bridge_interface, mac_address,
- mtu)
- LinuxBridgeInterfaceDriver.ensure_bridge(bridge, interface, net_attrs)
- return interface
-
- @staticmethod
- def remove_vlan_bridge(vlan_num, bridge):
- """Delete a bridge and vlan."""
- LinuxBridgeInterfaceDriver.remove_bridge(bridge)
- LinuxBridgeInterfaceDriver.remove_vlan(vlan_num)
-
- @staticmethod
- @utils.synchronized('lock_vlan', external=True)
- def ensure_vlan(vlan_num, bridge_interface, mac_address=None, mtu=None,
- interface=None):
- """Create a vlan unless it already exists."""
- if interface is None:
- interface = 'vlan%s' % vlan_num
- if not device_exists(interface):
- LOG.debug('Starting VLAN interface %s', interface)
- _execute('ip', 'link', 'add', 'link', bridge_interface,
- 'name', interface, 'type', 'vlan',
- 'id', vlan_num, run_as_root=True,
- check_exit_code=[0, 2, 254])
- # (danwent) the bridge will inherit this address, so we want to
- # make sure it is the value set from the NetworkManager
- if mac_address:
- _execute('ip', 'link', 'set', interface, 'address',
- mac_address, run_as_root=True,
- check_exit_code=[0, 2, 254])
- _execute('ip', 'link', 'set', interface, 'up', run_as_root=True,
- check_exit_code=[0, 2, 254])
- # NOTE(vish): set mtu every time to ensure that changes to mtu get
- # propogated
- _set_device_mtu(interface, mtu)
- return interface
-
- @staticmethod
- @utils.synchronized('lock_vlan', external=True)
- def remove_vlan(vlan_num):
- """Delete a vlan."""
- vlan_interface = 'vlan%s' % vlan_num
- delete_net_dev(vlan_interface)
-
- @staticmethod
- @utils.synchronized('lock_bridge', external=True)
- def ensure_bridge(bridge, interface, net_attrs=None, gateway=True,
- filtering=True):
- """Create a bridge unless it already exists.
-
- :param interface: the interface to create the bridge on.
- :param net_attrs: dictionary with attributes used to create bridge.
- :param gateway: whether or not the bridge is a gateway.
- :param filtering: whether or not to create filters on the bridge.
-
- If net_attrs is set, it will add the net_attrs['gateway'] to the bridge
- using net_attrs['broadcast'] and net_attrs['cidr']. It will also add
- the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set.
-
- The code will attempt to move any ips that already exist on the
- interface onto the bridge and reset the default gateway if necessary.
-
- """
- if not device_exists(bridge):
- LOG.debug('Starting Bridge %s', bridge)
- out, err = _execute('brctl', 'addbr', bridge,
- check_exit_code=False, run_as_root=True)
- if (err and err != "device %s already exists; can't create "
- "bridge with the same name\n" % (bridge)):
- msg = _('Failed to add bridge: %s') % err
- raise exception.NovaException(msg)
-
- _execute('brctl', 'setfd', bridge, 0, run_as_root=True)
- # _execute('brctl setageing %s 10' % bridge, run_as_root=True)
- _execute('brctl', 'stp', bridge, 'off', run_as_root=True)
- _execute('ip', 'link', 'set', bridge, 'up', run_as_root=True)
-
- if interface:
- LOG.debug('Adding interface %(interface)s to bridge %(bridge)s',
- {'interface': interface, 'bridge': bridge})
- out, err = _execute('brctl', 'addif', bridge, interface,
- check_exit_code=False, run_as_root=True)
- if (err and err != "device %s is already a member of a bridge; "
- "can't enslave it to bridge %s.\n" % (interface, bridge)):
- msg = _('Failed to add interface: %s') % err
- raise exception.NovaException(msg)
-
- # NOTE(apmelton): Linux bridge's default behavior is to use the
- # lowest mac of all plugged interfaces. This isn't a problem when
- # it is first created and the only interface is the bridged
- # interface. But, as instance interfaces are plugged, there is a
- # chance for the mac to change. So, set it here so that it won't
- # change in the future.
- if not CONF.fake_network:
- interface_addrs = netifaces.ifaddresses(interface)
- interface_mac = interface_addrs[netifaces.AF_LINK][0]['addr']
- _execute('ip', 'link', 'set', bridge, 'address', interface_mac,
- run_as_root=True)
-
- out, err = _execute('ip', 'link', 'set', interface, 'up',
- check_exit_code=False, run_as_root=True)
-
- # NOTE(vish): This will break if there is already an ip on the
- # interface, so we move any ips to the bridge
- # NOTE(danms): We also need to copy routes to the bridge so as
- # not to break existing connectivity on the interface
- old_routes = []
- out, err = _execute('ip', 'route', 'show', 'dev', interface)
- for line in out.split('\n'):
- fields = line.split()
- if fields and 'via' in fields:
- old_routes.append(fields)
- _execute('ip', 'route', 'del', *fields,
- run_as_root=True)
- out, err = _execute('ip', 'addr', 'show', 'dev', interface,
- 'scope', 'global')
- for line in out.split('\n'):
- fields = line.split()
- if fields and fields[0] == 'inet':
- if fields[-2] in ('secondary', 'dynamic', ):
- params = fields[1:-2]
- else:
- params = fields[1:-1]
- _execute(*_ip_bridge_cmd('del', params, fields[-1]),
- run_as_root=True, check_exit_code=[0, 2, 254])
- _execute(*_ip_bridge_cmd('add', params, bridge),
- run_as_root=True, check_exit_code=[0, 2, 254])
- for fields in old_routes:
- _execute('ip', 'route', 'add', *fields,
- run_as_root=True)
-
- if filtering:
- # Don't forward traffic unless we were told to be a gateway
- ipv4_filter = iptables_manager.ipv4['filter']
- if gateway:
- for rule in get_gateway_rules(bridge):
- ipv4_filter.add_rule(*rule)
- else:
- ipv4_filter.add_rule('FORWARD',
- ('--in-interface %s -j %s'
- % (bridge, CONF.iptables_drop_action)))
- ipv4_filter.add_rule('FORWARD',
- ('--out-interface %s -j %s'
- % (bridge, CONF.iptables_drop_action)))
-
- @staticmethod
- @utils.synchronized('lock_bridge', external=True)
- def remove_bridge(bridge, gateway=True, filtering=True):
- """Delete a bridge."""
- if not device_exists(bridge):
- return
- else:
- if filtering:
- ipv4_filter = iptables_manager.ipv4['filter']
- if gateway:
- for rule in get_gateway_rules(bridge):
- ipv4_filter.remove_rule(*rule)
- else:
- drop_actions = ['DROP']
- if CONF.iptables_drop_action != 'DROP':
- drop_actions.append(CONF.iptables_drop_action)
-
- for drop_action in drop_actions:
- ipv4_filter.remove_rule('FORWARD',
- ('--in-interface %s -j %s'
- % (bridge, drop_action)))
- ipv4_filter.remove_rule('FORWARD',
- ('--out-interface %s -j %s'
- % (bridge, drop_action)))
- delete_bridge_dev(bridge)
-
-
-# NOTE(cfb): This is a temporary fix to LP #1316621. We really want to call
-# ebtables with --concurrent. In order to do that though we need
-# libvirt to support this. Additionally since ebtables --concurrent
-# will hang indefinitely waiting on the lock we need to teach
-# oslo_concurrency.processutils how to timeout a long running
-# process first. Once those are complete we can replace all of this
-# with calls to ebtables --concurrent and a reasonable timeout.
-def _exec_ebtables(*cmd, **kwargs):
- check_exit_code = kwargs.pop('check_exit_code', True)
-
- # List of error strings to re-try.
- retry_strings = (
- 'Multiple ebtables programs',
- )
-
- # We always try at least once
- attempts = CONF.ebtables_exec_attempts
- if attempts <= 0:
- attempts = 1
- count = 1
- while count <= attempts:
- # Updated our counters if needed
- sleep = CONF.ebtables_retry_interval * count
- count += 1
- # NOTE(cfb): ebtables reports all errors with a return code of 255.
- # As such we can't know if we hit a locking error, or some
- # other error (like a rule doesn't exist) so we have to
- # to parse stderr.
- try:
- _execute(*cmd, check_exit_code=[0], **kwargs)
- except processutils.ProcessExecutionError as exc:
- # See if we can retry the error.
- if any(error in exc.stderr for error in retry_strings):
- if count > attempts and check_exit_code:
- LOG.warning(_LW('%s failed. Not Retrying.'), ' '.join(cmd))
- raise
- else:
- # We need to sleep a bit before retrying
- LOG.warning(_LW("%(cmd)s failed. Sleeping %(time)s "
- "seconds before retry."),
- {'cmd': ' '.join(cmd), 'time': sleep})
- time.sleep(sleep)
- else:
- # Not eligible for retry
- if check_exit_code:
- LOG.warning(_LW('%s failed. Not Retrying.'), ' '.join(cmd))
- raise
- else:
- return
- else:
- # Success
- return
-
-
-@utils.synchronized('ebtables', external=True)
-def ensure_ebtables_rules(rules, table='filter'):
- for rule in rules:
- cmd = ['ebtables', '-t', table, '-D'] + rule.split()
- _exec_ebtables(*cmd, check_exit_code=False, run_as_root=True)
- cmd[3] = '-I'
- _exec_ebtables(*cmd, run_as_root=True)
-
-
-@utils.synchronized('ebtables', external=True)
-def remove_ebtables_rules(rules, table='filter'):
- for rule in rules:
- cmd = ['ebtables', '-t', table, '-D'] + rule.split()
- _exec_ebtables(*cmd, check_exit_code=False, run_as_root=True)
-
-
-def isolate_dhcp_address(interface, address):
- # block arp traffic to address across the interface
- rules = []
- rules.append('INPUT -p ARP -i %s --arp-ip-dst %s -j DROP'
- % (interface, address))
- rules.append('OUTPUT -p ARP -o %s --arp-ip-src %s -j DROP'
- % (interface, address))
- rules.append('FORWARD -p IPv4 -i %s --ip-protocol udp '
- '--ip-destination-port 67:68 -j DROP'
- % interface)
- rules.append('FORWARD -p IPv4 -o %s --ip-protocol udp '
- '--ip-destination-port 67:68 -j DROP'
- % interface)
- # NOTE(vish): the above is not possible with iptables/arptables
- ensure_ebtables_rules(rules)
-
-
-def remove_isolate_dhcp_address(interface, address):
- # block arp traffic to address across the interface
- rules = []
- rules.append('INPUT -p ARP -i %s --arp-ip-dst %s -j DROP'
- % (interface, address))
- rules.append('OUTPUT -p ARP -o %s --arp-ip-src %s -j DROP'
- % (interface, address))
- rules.append('FORWARD -p IPv4 -i %s --ip-protocol udp '
- '--ip-destination-port 67:68 -j DROP'
- % interface)
- rules.append('FORWARD -p IPv4 -o %s --ip-protocol udp '
- '--ip-destination-port 67:68 -j DROP'
- % interface)
- remove_ebtables_rules(rules)
- # NOTE(vish): the above is not possible with iptables/arptables
-
-
-def get_gateway_rules(bridge):
- interfaces = CONF.forward_bridge_interface
- if 'all' in interfaces:
- return [('FORWARD', '-i %s -j ACCEPT' % bridge),
- ('FORWARD', '-o %s -j ACCEPT' % bridge)]
- rules = []
- for iface in CONF.forward_bridge_interface:
- if iface:
- rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (bridge,
- iface)))
- rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (iface,
- bridge)))
- rules.append(('FORWARD', '-i %s -o %s -j ACCEPT' % (bridge, bridge)))
- rules.append(('FORWARD', '-i %s -j %s' % (bridge,
- CONF.iptables_drop_action)))
- rules.append(('FORWARD', '-o %s -j %s' % (bridge,
- CONF.iptables_drop_action)))
- return rules
-
-
-# plugs interfaces using Open vSwitch
-class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver):
-
- def plug(self, network, mac_address, gateway=True):
- dev = self.get_dev(network)
- if not device_exists(dev):
- bridge = CONF.linuxnet_ovs_integration_bridge
- _ovs_vsctl(['--', '--may-exist', 'add-port', bridge, dev,
- '--', 'set', 'Interface', dev, 'type=internal',
- '--', 'set', 'Interface', dev,
- 'external-ids:iface-id=%s' % dev,
- '--', 'set', 'Interface', dev,
- 'external-ids:iface-status=active',
- '--', 'set', 'Interface', dev,
- 'external-ids:attached-mac=%s' % mac_address])
- _execute('ip', 'link', 'set', dev, 'address', mac_address,
- run_as_root=True)
- _set_device_mtu(dev, network.get('mtu'))
- _execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
- if not gateway:
- # If we weren't instructed to act as a gateway then add the
- # appropriate flows to block all non-dhcp traffic.
- _execute('ovs-ofctl',
- 'add-flow', bridge, 'priority=1,actions=drop',
- run_as_root=True)
- _execute('ovs-ofctl', 'add-flow', bridge,
- 'udp,tp_dst=67,dl_dst=%s,priority=2,actions=normal' %
- mac_address, run_as_root=True)
- # .. and make sure iptbles won't forward it as well.
- iptables_manager.ipv4['filter'].add_rule('FORWARD',
- '--in-interface %s -j %s' % (bridge,
- CONF.iptables_drop_action))
- iptables_manager.ipv4['filter'].add_rule('FORWARD',
- '--out-interface %s -j %s' % (bridge,
- CONF.iptables_drop_action))
- else:
- for rule in get_gateway_rules(bridge):
- iptables_manager.ipv4['filter'].add_rule(*rule)
-
- return dev
-
- def unplug(self, network):
- dev = self.get_dev(network)
- bridge = CONF.linuxnet_ovs_integration_bridge
- _ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev])
- return dev
-
- def get_dev(self, network):
- dev = 'gw-' + str(network['uuid'][0:11])
- return dev
-
-
-# plugs interfaces using Linux Bridge when using NeutronManager
-class NeutronLinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
-
- BRIDGE_NAME_PREFIX = 'brq'
- GATEWAY_INTERFACE_PREFIX = 'gw-'
-
- def plug(self, network, mac_address, gateway=True):
- dev = self.get_dev(network)
- bridge = self.get_bridge(network)
- if not gateway:
- # If we weren't instructed to act as a gateway then add the
- # appropriate flows to block all non-dhcp traffic.
- # .. and make sure iptbles won't forward it as well.
- iptables_manager.ipv4['filter'].add_rule('FORWARD',
- ('--in-interface %s -j %s'
- % (bridge, CONF.iptables_drop_action)))
- iptables_manager.ipv4['filter'].add_rule('FORWARD',
- ('--out-interface %s -j %s'
- % (bridge, CONF.iptables_drop_action)))
- return bridge
- else:
- for rule in get_gateway_rules(bridge):
- iptables_manager.ipv4['filter'].add_rule(*rule)
-
- create_tap_dev(dev, mac_address)
-
- if not device_exists(bridge):
- LOG.debug("Starting bridge %s ", bridge)
- utils.execute('brctl', 'addbr', bridge, run_as_root=True)
- utils.execute('brctl', 'setfd', bridge, str(0), run_as_root=True)
- utils.execute('brctl', 'stp', bridge, 'off', run_as_root=True)
- utils.execute('ip', 'link', 'set', bridge, 'address', mac_address,
- run_as_root=True, check_exit_code=[0, 2, 254])
- utils.execute('ip', 'link', 'set', bridge, 'up', run_as_root=True,
- check_exit_code=[0, 2, 254])
- LOG.debug("Done starting bridge %s", bridge)
-
- full_ip = '%s/%s' % (network['dhcp_server'],
- network['cidr'].rpartition('/')[2])
- utils.execute('ip', 'address', 'add', full_ip, 'dev', bridge,
- run_as_root=True, check_exit_code=[0, 2, 254])
-
- return dev
-
- def unplug(self, network):
- dev = self.get_dev(network)
- if not device_exists(dev):
- return None
- else:
- delete_net_dev(dev)
- return dev
-
- def get_dev(self, network):
- dev = self.GATEWAY_INTERFACE_PREFIX + str(network['uuid'][0:11])
- return dev
-
- def get_bridge(self, network):
- bridge = self.BRIDGE_NAME_PREFIX + str(network['uuid'][0:11])
- return bridge
-
-# provide compatibility with existing configs
-QuantumLinuxBridgeInterfaceDriver = NeutronLinuxBridgeInterfaceDriver
-
-iptables_manager = IptablesManager()
-
-
-def set_vf_interface_vlan(pci_addr, mac_addr, vlan=0):
- pf_ifname = pci_utils.get_ifname_by_pci_address(pci_addr,
- pf_interface=True)
- vf_ifname = pci_utils.get_ifname_by_pci_address(pci_addr)
- vf_num = pci_utils.get_vf_num_by_pci_address(pci_addr)
-
- # Set the VF's mac address and vlan
- exit_code = [0, 2, 254]
- port_state = 'up' if vlan > 0 else 'down'
- utils.execute('ip', 'link', 'set', pf_ifname,
- 'vf', vf_num,
- 'mac', mac_addr,
- 'vlan', vlan,
- run_as_root=True,
- check_exit_code=exit_code)
- # Bring up/down the VF's interface
- utils.execute('ip', 'link', 'set', vf_ifname,
- port_state,
- run_as_root=True,
- check_exit_code=exit_code)
diff --git a/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.sh b/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.sh
new file mode 100755
index 0000000..d5abbbe
--- /dev/null
+++ b/fuel-plugin-ovsnfv/deployment_scripts/puppet/modules/ovsdpdk/files/linux_net.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+# This script is patching /usr/lib/python2.7/dist-packages/nova/network/linux_net.py
+# More specifically it's adding '1' as recognized exit code
+# it's WA and will work just when _setr_device_mtu will not change dramatically
+
+# step1) get ROW for patching
+FILE="/usr/lib/python2.7/dist-packages/nova/network/linux_net.py"
+
+ROW=`grep -n "def _set_device_mtu(dev, mtu=None):" $FILE | cut -d ":" -f 1`
+
+# step2) use sed for patching it
+
+sed -i.bck "$((ROW+8))s/check_exit_code=\[0, 2, 254\]/check_exit_code=\[0, 1, 2, 254\]/" $FILE
+
+diff $FILE $FILE.bck
+
+if [ $? -eq 0 ]; then
+ echo "WARNING: linux_net.py not patched, please check if it's really needed"
+else
+ echo "SUCCESS: linux_net.py patched"
+fi