From ca317c8a9891c38ce0777ef2eba4f51716092a14 Mon Sep 17 00:00:00 2001 From: Harry Huang Date: Thu, 20 Dec 2018 18:54:44 +0800 Subject: Add reserved_range in subnet table JIRA: COMPASS-612 1. reserved_range can be range and individual ips e.g. "10.1.0.0-10.1.0.50, 10.1.0.66" 2. IP within reserved range will cause an error Change-Id: If2160af165a57cab3bc8b528379879cad42a5db0 Signed-off-by: Harry Huang --- compass-deck/db/api/host.py | 51 +++++++++++++++++++++++++++++++++++------- compass-deck/db/api/network.py | 40 ++++++++++++++++++++++++++++----- compass-deck/db/api/utils.py | 1 + 3 files changed, 79 insertions(+), 13 deletions(-) (limited to 'compass-deck/db/api') diff --git a/compass-deck/db/api/host.py b/compass-deck/db/api/host.py index 15e0bb6..61e3ab2 100644 --- a/compass-deck/db/api/host.py +++ b/compass-deck/db/api/host.py @@ -16,6 +16,7 @@ import functools import logging import netaddr +import ipaddress import re from compass.db.api import database @@ -23,6 +24,7 @@ from compass.db.api import metadata_holder as metadata_api from compass.db.api import permission from compass.db.api import user as user_api from compass.db.api import utils +from compass.db.api import network from compass.db import exception from compass.db import models from compass.utils import util @@ -642,6 +644,35 @@ def get_hostnetwork(host_network_id, user=None, session=None, **kwargs): return _get_hostnetwork(host_network_id, session=session) +def check_ip_available(subnet, ip): + if not subnet.reserved_range: + return + ip_int = int(ipaddress.IPv4Address(ip.decode())) + reserved_ranges = [] + reserved_ips = [] + for item in subnet.reserved_range.split(','): + ip_ends = item.split('-') + if len(ip_ends) == 2: + reserved_ranges.append(item) + elif len(ip_ends) == 1: + reserved_ips.append(item) + for item in reserved_ranges: + ends = item.split('-') + check_1 = int(ipaddress.IPv4Address(ends[0].decode())) - ip_int + check_2 = int(ipaddress.IPv4Address(ends[1].decode())) - ip_int + if (check_1 > 0) ^ (check_2 > 0): + raise exception.Forbidden( + 'IP %s is reserved, reserved range: %s' + % (ip, subnet.reserved_range) + ) + for item in reserved_ips: + if ip_int == int(ipaddress.IPv4Address(item.decode())): + raise exception.Forbidden( + 'IP %s is reserved, reserved range: %s' + % (ip, subnet.reserved_range) + ) + + @utils.supported_filters( ADDED_NETWORK_FIELDS, optional_support_keys=OPTIONAL_ADDED_NETWORK_FIELDS, @@ -652,17 +683,20 @@ def get_hostnetwork(host_network_id, user=None, session=None, **kwargs): ) @utils.wrap_to_dict(RESP_NETWORK_FIELDS) def _add_host_network( - host_id, exception_when_existing=True, - session=None, user=None, interface=None, ip=None, **kwargs + host_id, exception_when_existing=True, session=None, + user=None, interface=None, ip=None, subnet_id=None, **kwargs ): """Add hostnetwork to a host.""" host = _get_host(host_id, session=session) check_host_editable(host, user=user) + subnet = network.get_subnet_internal(subnet_id, session=session) + check_ip_available(subnet, ip) user_id = user.id return utils.add_db_object( session, models.HostNetwork, exception_when_existing, - host.id, interface, user_id, ip=ip, **kwargs + host.id, interface, user_id, + ip=ip, subnet_id=subnet_id, **kwargs ) @@ -671,14 +705,13 @@ def _add_host_network( permission.PERMISSION_ADD_HOST_NETWORK ) def add_host_network( - host_id, exception_when_existing=True, - interface=None, user=None, session=None, **kwargs + host_id, exception_when_existing=True, interface=None, + user=None, session=None, subnet_id=None, **kwargs ): """Create a hostnetwork to a host.""" return _add_host_network( - host_id, - exception_when_existing, - interface=interface, session=session, user=user, **kwargs + host_id, exception_when_existing, interface=interface, + user=user, session=session, subnet_id=subnet_id, **kwargs ) @@ -747,6 +780,8 @@ def _update_host_network( ): """Update host network.""" check_host_editable(host_network.host, user=user) + subnet = network.get_subnet_internal(host_network.subnet_id, session=session) + check_ip_available(subnet, ip) return utils.update_db_object(session, host_network, **kwargs) diff --git a/compass-deck/db/api/network.py b/compass-deck/db/api/network.py index e505344..763b0b3 100644 --- a/compass-deck/db/api/network.py +++ b/compass-deck/db/api/network.py @@ -15,6 +15,7 @@ """Network related database operations.""" import logging import netaddr +import ipaddress import re from compass.db.api import database @@ -27,14 +28,15 @@ from compass.db import models SUPPORTED_FIELDS = ['subnet', 'name', 'gateway'] RESP_FIELDS = [ - 'id', 'name', 'subnet', 'gateway', 'created_at', 'updated_at' + 'id', 'name', 'subnet', 'gateway', 'created_at', + 'updated_at', 'reserved_range' ] ADDED_FIELDS = ['subnet'] -OPTIONAL_ADDED_FIELDS = ['name', 'gateway'] +OPTIONAL_ADDED_FIELDS = ['name', 'gateway', 'reserved_range'] IGNORE_FIELDS = [ 'id', 'created_at', 'updated_at' ] -UPDATED_FIELDS = ['subnet', 'name', 'gateway'] +UPDATED_FIELDS = ['subnet', 'name', 'gateway', 'reserved_range'] def _check_subnet(subnet): @@ -47,6 +49,29 @@ def _check_subnet(subnet): 'subnet %s format unrecognized' % subnet) +def _check_ip_range(ip_ranges): + """Check if the ip range is valid. + The valid range can be a range or individual ips. + Range should be two ips jointed with "-", different ip + ranges and ips should be separated by "," + e.g. "10.1.0.0-10.1.0.50, 10.1.0.60" + """ + for ip_range in ip_ranges.split(','): + ip_ends = ip_range.split('-') + try: + ipaddress.IPv4Address(ip_ends[0].decode()) + if len(ip_ends) == 2: + ipaddress.IPv4Address(ip_ends[1].decode()) + except Exception as error: + logging.exception(error) + raise exception.InvalidParameter( + 'ip range %s format unrecognized' % ip_ranges) + finally: + if len(ip_ends) > 2: + raise exception.InvalidParameter( + 'ip range %s format unrecognized' % ip_ranges) + + @utils.supported_filters(optional_support_keys=SUPPORTED_FIELDS) @database.run_in_session() @user_api.check_user_permission( @@ -72,6 +97,11 @@ def _get_subnet(subnet_id, session=None, **kwargs): ) +def get_subnet_internal(subnet_id, session=None, **kwargs): + """"Helper function to get subnet.""" + return _get_subnet(subnet_id=subnet_id, session=session, **kwargs) + + @utils.supported_filters([]) @database.run_in_session() @user_api.check_user_permission( @@ -93,7 +123,7 @@ def get_subnet( ADDED_FIELDS, optional_support_keys=OPTIONAL_ADDED_FIELDS, ignore_support_keys=IGNORE_FIELDS ) -@utils.input_validates(subnet=_check_subnet) +@utils.input_validates(subnet=_check_subnet, reserved_range=_check_ip_range) @database.run_in_session() @user_api.check_user_permission( permission.PERMISSION_ADD_SUBNET @@ -114,7 +144,7 @@ def add_subnet( optional_support_keys=UPDATED_FIELDS, ignore_support_keys=IGNORE_FIELDS ) -@utils.input_validates(subnet=_check_subnet) +@utils.input_validates(subnet=_check_subnet, reserved_range=_check_ip_range) @database.run_in_session() @user_api.check_user_permission( permission.PERMISSION_ADD_SUBNET diff --git a/compass-deck/db/api/utils.py b/compass-deck/db/api/utils.py index ef975ef..8921b4a 100644 --- a/compass-deck/db/api/utils.py +++ b/compass-deck/db/api/utils.py @@ -1219,6 +1219,7 @@ def check_power_manage(power_manage): if not isinstance(power_manage, dict): raise exception.InvalidParameter( 'invalid power manage %s' % power_manage + ) for key in power_manage: if key not in ['ip', 'username', 'password']: -- cgit 1.2.3-korg