From 64ab011299fa230399cba5c401962974a4b6c069 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sun, 12 Jan 2020 13:41:54 +0100 Subject: [PATCH] import mac.py from cinv --- uncloud/hack/hackcloud/mac-gen.py | 171 ++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 uncloud/hack/hackcloud/mac-gen.py diff --git a/uncloud/hack/hackcloud/mac-gen.py b/uncloud/hack/hackcloud/mac-gen.py new file mode 100644 index 0000000..9f23854 --- /dev/null +++ b/uncloud/hack/hackcloud/mac-gen.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# 2012 Nico Schottelius (nico-cinv at schottelius.org) +# +# This file is part of cinv. +# +# cinv is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cinv is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cinv. If not, see . +# +# + +import argparse +import logging +import os.path +import os +import re + +import cinv +from cinv import fsproperty + +log = logging.getLogger(__name__) + +class Error(cinv.Error): + pass + + +class Mac(object): + + def __init__(self): + self.base_dir = self.get_base_dir() + + _prefix = fsproperty.FileStringProperty(lambda obj: os.path.join(obj.base_dir, "prefix")) + free = fsproperty.FileListProperty(lambda obj: os.path.join(obj.base_dir, "free")) + last = fsproperty.FileStringProperty(lambda obj: os.path.join(obj.base_dir, "last")) + + def _init_base_dir(self): + try: + os.makedirs(self.base_dir, exist_ok=True) + except OSError as e: + raise Error(e) + + @staticmethod + def validate_mac(mac): + if not re.match(r'([0-9A-F]{2}[-:]){5}[0-9A-F]{2}$', mac, re.I): + raise Error("Not a valid mac address: %s" % mac) + + def free_append(self, mac): + if mac in self.free: + raise Error("Mac already in free database: %s" % mac) + + self._init_base_dir() + self.free.append(mac) + + @staticmethod + def get_base_dir(): + return cinv.get_base_dir("db/mac") + + @classmethod + def exists(cls): + return os.path.exists(cls.get_base_dir()) + + def get_next(self): + self._init_base_dir() + + if self.free: + return self.free.pop() + + if not self.prefix: + raise Error("Cannot generate address without prefix - use prefix-set") + + if self.last: + suffix = re.search(r'([0-9A-F]{2}[-:]){2}[0-9A-F]{2}$', self.last, re.I) + last_number_hex = "0x%s" % suffix.group().replace(":", "") + last_number = int(last_number_hex, 16) + + if last_number == int('0xffffff', 16): + raise Error("Exhausted all possible mac addresses - try to free some") + + next_number = last_number + 1 + else: + next_number = 0 + + next_number_hex = "%0.6x" % next_number + next_suffix = "%s:%s:%s" % (next_number_hex[0:2], next_number_hex[2:4], next_number_hex[4:6]) + + next_mac = "%s:%s" % (self.prefix, next_suffix) + + self.last = next_mac + + return next_mac + + + @property + def prefix(self): + return self._prefix + + @prefix.setter + def prefix(self, prefix): + if not re.match(r'([0-9A-F]{2}[-:]){2}[0-9A-F]{2}$', prefix, re.I): + raise Error("Wrong mac address format - use 00:11:22") + + self._init_base_dir() + self._prefix = prefix + + @classmethod + def commandline_generate(cls, args): + mac = Mac() + print(mac.get_next()) + + @classmethod + def commandline_free_add(cls, args): + mac = Mac() + mac.validate_mac(args.address) + mac.free_append(args.address) + + @classmethod + def commandline_free_list(cls, args): + mac = Mac() + for mac in mac.free: + print(mac) + + @classmethod + def commandline_prefix_set(cls, args): + mac = Mac() + mac.prefix = args.prefix + + @classmethod + def commandline_prefix_get(cls, args): + mac = cls() + print(mac.prefix) + + @classmethod + def commandline_add(cls, args): + host = cls(fqdn=args.fqdn) + host.host_type = args.type + + @classmethod + def commandline_args(cls, parent_parser, parents): + """Add us to the parent parser and add all parents to our parsers""" + + parser = {} + parser['sub'] = parent_parser.add_subparsers(title="Mac Commands") + + parser['free-add'] = parser['sub'].add_parser('free-add', parents=parents) + parser['free-add'].add_argument('address', help='Address to add to free database') + parser['free-add'].set_defaults(func=cls.commandline_free_add) + + parser['free-list'] = parser['sub'].add_parser('free-list', parents=parents, + help="List free mac addresses") + parser['free-list'].set_defaults(func=cls.commandline_free_list) + + parser['generate'] = parser['sub'].add_parser('generate', parents=parents) + parser['generate'].set_defaults(func=cls.commandline_generate) + + parser['prefix-get'] = parser['sub'].add_parser('prefix-get', parents=parents) + parser['prefix-get'].set_defaults(func=cls.commandline_prefix_get) + + parser['prefix-set'] = parser['sub'].add_parser('prefix-set', parents=parents) + parser['prefix-set'].add_argument('prefix', help="3 Byte address prefix (f.i. '00:16:3e')") + parser['prefix-set'].set_defaults(func=cls.commandline_prefix_set)