Commit 5f177e1f authored by Alain Takoudjou's avatar Alain Takoudjou

partition can request to update ip route of tap interface

parent 36d19481
......@@ -840,8 +840,10 @@ class Tap(object):
if self.ipv4_addr:
# Check if this route exits
code, result = callAndRead(['ip', 'route', 'show', self.ipv4_addr])
if code == 0 and self.ipv4_addr in result and self.name in result:
if self.ipv4_addr in result:
# Skip already configured route
return
code, result = callAndRead(['ip', 'route', 'show'])
callAndRead(['route', 'add', '-host', self.ipv4_addr, 'dev', self.name])
else:
raise ValueError("%s should not be empty. No ipv4 address assigned to %s" %
......
......@@ -42,6 +42,7 @@ import warnings
import logging
import json
import shutil
import ConfigParser
if sys.version_info < (2, 6):
warnings.warn('Used python version (%s) is old and has problems with'
......@@ -61,8 +62,10 @@ from slapos.grid.svcbackend import (launchSupervisord,
_getSupervisordSocketPath)
from slapos.grid.utils import (md5digest, dropPrivileges, SlapPopen, updateFile)
from slapos.human import human2bytes
from slapos.format import callAndRead
import slapos.slap
from netaddr import valid_ipv4, valid_ipv6
import netifaces
# XXX: should be moved to SLAP library
......@@ -79,6 +82,7 @@ PROMISE_TIMEOUT = 3
COMPUTER_PARTITION_TIMESTAMP_FILENAME = '.timestamp'
COMPUTER_PARTITION_LATEST_BANG_TIMESTAMP_FILENAME = '.slapos_latest_bang_timestamp'
COMPUTER_PARTITION_INSTALL_ERROR_FILENAME = '.slapgrid-%s-error.log'
COMPUTER_PARTITION_UPDATE_TAP_ROUTE = '.%s-request-update-route'
# XXX hardcoded watchdog_path
WATCHDOG_PATH = '/opt/slapos/bin/slapos-watchdog'
......@@ -396,6 +400,40 @@ class Slapgrid(object):
self.ipv4_global_network= ""
self.firewall_conf = firewall_conf
def _getDefaultNetworkInterface(self):
return netifaces.gateways()['default'][netifaces.AF_INET]
def _checkIpAddressOnIfaces(self, ip):
"""
Check if ip address already exists on server
"""
for iface in netifaces.interfaces():
if iface == 'lo':
continue
address_list = netifaces.ifaddresses(iface)
if address_list.has_key(netifaces.AF_INET):
for address in address_list[netifaces.AF_INET]:
if address['addr'] == ip:
return iface, address
def _checkAndExecuteRouteCmd(self, run_command, check_command, check_list, rule_exists=False):
self.logger.debug("Execute command: %s" % ' '.join(check_command))
_, result = callAndRead(check_command)
rule_found = False
for result_line in result.split('\n'):
if not result_line:
continue
rule_found = True
for item in check_list:
rule_found = rule_found and item in result_line
if rule_found:
break
if not rule_exists and not rule_found:
self.logger.debug("Execute command: %s" % ' '.join(run_command))
return callAndRead(run_command)
elif rule_exists and rule_found:
self.logger.debug("Execute command: %s" % ' '.join(run_command))
return callAndRead(run_command)
def _getWatchdogLine(self):
invocation_list = [WATCHDOG_PATH]
......@@ -862,6 +900,141 @@ stderr_logfile_backups=1
self.logger.warn("IP/Network address %s is not valid. ignored.." % ip)
return valid_list
def _restoreSlapTapRoute(self, partition_id, ipv4):
instance_path = os.path.join(self.instance_root, partition_id)
saved_request_file = os.path.join(
instance_path, '.slapos-request-update-route')
if not os.path.exists(saved_request_file):
return
self.logger.debug("Cleaning custom ip routes...")
config_request = ConfigParser.ConfigParser()
try:
config_request.readfp(open(saved_request_file))
except ConfigParser.MissingSectionHeaderError:
self.logger.error("%s: Invalid config file specified." % saved_request_file)
raise
route_section = '%s-routes' % partition_id
if not route_section in config_request.sections():
raise Exception("Section %s required to restore ip route configuration" \
"is not found in file %s" % (route_section, saved_request_file))
gateway_ip = tap_ip = ''
option_list = config_request.options(route_section)
interface = config_request.get(route_section, 'interface')
if 'forward-to-ip' in option_list:
gateway_ip = config_request.get(route_section, 'forward-to-ip')
if 'ip-to-interface' in option_list:
tap_ip = config_request.get(route_section, 'ip-to-interface')
if gateway_ip:
tap_route_cmd = ['route', 'del', ipv4, 'gw', gateway_ip]
tap_default_cmd = ['route', 'add', '-host', ipv4, 'dev', interface]
self._checkAndExecuteRouteCmd(
tap_default_cmd,
['ip', 'route', 'show', ipv4],
[ipv4, interface], False)
self._checkAndExecuteRouteCmd(
tap_route_cmd,
['ip', 'route', 'show', ipv4],
[ipv4, gateway_ip], True)
elif tap_ip:
tap_route_cmd = ['ip', 'route', 'del', tap_ip, 'dev', interface]
self._checkAndExecuteRouteCmd(
tap_route_cmd,
['ip', 'route', 'show', tap_ip],
[tap_ip, interface], True)
os.remove(saved_request_file)
def _updateSlapTapRoute(self, partition_id, ipv4, iface, hosting_ip_list, check_remove=False):
"""
This is used to update route to tap interface in case a instance(vm) is moved to
another computer. Then we add routes to reach the moved instance.
"""
instance_path = os.path.join(self.instance_root, partition_id)
filename = COMPUTER_PARTITION_UPDATE_TAP_ROUTE % partition_id
request_file = os.path.join(instance_path, filename)
saved_request_file = os.path.join(
instance_path, '.slapos-request-update-route')
if os.path.exists(request_file) and not check_remove:
self.logger.debug("File %s found. Configure custom ip routes..." % filename)
else:
return self._restoreSlapTapRoute(partition_id, ipv4)
config_request = ConfigParser.ConfigParser()
try:
config_request.readfp(open(request_file))
except ConfigParser.MissingSectionHeaderError:
self.logger.error("%s: Invalid file specified." % request_file)
raise
route_section = '%s-routes' % partition_id
if not route_section in config_request.sections():
self.logger.debug('Section %s is missing, skiping file %s.' % (
route_section, filename))
return
option_list = config_request.options(route_section)
interface = config_request.get(route_section, 'interface')
if iface != interface:
raise ValueError("Cannot update ip route on unallowed or invalid interface: %s" % interface)
gateway_ip = tap_ip = ''
if 'forward-to-ip' in option_list:
gateway_ip = config_request.get(route_section, 'forward-to-ip')
if not valid_ipv4(gateway_ip):
raise ValueError("Invalid or unallowed IPv4 specified: %s" % gateway_ip)
if 'ip-to-interface' in option_list:
tap_ip = config_request.get(route_section, 'ip-to-interface')
if not tap_ip in hosting_ip_list:
raise ValueError("Invalid or unallowed IPv4 specified: %s" % tap_ip)
# save requested file
shutil.copy(request_file, saved_request_file)
if gateway_ip:
# Forward route from interface to another ip
result = self._checkIpAddressOnIfaces(gateway_ip)
if result is not None:
self.logger.debug("IP address %s found on interface %s, ip route will " \
"not be added!" % (gateway_ip, result[0]))
return
_, default_iface = self._getDefaultNetworkInterface()
tap_route_cmd = ['route', 'add', '-host', ipv4, 'gw', gateway_ip]
tap_default_cmd = ['ip', 'route', 'del', ipv4, 'dev', interface]
self._checkAndExecuteRouteCmd(
['route', 'add', '-host', gateway_ip, 'dev', default_iface],
['ip', 'route', 'show', gateway_ip],
[gateway_ip, default_iface])
# Remove current route to slaptap
self._checkAndExecuteRouteCmd(
tap_default_cmd,
['ip', 'route', 'show', ipv4],
[ipv4, interface], True)
# Add route to new gateway
self._checkAndExecuteRouteCmd(
tap_route_cmd,
['ip', 'route', 'show', ipv4],
[ipv4, gateway_ip], False)
elif tap_ip:
# add new route to tap interface
tap_route_cmd = ['route', 'add', '-host', tap_ip, 'dev', interface]
self._checkAndExecuteRouteCmd(
tap_route_cmd,
['ip', 'route', 'show', tap_ip],
[tap_ip, interface], False)
if 'ip-to-interface-dnat' in option_list:
set_dnat = config_request.get(route_section, 'ip-to-interface-dnat')
if set_dnat in ['True', 'true', 'yes', '1']:
# Add firewall rule to forward to another ip
command = '--permanent --direct --add-rule ipv4 nat '
command += 'PREROUTING 800 -d %s -j DNAT --to-destination %s' % (
ipv4, tap_ip)
return [command]
def _setupComputerPartitionFirewall(self, computer_partition, ip_list, drop_entries=False):
"""
Using linux iptables, limit access to IP of this partition to all
......@@ -869,10 +1042,10 @@ stderr_logfile_backups=1
"""
ipv4_list = []
ipv6_list = []
tap_interface = ''
tap_route_ipv4 = ''
source_ipv4_list = []
source_ipv6_list = []
hosting_ipv4_list = []
hosting_ipv6_list = []
getFirewallRules = getattr(self, '_getFirewallAcceptRules')
if not drop_entries:
......@@ -886,19 +1059,16 @@ stderr_logfile_backups=1
iface, ip = (net_ip[0], net_ip[1])
if not iface.startswith('route_'):
continue
tap_interface = iface.lstrip('route_')
tap_route_ipv4 = ip
if valid_ipv4(ip):
ipv4_list.append(ip)
elif valid_ipv6(ip):
ipv6_list.append(ip)
hosting_ip_list = computer_partition.getFullHostingIpAddressList()
for iface, ip in hosting_ip_list:
if valid_ipv4(ip):
if not ip in ipv4_list:
hosting_ipv4_list.append(ip)
elif valid_ipv6(ip):
if not ip in ipv6_list:
hosting_ipv6_list.append(ip)
filter_dict = getattr(computer_partition, '_filter_dict', None)
extra_list = []
......@@ -916,10 +1086,17 @@ stderr_logfile_backups=1
source_ipv4_list = self._getValidIpv4FromList(extra_list, True)
hosting_ipv4_list.extend(self._getValidIpv4FromList(accept_ip_list, True))
# XXX - ipv6_list and source_ipv6_list ignored for the moment
# In case route need to update route of instance
result_list = self._updateSlapTapRoute(computer_partition.getId(),
tap_route_ipv4,
tap_interface,
hosting_ipv4_list,
drop_entries)
for ip in ipv4_list:
cmd_list = getFirewallRules(ip, hosting_ipv4_list,
source_ipv4_list, ip_type='ipv4')
if ip == tap_route_ipv4 and result_list is not None:
cmd_list.extend(result_list)
self._checkAddFirewallRules(computer_partition.getId(),
cmd_list, add=add_rules)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment