Skip to content

Instantly share code, notes, and snippets.

@namnh68
Last active June 8, 2016 08:46
Show Gist options
  • Save namnh68/9f2a9e5cd7c00beb0bb3679750a67549 to your computer and use it in GitHub Desktop.
Save namnh68/9f2a9e5cd7c00beb0bb3679750a67549 to your computer and use it in GitHub Desktop.
overlap for ipv6
from sqlalchemy import event
from sqlalchemy import DDL
def mysql_cidr_overlap(engine, metadata):
@event.listens_for(metadata, "after_create")
def _create_mysql_proc(target, connection, **kw):
if connection.engine.name != 'mysql':
return
if connection.scalar(
"SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES "
"WHERE ROUTINE_TYPE='FUNCTION' AND ROUTINE_SCHEMA=DATABASE() AND "
"ROUTINE_NAME=%s",
("cidr_overlap", )
):
connection.execute("DROP FUNCTION cidr_overlap")
connection.execute(
DDL("""
CREATE FUNCTION cidr_overlap (cidr1 varchar(64), ip_version_1 tinyint, cidr2 varchar(64), ip_version_2 tinyint)
RETURNS TINYINT
BEGIN
DECLARE ipv6_1, ipv6_2, bitmask INT;
DECLARE division, modulo, netmask SMALLINT;
DECLARE tmp TINYINT DEFAULT 1;
SET netmask = least(
cast(substring_index(cidr1, '/', -1) as signed integer),
cast(substring_index(cidr2, '/', -1) as signed integer));
IF (ip_version_1 = 6 AND ip_version_2 = 6) THEN
SET division = netmask DIV 16;
SET modulo = netmask %% 16;
masking : WHILE (division >= 0) DO
SET ipv6_1 = CONV(substring_index(substring_index
(cidr1, ':', tmp), ':', -1), 16, 10);
SET ipv6_2 = CONV(substring_index(substring_index
(cidr2, ':', tmp), ':', -1), 16, 10);
IF (division != 0) THEN
IF (ipv6_1 != ipv6_2) THEN
RETURN 0;
LEAVE masking;
END IF;
ELSE
IF (modulo = 0) THEN
RETURN 1;
ELSE
SET bitmask = pow(2, (16 - modulo)) - 1;
RETURN ipv6_1 & ~bitmask = ipv6_2 & ~bitmask;
END IF;
END IF;
SET tmp = tmp + 1;
SET division = division - 1;
END WHILE masking;
ELSEIF (ip_version_1 = 4 AND ip_version_2 = 4) THEN
SET bitmask = pow(2, 32 - netmask) - 1;
RETURN
inet_aton(substring_index(cidr1, '/', 1)) & ~bitmask =
inet_aton(substring_index(cidr2, '/', 1)) & ~bitmask;
ELSE
RETURN 0;
END IF;
END
""")
)
if __name__ == '__main__':
from sqlalchemy import Column, Integer, String, create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, aliased
from sqlalchemy import event
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
cidr = Column(String(64))
ip_version = Column(Integer)
event.listen(
A.__table__, "after_create",
DDL("""
CREATE TRIGGER no_overlap_cidr_a
BEFORE INSERT ON a
FOR EACH ROW
BEGIN
DECLARE msg VARCHAR(200);
IF (EXISTS(SELECT * FROM a WHERE cidr_overlap(cidr, ip_version, NEW.cidr, NEW.ip_version))) THEN
SET msg = CONCAT(
'inserted subnet ', NEW.cidr,
' conflicts with existing subnets');
SIGNAL sqlstate '45000'
SET MESSAGE_TEXT = msg;
END IF;
END
""")
)
e = create_engine("mysql://root:abc123@localhost/test", echo=True)
mysql_cidr_overlap(e, Base.metadata)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)
s = Session(e)
with s.begin_nested():
s.add(A(cidr='fd04:901d:e7de:abcf::/64', ip_version='6'))
with s.begin_nested():
s.add(A(cidr='fd04:901d:e7de:abce::/64', ip_version='6'))
with s.begin_nested():
s.add(A(cidr='192.168.1.0/24', ip_version='4'))
with s.begin_nested():
s.add(A(cidr='192.168.2.0/24', ip_version='4'))
try:
with s.begin_nested():
s.add(A(cidr='192.168.0.0/16', ip_version='4'))
except Exception as e:
print "Error! %s" % e
try:
with s.begin_nested():
s.add(A(cidr='fd04:901d:e7de:abcf::/65', ip_version='6'))
except Exception as e:
print "Error! %s" % e
s.commit()
a1, a2 = aliased(A), aliased(A)
# return all non-overlapping CIDR pairs
for a, b in s.query(a1.cidr, a2.cidr).\
filter(~func.cidr_overlap\
(a1.cidr, a1.ip_version, a2.cidr, a2.ip_version)).\
filter(a1.id > a2.id):
print a, b
@namnh68
Copy link
Author

namnh68 commented Jun 8, 2016

Here is result:

stack@hoainam:~/Documents$ python overlap_for_ipv6.py

2016-06-09 15:47:57,300 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,301 INFO sqlalchemy.engine.base.Engine COMMIT
2016-06-09 15:47:57,302 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-06-09 15:47:57,303 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_1
2016-06-09 15:47:57,303 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,304 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,304 INFO sqlalchemy.engine.base.Engine ('fd04:901d:e7de:abcf::/64', '6')
2016-06-09 15:47:57,306 INFO sqlalchemy.engine.base.Engine RELEASE SAVEPOINT sa_savepoint_1
2016-06-09 15:47:57,306 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,306 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_2
2016-06-09 15:47:57,306 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,307 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,307 INFO sqlalchemy.engine.base.Engine ('fd04:901d:e7de:abce::/64', '6')
2016-06-09 15:47:57,308 INFO sqlalchemy.engine.base.Engine RELEASE SAVEPOINT sa_savepoint_2
2016-06-09 15:47:57,308 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,309 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_3
2016-06-09 15:47:57,309 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,309 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,309 INFO sqlalchemy.engine.base.Engine ('192.168.1.0/24', '4')
2016-06-09 15:47:57,310 INFO sqlalchemy.engine.base.Engine RELEASE SAVEPOINT sa_savepoint_3
2016-06-09 15:47:57,310 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,311 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_4
2016-06-09 15:47:57,311 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,311 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,311 INFO sqlalchemy.engine.base.Engine ('192.168.2.0/24', '4')
2016-06-09 15:47:57,312 INFO sqlalchemy.engine.base.Engine RELEASE SAVEPOINT sa_savepoint_4
2016-06-09 15:47:57,312 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,312 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_5
2016-06-09 15:47:57,313 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,313 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,313 INFO sqlalchemy.engine.base.Engine ('192.168.0.0/16', '4')
2016-06-09 15:47:57,313 INFO sqlalchemy.engine.base.Engine ROLLBACK TO SAVEPOINT sa_savepoint_5
2016-06-09 15:47:57,314 INFO sqlalchemy.engine.base.Engine ()
Error! (_mysql_exceptions.OperationalError) (1644, 'inserted subnet 192.168.0.0/16 conflicts with existing subnets') [SQL: u'INSERT INTO a (cidr, ip_version) VALUES (%s, %s)'] [parameters: ('192.168.0.0/16', '4')]
2016-06-09 15:47:57,314 INFO sqlalchemy.engine.base.Engine SAVEPOINT sa_savepoint_6
2016-06-09 15:47:57,314 INFO sqlalchemy.engine.base.Engine ()
2016-06-09 15:47:57,315 INFO sqlalchemy.engine.base.Engine INSERT INTO a (cidr, ip_version) VALUES (%s, %s)
2016-06-09 15:47:57,315 INFO sqlalchemy.engine.base.Engine ('fd04:901d:e7de:abcf::/65', '6')
2016-06-09 15:47:57,315 INFO sqlalchemy.engine.base.Engine ROLLBACK TO SAVEPOINT sa_savepoint_6
2016-06-09 15:47:57,315 INFO sqlalchemy.engine.base.Engine ()
Error! (_mysql_exceptions.OperationalError) (1644, 'inserted subnet fd04:901d:e7de:abcf::/65 conflicts with existing subnets') [SQL: u'INSERT INTO a (cidr, ip_version) VALUES (%s, %s)'] [parameters: ('fd04:901d:e7de:abcf::/65', '6')]
2016-06-09 15:47:57,316 INFO sqlalchemy.engine.base.Engine COMMIT
2016-06-09 15:47:57,349 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2016-06-09 15:47:57,350 INFO sqlalchemy.engine.base.Engine SELECT a_1.cidr AS a_1_cidr, a_2.cidr AS a_2_cidr
FROM a AS a_1, a AS a_2
WHERE NOT cidr_overlap(a_1.cidr, a_1.ip_version, a_2.cidr, a_2.ip_version) AND a_1.id > a_2.id
2016-06-09 15:47:57,350 INFO sqlalchemy.engine.base.Engine ()
fd04:901d:e7de:abce::/64 fd04:901d:e7de:abcf::/64
192.168.1.0/24 fd04:901d:e7de:abcf::/64
192.168.2.0/24 fd04:901d:e7de:abcf::/64
192.168.1.0/24 fd04:901d:e7de:abce::/64
192.168.2.0/24 fd04:901d:e7de:abce::/64
192.168.2.0/24 192.168.1.0/24

@namnh68
Copy link
Author

namnh68 commented Jun 8, 2016

Show information in DB:

mysql> select * from a;
+----+--------------------------+------------+
| id | cidr | ip_version |
+----+--------------------------+------------+
| 1 | fd04:901d:e7de:abcf::/64 | 6 |
| 2 | fd04:901d:e7de:abce::/64 | 6 |
| 3 | 192.168.1.0/24 | 4 |
| 4 | 192.168.2.0/24 | 4 |
+----+--------------------------+------------+
4 rows in set (0.00 sec)

Please refer link: http://paste.openstack.org/show/508869/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment