Esempio n. 1
0
    def load(self, path, ifaces):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with the given interfaces and install them.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
            ifaces: A list of interfaces name.
        """
        self.model = ModelInitiator("ethernet", path, backup_interval=-1)
        if not self.model.db:
            raise IOError("Cannot load any configuration.")

        # Initialise the interfaces
        # TODO: 2nd iface's type is "LAN"; another is "WAN"
        if 1 == len(self.model.db) and "id" not in self.model.db[0]:
            _logger.debug("factory install")
            default_db = self.model.db.pop()
            ip_3_def = int(default_db["ip"].split(".")[2]) - 1
            for iface in ifaces:
                ifaddr = ip.ifaddresses(iface)
                db = copy.deepcopy(default_db)
                db["name"] = iface
                db["id"] = int(iface.replace("eth", "")) + 1

                ip_3 = ip_3_def + db["id"]
                db["ip"] = "192.168.%d.127" % ip_3
                db["subnet"] = "192.168.%d.0" % ip_3
                db["gateway"] = "192.168.%d.254" % ip_3

                db["status"] = True if ifaddr["link"] == 1 else False
                db["mac"] = ifaddr["mac"]
                self.model.db.append(db)
            self.save()
Esempio n. 2
0
class Example(Sanji):
    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        """
        Config will be located at ./data/example.json
        If ./data/example.json not exist,
            modelInitiator will create from ./example.json.factory
            and program could access dict object from self.model.db
        (modelInitiator is optional, you can handle data/config by your own)
        """
        self.model = ModelInitiator("example", path_root)

        # Check daemon status here and start/stop it.

    @Route(methods="get", resource="/example")
    def get(self, message, response):
        # Response dict
        # response code => http status code
        #          data => payload
        return response(code=200, data=self.model.db)

    @Route(methods="put", resource="/example")
    def put(self, message, response):
        # Assign incomming dict object to self.model.db
        self.model.db = message.data

        # Save to ./data/example.json
        self.model.save_db()

        # Response current dict object
        #   response code default = 200
        return response(data=self.model.db)
Esempio n. 3
0
class TestModelInitiatorClass(unittest.TestCase):
    """
    " Test class
    """

    model_name = "test_myself"
    model_path = "/tmp/sanji-sdk/tests/test_myself"
    model_db_folder = "/tmp/sanji-sdk/tests/test_myself/data"
    model_factory_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.factory.json"
    model_db = "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json"

    def setUp(self):
        """
        " Prepare
        """
        os.makedirs(self.model_path)
        self.model_initaitor = ModelInitiator(self.model_name, self.model_path)

    def tearDown(self):
        """
        " Clean up
        """
        if os.path.exists(self.model_path):
            shutil.rmtree(self.model_path)

        self.model_initaitor = None

    def test_init(self):
        """
        " Test __init__()
        """
        self.assertEquals(self.model_initaitor.model_name, self.model_name)

    def test_mkdir(self):
        """
        " It Should generate a data folder.
        """
        result = self.model_initaitor.mkdir()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db_folder))

    def test_create_db(self):
        """
        " It should generate a factory db.
        """
        self.model_initaitor.mkdir()
        try:
            with open(self.model_factory_db, 'a'):
                os.utime(self.model_factory_db, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        result = self.model_initaitor.create_db()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db))

        self.db_type = "sql"
        result = self.model_initaitor.create_db()
        self.assertFalse(result)
Esempio n. 4
0
    def test_db_manager(self):
        # case 1: existing
        self.model_initiator.save_db()
        self.model_initiator.db_manager()
        self.assertEqual(self.model_initiator.db_status, "existing")

        # case 2: factory
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db") as create_db:
            create_db.return_value = 1
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator = ModelInitiator(self.model_name,
                                                  self.model_path,
                                                  backup_interval=-1)

            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "factory")

        # case 3: backup
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db") as create_db:
            create_db.return_value = 1
            self.model_initiator.save_db()
            self.model_initiator.backup_db()
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator.db_manager()
            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "backup")
Esempio n. 5
0
 def init(self, *args, **kwargs):
     path_root = os.path.abspath(os.path.dirname(__file__))
     """
     Config will be located at ./data/example.json
     If ./data/example.json not exist,
         modelInitiator will create from ./example.json.factory
         and program could access dict object from self.model.db
     (modelInitiator is optional, you can handle data/config by your own)
     """
     self.model = ModelInitiator("example", path_root)
Esempio n. 6
0
    def __init__(self, name, path, schema=None, model_cls=dict):
        self.model_cls = model_cls
        self.schema = schema

        if schema is not None and not isinstance(schema, Schema):
            raise TypeError("schema should be instance of voluptuous.Schema")

        if not issubclass(model_cls, dict):
            raise TypeError("model_cls should be derivative dict class")

        self.model = ModelInitiator(model_name=name, model_path=path)
        self._batch = ModelBatch(self.model)
Esempio n. 7
0
    def setUp(self):
        """
        " Prepare
        """
        factory_data = {"name": "factory"}
        if not os.path.exists(self.model_db_folder):
            os.makedirs(self.model_db_folder)
        with open(self.model_factory_db, "w") as fp:
            json.dump(factory_data, fp, indent=4)

        self.model_initiator = ModelInitiator(self.model_name,
                                              self.model_path,
                                              backup_interval=-1)
Esempio n. 8
0
    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("route", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()
Esempio n. 9
0
    def test_init(self):
        """
        " Test __init__()
        """
        # case 1: check name
        self.assertEquals(self.model_initiator.model_name, self.model_name)

        # case 2: thread
        self.model_initiator = None
        self.model_initiator = ModelInitiator(self.model_name,
                                              self.model_path,
                                              backup_interval=1)
        self.assertEquals(self.model_initiator.model_name, self.model_name)
Esempio n. 10
0
    def test_db_manager(self):
        # case 1: existing
        self.model_initiator.save_db()
        self.model_initiator.db_manager()
        self.assertEqual(self.model_initiator.db_status, "existing")

        # case 2: factory
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db")as create_db:
            create_db.return_value = 1
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator = ModelInitiator(
                self.model_name, self.model_path, backup_interval=-1)

            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "factory")

        # case 3: backup
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db")as create_db:
            create_db.return_value = 1
            self.model_initiator.save_db()
            self.model_initiator.backup_db()
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator.db_manager()
            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "backup")
Esempio n. 11
0
    def load(self, path, ifaces):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with the given interfaces and install them.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
            ifaces: A list of interfaces name.
        """
        self.model = ModelInitiator("ethernet", path, backup_interval=-1)
        if not self.model.db:
            raise IOError("Cannot load any configuration.")

        # Initialise the interfaces
        # TODO: 2nd iface's type is "LAN"; another is "WAN"
        if 1 == len(self.model.db) and "id" not in self.model.db[0]:
            _logger.debug("factory install")
            default_db = self.model.db.pop()
            ip_3_def = int(default_db["ip"].split(".")[2]) - 1
            for iface in ifaces:
                ifaddr = ip.ifaddresses(iface)
                db = copy.deepcopy(default_db)
                db["name"] = iface
                db["id"] = int(iface.replace("eth", "")) + 1

                ip_3 = ip_3_def + db["id"]
                db["ip"] = "192.168.%d.127" % ip_3
                db["subnet"] = "192.168.%d.0" % ip_3
                db["gateway"] = "192.168.%d.254" % ip_3

                db["currentStatus"] = ifaddr["link"]
                db["mac"] = ifaddr["mac"]
                self.model.db.append(db)
            self.save()
Esempio n. 12
0
    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.status = status.Status(name="status", path=path_root)
        self.properties = ModelInitiator(
            model_name="properties", model_path=path_root)

        # Check aliasName
        if self.properties.db.get("aliasName", "$ModelName") == "$ModelName":
            self.set_alias()
Esempio n. 13
0
 def setUp(self):
     path_root = os.path.dirname(os.path.realpath(__file__)) + "/../../"
     try:
         os.unlink(path_root + "data/ntp.json")
         os.unlink(path_root + "data/ntp.json.backup")
     except:
         pass
     self.model = ModelInitiator("ntp", path_root)
     self.ntp = Ntp(self.model)
Esempio n. 14
0
    def setUp(self):
        """
        " Prepare
        """
        factory_data = {"name": "factory"}
        if not os.path.exists(self.model_db_folder):
            os.makedirs(self.model_db_folder)
        with open(self.model_factory_db, "w") as fp:
            json.dump(factory_data, fp, indent=4)

        self.model_initiator = ModelInitiator(
            self.model_name, self.model_path, backup_interval=-1)
Esempio n. 15
0
    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("cellular", path_root)
        self.model.db[0] = Index.CONF_SCHEMA(self.model.db[0])

        self._dev_name = None
        self._mgr = None
        self._vnstat = None

        self.__init_monit_config(
            enable=(self.model.db[0]["enable"]
                    and self.model.db[0]["keepalive"]["enable"] and True
                    and self.model.db[0]["keepalive"]["reboot"]["enable"]
                    and True),
            target_host=self.model.db[0]["keepalive"]["targetHost"],
            iface=self._dev_name,
            cycles=self.model.db[0]["keepalive"]["reboot"]["cycles"])
        self._init_thread = Thread(name="sanji.cellular.init_thread",
                                   target=self.__initial_procedure)
        self._init_thread.daemon = True
        self._init_thread.start()
Esempio n. 16
0
    def test_init(self):
        """
        " Test __init__()
        """
        # case 1: check name
        self.assertEquals(self.model_initiator.model_name, self.model_name)

        # case 2: thread
        self.model_initiator = None
        self.model_initiator = ModelInitiator(
            self.model_name, self.model_path, backup_interval=1)
        self.assertEquals(self.model_initiator.model_name, self.model_name)
Esempio n. 17
0
    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("dns", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()
Esempio n. 18
0
    def __init__(self, name, path, schema=None, model_cls=dict):
        self.model_cls = model_cls
        self.schema = schema

        if schema is not None and not isinstance(schema, Schema):
            raise TypeError("schema should be instance of voluptuous.Schema")

        if not issubclass(model_cls, dict):
            raise TypeError("model_cls should be derivative dict class")

        self.model = ModelInitiator(
            model_name=name,
            model_path=path
        )
        self._batch = ModelBatch(self.model)
Esempio n. 19
0
class Cellular(Sanji):
    search_router_pattern =\
        re.compile(ur'option routers ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)')
    search_dns_pattern =\
        re.compile(ur'option domain-name-servers (.*);')
    search_ip_pattern =\
        re.compile(ur'fixed-address ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)')
    search_subnet_pattern =\
        re.compile(ur'option subnet-mask ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)')
    search_name_pattern =\
        re.compile(ur'interface "([a-z]+[0-9])"')

    def search_name(self, filetext):
        name = re.search(self.search_name_pattern, filetext)
        if name:
            logger.debug("name is %s" % name.group(1))
            return name.group(1)
        else:
            return 'N/A'

    def search_router(self, filetext):
        router = re.search(self.search_router_pattern, filetext)
        if router:
            logger.debug("router is %s" % router.group(1))
            return router.group(1)
        else:
            return 'N/A'

    def search_dns(self, filetext):
        dns = re.search(self.search_dns_pattern, filetext)
        if dns:
            logger.debug("dns is %s" % dns.group(1))
            return dns.group(1)
        else:
            return 'N/A'

    def search_ip(self, filetext):
        ip = re.search(self.search_ip_pattern, filetext)
        if ip:
            logger.debug("ip is %s" % ip.group(1))
            return ip.group(1)
        else:
            return 'N/A'

    def search_subnet(self, filetext):
        subnet = re.search(self.search_subnet_pattern, filetext)
        if subnet:
            logger.debug("subnet is %s" % subnet.group(1))
            return subnet.group(1)
        else:
            return 'N/A'

    def is_target_device_appear(self, name):
        if os.path.exists(name):
            return True
        else:
            return False

    def is_leases_file_appear(self):
        try:
            with open('/var/lib/dhcp/dhclient.leases',
                      'r') as leases:
                filetext = leases.read()
                return filetext
        except Exception:
            logger.debug("File open failure")
            return ''

    def reconnect_if_disconnected(self):
        for model in self.model.db:
            if self.is_target_device_appear(model['modemPort']):
                dev_id = str(model['id'])
                # update signal
                model['signal'] = self.get_signal_by_id(dev_id)
                logger.debug("Signal %s on device path %s"
                             % (model['signal'],
                                model['modemPort']))

                # check network availability
                # if network status down, turn up
                if model['enable'] == 1:
                        logger.debug("Enable is 1")
                        if self.get_status_by_id(dev_id) == \
                                "'disconnected'":
                            logger.debug("Start connect")
                            self.set_offline_by_id(dev_id)
                            self.set_online_by_id(dev_id)
                            # update info according to dhclient.leases
                            filetext = self.is_leases_file_appear()
                            if filetext:
                                # parse name
                                model['name'] = self.search_name(filetext)

                                # parse router
                                model['router'] = self.search_router(filetext)

                                # parse dns
                                model['dns'] = self.search_dns(filetext)

                                # parse ip
                                model['ip'] = self.search_ip(filetext)

                                # parse subnet
                                model['subnet'] = self.search_subnet(filetext)

                                self.model.save_db()
                else:
                        if self.get_status_by_id(dev_id) == "'connected'":
                            self.set_offline_by_id(dev_id)

            else:
                    model['signal'] = 99

    def get_signal_by_id(self, dev_id):
        try:
            tmp = subprocess.check_output(
                "qmicli -p -d /dev/cdc-wdm" + dev_id +
                " --nas-get-signal-info | grep RSSI \
                | cut -d \"'\" -f 2 \
                | cut -d \" \" -f 1 \
                |tr -d [:cntrl:]",
                shell=True)
            if tmp.isdigit():
                return tmp
            else:
                return 99
        except Exception:
            return 99

    def get_status_by_id(self, dev_id):
        try:
                tmp = subprocess.check_output("qmicli -p -d /dev/cdc-wdm" +
                                              dev_id +
                                              " --wds-get-packet-service-status\
                                              |awk '{print $4}'|\
                                              tr -d [:space:]",
                                              shell=True)
                if tmp == 'connected':
                    return tmp
                else:
                    return 'disconnected'
        except Exception:
                return 'disconnected'

    def set_online_by_id(self, dev_id):
        try:
                subprocess.check_output("rm -rf /var/lib/dhcp/dhclient.leases",
                                        shell=True)
                subprocess.check_output("qmi-network /dev/cdc-wdm" +
                                        dev_id + " start", shell=True)
                subprocess.check_output("dhclient wwan" +
                                        dev_id, shell=True)
                return 'success'
        except Exception:
                return 'fail'

    def set_offline_by_id(self, dev_id):
        try:
                subprocess.check_output("dhclient -r wwan" +
                                        dev_id, shell=True)
                subprocess.check_output("qmi-network /dev/cdc-wdm" +
                                        dev_id + " stop", shell=True)
                return 'success'
        except Exception:
                return 'fail'

    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("cellular", path_root)

    @Route(methods="get", resource="/network/cellulars")
    def get_root(self, message, response):
            return response(code=200, data=self.model.db)

    @Route(methods="get", resource="/network/cellulars/:id")
    def get_root_by_id(self, message, response):
            if int(message.param['id']) > len(self.model.db):
                    return response(code=400, data={
                        "message": "No such id resources."})
            else:
                    return response(code=200,
                                    data=self.model.db
                                    [int(message.param['id'])])

    @Route(methods="put", resource="/network/cellulars/:id")
    def put_root_by_id(self, message, response):
        if not hasattr(message, "data"):
            return response(code=400, data={"message": "Invalid Input."})

        is_match_param = 0

        id = int(message.param['id'])

        if "enable" in message.data:
            self.model.db[id]["enable"] = message.data["enable"]
            is_match_param = 1

        if "apn" in message.data:
            self.model.db[id]["apn"] = message.data["apn"]
            is_match_param = 1

        if "username" in message.data:
            self.model.db[id]["username"] = message.data["username"]
            is_match_param = 1

        if "name" in message.data:
            self.model.db[id]["name"] = message.data["name"]
            is_match_param = 1

        if "dialNumber" in message.data:
            self.model.db[id]["dialNumber"] = message.data["dialNumber"]
            is_match_param = 1

        if "password" in message.data:
            self.model.db[id]["password"] = message.data["password"]
            is_match_param = 1

        if "pinCode" in message.data:
            self.model.db[id]["pinCode"] = message.data["pinCode"]
            is_match_param = 1

        if "enableAuth" in message.data:
            self.model.db[id]["enableAuth"] = message.data["enableAuth"]
            is_match_param = 1

        if is_match_param == 0:
            return response(code=400, data={"message": "No such resources."})

        self.model.save_db()
        return response(code=200,
                        data=self.model.db[id])

    def run(self):
        while True:
            self.reconnect_if_disconnected()
            sleep(30)
Esempio n. 20
0
 def setUp(self):
     """
     " Prepare
     """
     os.makedirs(self.model_path)
     self.model_initaitor = ModelInitiator(self.model_name, self.model_path)
Esempio n. 21
0
class Ethernet(Sanji):
    """
    A model to handle Ethernet interfaces' configuration.

    Attributes:
        model: Ethernet interfaces' database with json format.
    """
    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            bundle_env = kwargs["bundle_env"]
        except KeyError:
            bundle_env = os.getenv("BUNDLE_ENV", "debug")

        self.path_root = os.path.abspath(os.path.dirname(__file__))
        if bundle_env == "debug":  # pragma: no cover
            self.path_root = "%s/tests" % self.path_root

        # Find all ethernet interfaces and load the configuration
        ifaces = ip.interfaces()
        ifaces = [x for x in ifaces if x.startswith("eth")]
        if 0 == len(ifaces):
            _logger.info("No interfaces to be configured.")
            self.stop()
            raise ValueError("No interfaces to be configured.")

        try:
            self.load(self.path_root, ifaces)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

        # Apply the configuration
        for iface in self.model.db:
            self.apply(iface)

    def run(self):
        for iface in self.model.db:
            iface["type"] = "eth"
            iface["mode"] = "dhcp" if iface["enableDhcp"] else "static"
            self.publish.event.put("/network/interfaces/{}".format(
                iface["name"]),
                                   data=iface)

    def load(self, path, ifaces):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with the given interfaces and install them.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
            ifaces: A list of interfaces name.
        """
        self.model = ModelInitiator("ethernet", path, backup_interval=-1)
        if not self.model.db:
            raise IOError("Cannot load any configuration.")

        # Initialise the interfaces
        # TODO: 2nd iface's type is "LAN"; another is "WAN"
        if 1 == len(self.model.db) and "id" not in self.model.db[0]:
            _logger.debug("factory install")
            default_db = self.model.db.pop()
            ip_3_def = int(default_db["ip"].split(".")[2]) - 1
            for iface in ifaces:
                ifaddr = ip.ifaddresses(iface)
                db = copy.deepcopy(default_db)
                db["name"] = iface
                db["id"] = int(iface.replace("eth", "")) + 1

                ip_3 = ip_3_def + db["id"]
                db["ip"] = "192.168.%d.127" % ip_3
                db["subnet"] = "192.168.%d.0" % ip_3
                db["gateway"] = "192.168.%d.254" % ip_3

                db["status"] = True if ifaddr["link"] == 1 else False
                db["mac"] = ifaddr["mac"]
                self.model.db.append(db)
            self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def apply(self, data):
        """
        Apply the configuration to an interface.

        Args:
            data: Information for the interface to be applied (with dictionary
                format)
        """
        iface = "eth%d" % (data["id"] - 1)

        ip.ifupdown(iface, True if data["enable"] else False)
        if not data["enable"]:
            return

        if data["enableDhcp"]:
            ip.ifconfig(iface,
                        True,
                        script="%s/hooks/dhclient-script" % self.path_root)
        else:
            ip.ifconfig(iface, False, data["ip"], data["netmask"],
                        data["gateway"])

    def read(self, id, restart=False, config=True):
        """
        Read the setting for an interface.

        Args:
            id: Interface id, interface name will be eth(id+1).
        """
        for data in self.model.db:
            if data["id"] == id:
                break
        else:
            return None

        # deepcopy to prevent settings be modified
        data = copy.deepcopy(data)

        if not restart and "restart" in data:
            data.pop("restart")

        iface = "eth%d" % (data["id"] - 1)
        ifaddr = ip.ifaddresses(iface)
        data["status"] = True if ifaddr["link"] == 1 else False
        data["mac"] = ifaddr["mac"]

        # """Use configuration data instead of realtime retrieving
        if True is config:
            return data

        data["ip"] = ""
        data["netmask"] = ""
        data["subnet"] = ""
        data["broadcast"] = ""
        # data["gateway"] = ""
        if ifaddr["inet"] and len(ifaddr["inet"]):
            data["ip"] = ifaddr["inet"][0]["ip"]
            data["netmask"] = ifaddr["inet"][0]["netmask"]
            if "subnet" in ifaddr["inet"][0]:
                data["subnet"] = ifaddr["inet"][0]["subnet"]
            else:
                data.pop("subnet")
            if "broadcast" in ifaddr["inet"][0]:
                data["broadcast"] = ifaddr["inet"][0]["broadcast"]
            elif "broadcast" in data:
                data.pop("broadcast")
        # """
        return data

    @staticmethod
    def schema_validate(message):
        """
        Validate the received data, ensure the schema is correct.
        """
        # TODO: ip validation
        schema = Schema(
            {
                Required("id"): Range(min=1),
                Required("enable"): bool,
                Required("enableDhcp"): bool,
                Optional("wan"): bool,
                Optional("ip"): Any(str, unicode),
                Optional("netmask"): Any(str, unicode),
                Optional("gateway"): Any(str, unicode),
                Optional("dns"): [Any(str, unicode)],
                Extra: object
            },
            extra=REMOVE_EXTRA)

        if not hasattr(message, "data"):
            raise KeyError("Invalid input: \"data\" attribute is required.")

        if type(message.data) is list:
            if 0 == len(message.data):
                raise KeyError("Invalid input: empty \"data\".")
            for item in message.data:
                try:
                    schema(item)
                except Exception, e:
                    raise KeyError("Invalid input: %s." % e)

        if type(message.data) is dict:
            try:
                schema(message.data)
            except Exception, e:
                raise KeyError("Invalid input: %s." % e)
Esempio n. 22
0
 def init(self, *args, **kwargs):
     path_root = os.path.abspath(os.path.dirname(__file__))
     self.model = ModelInitiator("cellular", path_root)
Esempio n. 23
0
class Dns(Sanji):
    CONFIG_PATH = "/etc/resolv.conf"

    IFACE_SCHEMA = Schema({
        Required("name"): All(str, Length(1, 255)),
        Required("dns"): [Any("", All(str, Length(0, 15)))]
    }, extra=REMOVE_EXTRA)

    PUT_DB_SCHEMA = Schema({
        Required("source"): All(str, Length(1, 255)),
        Required("dns"): [Any("", All(str, Length(0, 15)))]
    }, extra=REMOVE_EXTRA)

    PUT_DNS_SCHEMA = Schema({
        Optional("enableFixed"): bool,
        Optional("fixedDns"): [Any("", All(str, Length(0, 15)))]
    }, extra=REMOVE_EXTRA)

    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            bundle_env = kwargs["bundle_env"]
        except KeyError:
            bundle_env = os.getenv("BUNDLE_ENV", "debug")

        # load configuration
        self.path_root = os.path.abspath(os.path.dirname(__file__))
        if bundle_env == "debug":  # pragma: no cover
            self.path_root = "%s/tests" % self.path_root

        try:
            self.load(self.path_root)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

        # initialize DNS database
        self.dns_db = []
        if "fixedDns" in self.model.db:
            self.add_dns_list(
                {"source": "fixed",
                 "dns": self.model.db["fixedDns"]})

    def run(self):
        try:
            self.update_config()
        except Exception as e:
            _logger.warning("Failed to update %s: %s" % (Dns.CONFIG_PATH, e))

    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("dns", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def get_dns_list(self, source):
        """
        Get DNS list by source from database.

        Args:
            source: source which the DNS list belongs to.
        """
        for entry in self.dns_db:
            if source == entry["source"]:
                return entry
        return None

    def set_dns_list(self, obj, update=True):
        """
        Update DNS list by source from database.

        Args:
            obj: a dictionary with "source" and "dns" list, for example:
                {
                    "source": "eth0",
                    "dns": ["8.8.8.8", "8.8.4.4"]
                }
        """
        for entry in self.dns_db:
            if obj["source"] == entry["source"]:
                entry["dns"] = obj["dns"]
                return entry
        return self.add_dns_list(obj, update)

    def add_dns_list(self, obj, update=True):
        """
        Add DNS list by source into database and update setting if
        required.

        Args:
            obj: a dictionary with "source" and "dns" list, for example:
                {
                    "source": "eth0",
                    "dns": ["8.8.8.8", "8.8.4.4"]
                }
        """
        entry = self.get_dns_list(obj["source"])
        if entry:
            entry["dns"] = obj["dns"]
        else:
            self.dns_db.append(obj)

        # update config if data updated
        if update and "source" in self.model.db \
                and obj["source"] == self.model.db["source"]:
            self.update_config()

    def remove_dns_list(self, source):
        """
        Remove DNS list by source from database.

        Args:
            source: source for the DNS list belongs to.
        """
        self.dns_db[:] = \
            [i for i in self.dns_db if i.get("source") != source]

    def _generate_config(self):
        """
        Generate /etc/resolv.conf content.
        Priority:
            1. fixed DNS
            2. temporary DNS
            3. by source
        """
        resolv = ""
        data = self.get_current_dns()
        if "dns" not in data:
            return resolv

        for server in data["dns"]:
            if server != "":
                resolv = resolv + ("nameserver %s\n" % server)
        return resolv

    def _write_config(self, resolv):
        """
        Write DNS configurations into DNS file (/etc/resolv.conf).

        Args:
            resolv_info: Text content for DNS information.
        """
        with open(Dns.CONFIG_PATH, "w") as f:
            f.write(resolv)

    def update_config(self):
        """
        Update the DNS configuration by settings.
        """
        self._write_config(self._generate_config())

    def get_current_dns(self):
        """
        Get current DNS settings, include fixed information.
            {
              "enableFixed": false,
              "fixedDns": ["8.8.8.8", "8.8.4.4"],
              "source": "eth0",
              "dns": ["192.168.50.33", "192.168.50.36"]
            }
        """
        data = copy.deepcopy(self.model.db)
        if "enableFixed" not in data:
            data["enableFixed"] = False

        if data["enableFixed"] is True:
            data["source"] = "fixed"
        if "source" in data:
            dns = self.get_dns_list(data["source"])
            if dns and "dns" in dns:
                data["dns"] = copy.copy(dns["dns"])
            elif data["enableFixed"] is True:
                data["dns"] = data["fixedDns"]
        return data

    @Route(methods="get", resource="/network/dns")
    def _get_current_dns(self, message, response):
        data = self.get_current_dns()
        return response(data=data)

    def set_current_dns(self, data):
        """
        Update current DNS configuration by message.
        """
        # add to DNS database if data include both source and dns list
        # fixed DNS updated later
        if "source" in data and "dns" in data and data["source"] != "fixed":
            self.add_dns_list(data, False)

        # update settings
        self.model.db.pop("dns", None)

        if "enableFixed" not in self.model.db:
            self.model.db["enableFixed"] = False

        source = None if "source" not in data else data.pop("source")
        dnslist = None if "dns" not in data else data.pop("dns")
        if source and source != "fixed":
                self.model.db["source"] = source
        elif source is None and dnslist:
            self.model.db.pop("source", None)
            self.model.db["dns"] = dnslist

        self.model.db.update(data)
        self.save()

        # update fixed
        dns = {}
        dns["source"] = "fixed"
        if "fixedDns" in self.model.db:
            dns["dns"] = self.model.db["fixedDns"]
        else:
            dns["dns"] = []
        self.set_dns_list(dns)

        self.update_config()

    @Route(methods="put", resource="/network/dns", schema=PUT_DNS_SCHEMA)
    def _put_current_dns(self, message, response):
        try:
            self.set_current_dns(message.data)
        except Exception as e:
            return response(code=400, data={"message": e.message})
        return response(data=message.data)

    @Route(methods="get", resource="/network/dns/db")
    def _get_dns_database(self, message, response):
        return response(data=self.dns_db)

    def set_dns_database(self, message, response):
        """
        Update DNS database batch or by source.
        """
        if type(message.data) is list:
            for dns in message.data:
                self.add_dns_list(dns)
        elif type(message.data) is dict:
            self.add_dns_list(message.data)
        else:
            return response(code=400,
                            data={"message": "Wrong type of DNS database."})
        return response(data=self.dns_db)

    @Route(methods="put", resource="/network/dns/db")
    def _put_dns_database(self, message, response):
        return self.set_dns_database(message, response)

    @Route(methods="put", resource="/network/interfaces/:name")
    def _event_network_interface(self, message):
        """
        Listen interface event to update the dns database and settings.
        """
        if not(hasattr(message, "data")):
            raise ValueError("Data cannot be None or empty.")
        try:
            self.IFACE_SCHEMA(message.data)
        except Exception as e:
            raise e

        _logger.debug("[/network/interfaces] interface: %s, dns: %s"
                      % (message.param["name"], message.data["dns"]))

        dns = {"source": message.param["name"],
               "dns": message.data["dns"]}
        self.add_dns_list(dns)

    @Route(methods="put", resource="/network/wan")
    def _event_network_wan(self, message):
        """
        Listen wan event to update the dns settings.
        """
        try:
            self.set_current_dns({"source": message.data["interface"]})
        except Exception as e:
            _logger.info("[/network/wan] %s".format(e.message))
Esempio n. 24
0
class Index(Sanji):

    PUT_SCHEMA = Schema(
        {
            "timezone": All(str, Length(8)),
            "ntp": {
                "enable": All(int, Range(min=0, max=1)),
                "server": All(str, Length(1, 2048)),
                "interval": All(int, Range(min=60, max=60 * 60 * 24 * 30))
            }
        },
        extra=REMOVE_EXTRA)

    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("ntp", path_root)
        self.ntp = Ntp(self.model)

    @Route(methods="get", resource="/system/time")
    def get(self, message, response):
        realtime_data = {"time": SysTime.get_system_time()}

        return response(data=dict(self.model.db.items() +
                                  realtime_data.items()))

    @Route(methods="put", resource="/system/time", schema=PUT_SCHEMA)
    def put(self, message, response):
        rc = None
        try:
            # update ntp settings
            if "ntp" in message.data:
                rc = self.ntp.update(message.data["ntp"])
                if rc is False:
                    raise RuntimeWarning("Update ntp settings failed.")
                self.model.db["ntp"] = dict(self.model.db["ntp"].items() +
                                            message.data["ntp"].items())

            # change timezone
            if "timezone" in message.data:
                rc = SysTime.set_system_timezone(message.data["timezone"])
                if rc is False:
                    raise RuntimeWarning("Change timezone failed.")
                self.model.db["timezone"] = message.data["timezone"]

            # manual change sys time
            if "time" in message.data:
                if self.model.db["ntp"]["enable"] == 1:
                    _logger.debug("NTP enabled. skipping time setup.")
                else:
                    rc = SysTime.set_system_time(message.data["time"])
                    if rc is False:
                        raise RuntimeWarning("Change system time failed.")

            if rc is None:
                return response(code=400,
                                data={"message": "No input paramters."})
            else:
                self.model.save_db()
        except Exception as e:
            _logger.debug(e, exc_info=True)
            code = 400 if not isinstance(e, RuntimeWarning) else 500
            return response(code=code, data={"message": str(e)})

        realtime_data = {"time": SysTime.get_system_time()}

        # operation successed
        return response(data=dict(self.model.db.items() +
                                  realtime_data.items()))
Esempio n. 25
0
class TestModelInitiatorClass(unittest.TestCase):
    """
    " Test class
    """

    model_name = "test_myself"
    model_path = "/tmp/sanji-sdk/tests/test_myself"
    model_db_folder = "/tmp/sanji-sdk/tests/test_myself/data"
    model_factory_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json.factory"
    model_backup_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json.backup"
    model_db = "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json"

    def setUp(self):
        """
        " Prepare
        """
        factory_data = {"name": "factory"}
        if not os.path.exists(self.model_db_folder):
            os.makedirs(self.model_db_folder)
        with open(self.model_factory_db, "w") as fp:
            json.dump(factory_data, fp, indent=4)

        self.model_initiator = ModelInitiator(self.model_name,
                                              self.model_path,
                                              backup_interval=-1)

    def tearDown(self):
        """
        " Clean up
        """
        self.model_initiator.stop_backup()
        self.model_initiator = None

        if os.path.exists(self.model_path):
            removeall(self.model_db_folder)

    def test_init(self):
        """
        " Test __init__()
        """
        # case 1: check name
        self.assertEquals(self.model_initiator.model_name, self.model_name)

        # case 2: thread
        self.model_initiator = None
        self.model_initiator = ModelInitiator(self.model_name,
                                              self.model_path,
                                              backup_interval=1)
        self.assertEquals(self.model_initiator.model_name, self.model_name)

    def test_db_manager(self):
        # case 1: existing
        self.model_initiator.save_db()
        self.model_initiator.db_manager()
        self.assertEqual(self.model_initiator.db_status, "existing")

        # case 2: factory
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db") as create_db:
            create_db.return_value = 1
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator = ModelInitiator(self.model_name,
                                                  self.model_path,
                                                  backup_interval=-1)

            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "factory")

        # case 3: backup
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db") as create_db:
            create_db.return_value = 1
            self.model_initiator.save_db()
            self.model_initiator.backup_db()
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator.db_manager()
            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "backup")

    def test_create_db(self):
        """
        " It should generate a factory db if there is no db.
        """
        if os.path.exists(self.model_db_folder):
            shutil.rmtree(self.model_db_folder)

        os.makedirs(self.model_db_folder)

        try:
            with open(self.model_factory_db, "a"):
                os.utime(self.model_factory_db, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        # case 1: general case
        result = self.model_initiator.create_db()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db))

        # case 2: no factory db
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        if os.path.exists(self.model_factory_db):
            os.remove(self.model_factory_db)

        with self.assertRaises(RuntimeError):
            result = self.model_initiator.create_db()

        # case 3: sql type
        self.db_type = "sql"
        with self.assertRaises(RuntimeError):
            result = self.model_initiator.create_db()

    def test_recover_db(self):
        # case 1: Check file which restore from backup db
        self.model_initiator.db = {}
        self.model_initiator.db["name"] = "backup"
        self.model_initiator.db["type"] = "json"
        self.model_initiator.save_db()
        self.model_initiator.backup_db()
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        self.assertFalse(os.path.exists(self.model_db))
        self.model_initiator.recover_db(self.model_backup_db)
        self.assertTrue(os.path.exists(self.model_db))
        # case 2: Check data which restore from backup db
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "backup", "type": "json"})

        # case 3: Check file which restore from factory db
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        self.assertFalse(os.path.exists(self.model_db))
        self.model_initiator.recover_db(self.model_factory_db)
        self.assertTrue(os.path.exists(self.model_db))

        # case 4: Check data which restore from factory db
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "factory"})

        # case 5: no file
        try:
            self.assertRaises(
                self.model_initiator.recover_db("/tmp/1234555555.txt"))
        except IOError:
            pass
        else:
            self.fail("No file to load but pass.")

    def test_backup_db(self):
        """
        " Test backup db
        """
        if os.path.exists(self.model_backup_db):
            os.remove(self.model_backup_db)
        # case 1: Check file exist
        self.model_initiator.backup_db()
        self.assertTrue(os.path.exists(self.model_backup_db))

        # case 2: Check data
        with open(self.model_backup_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "factory"})

    def test_load_db(self):
        """
        " It should load json db as a dictionary.
        """
        # case 1: No folder
        self.model_initiator.db = None
        self.assertEqual(type(self.model_initiator.db), type(None))
        self.model_initiator.create_db()
        try:
            with open(self.model_factory_db, "a"):
                os.utime(self.model_factory_db, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        # case 2: data
        data = {"enable": 1}
        with open(self.model_factory_db, "w") as fp:
            json.dump(data, fp, indent=4)

        shutil.copyfile(self.model_factory_db, self.model_db)
        self.model_initiator.load_db()
        self.assertEqual(self.model_initiator.db, data)

        # case 3:
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
            try:
                self.assertRaises(self.model_initiator.load_db())
            except Exception:
                pass

    def test_save_db(self):
        """
        " Test save db
        """
        # case 1: data of saving
        self.model_initiator.db = {}
        self.model_initiator.db["name"] = "John"
        self.model_initiator.db["age"] = 33
        self.model_initiator.save_db()
        db_data = None
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "John", "age": 33})

        # case 2: non dictionary or array type.
        self.model_initiator.db = "string type"
        rc = self.model_initiator.save_db()
        self.assertFalse(rc)

        # case 3: open with no file. (coverage)
        if os.path.exists(self.model_db_folder):
            shutil.rmtree(self.model_db_folder)

        try:
            self.assertRaises(self.model_initiator.save_db())
        except Exception:
            pass

    @patch("sanji.model_initiator.Thread")
    def test_start_backup_thread_is_not_alive(self, Thread):
        self.model_initiator._backup_thread.is_alive = Mock(return_value=False)
        Thread.return_value = MagicMock(daemon=False, start=Mock())
        self.model_initiator.start_backup()
        self.assertTrue(self.model_initiator._backup_thread.daemon)
        self.model_initiator._backup_thread.start.assert_called_once_with()

    def test_start_backup_thread_is_alive(self):
        with self.assertRaises(RuntimeError):
            self.model_initiator._backup_thread.is_alive =\
                Mock(return_value=True)
            self.model_initiator.start_backup()
        self.model_initiator.stop_backup = Mock()

    def test_stop_backup(self):
        rc = self.model_initiator.stop_backup()
        self.assertFalse(rc)
        self.model_initiator.start_backup()
        rc = self.model_initiator.stop_backup()
        self.assertTrue(rc)

        self.model_initaitor.db_type = "json"
        self.model_initaitor.factory_json_db_path = "/1231231235566"
        result = self.model_initaitor.create_db()
        self.assertFalse(result)
Esempio n. 26
0
class Model(object):
    def __init__(self, name, path, schema=None, model_cls=dict):
        self.model_cls = model_cls
        self.schema = schema

        if schema is not None and not isinstance(schema, Schema):
            raise TypeError("schema should be instance of voluptuous.Schema")

        if not issubclass(model_cls, dict):
            raise TypeError("model_cls should be derivative dict class")

        self.model = ModelInitiator(model_name=name, model_path=path)
        self._batch = ModelBatch(self.model)

    def batch(self):
        return self._batch

    def _cast_model(self, obj):
        return self.model_cls(obj)

    @property
    def maxId(self):
        """int: current max id of objects"""
        if len(self.model.db) == 0:
            return 0

        return max(map(lambda obj: obj["id"], self.model.db))

    def validation(self, instance):
        """Valid input instance is vaild or not
            Args:
                Object: input instance
            Returns:
                Object: Instance after vaildation or original instance
                        if schema is None
            Raises:
                Error: If vaildation failed
        """
        if self.schema is None:
            return instance

        return self.schema(instance)

    def add(self, obj):
        """Add a object
            Args:
                Object: Object will be added
            Returns:
                Object: Object with id
            Raises:
                TypeError: If add object is not a dict
                MultipleInvalid: If input object is invaild
        """
        if not isinstance(obj, dict):
            raise TypeError("Add object should be a dict object")
        obj = self.validation(obj)
        obj["id"] = self.maxId + 1
        obj = self._cast_model(obj)
        self.model.db.append(obj)

        if not self._batch.enable.is_set():
            self.model.save_db()
        return obj

    def get(self, id):
        """Get a object by id
            Args:
                id (int): Object id

            Returns:
                Object: Object with specified id
                None: If object not found
        """
        for obj in self.model.db:
            if obj["id"] == id:
                return self._cast_model(obj)

        return None

    def remove(self, id):
        """Remove a object by id
            Args:
                id (int): Object's id should be deleted
            Returns:
                len(int): affected rows
        """
        before_len = len(self.model.db)
        self.model.db = [t for t in self.model.db if t["id"] != id]
        if not self._batch.enable.is_set():
            self.model.save_db()
        return before_len - len(self.model.db)

    def removeAll(self):
        """Remove all objects
            Returns:
                len(int): affected rows
        """
        before_len = len(self.model.db)
        self.model.db = []
        if not self._batch.enable.is_set():
            self.model.save_db()
        return before_len - len(self.model.db)

    def update(self, id, newObj):
        """Update a object
            Args:
                id (int): Target Object ID
                newObj (object): New object will be merged into original object
            Returns:
                Object: Updated object
                None: If specified object id is not found
                MultipleInvalid: If input object is invaild
        """
        newObj = self.validation(newObj)
        for obj in self.model.db:
            if obj["id"] != id:
                continue

            newObj.pop("id", None)
            obj.update(newObj)
            obj = self._cast_model(obj)
            if not self._batch.enable.is_set():
                self.model.save_db()
            return obj

        return None

    def set(self, id, newObj):
        """Set a object
            Args:
                id (int): Target Object ID
                newObj (object): New object will be set
            Returns:
                Object: New object
                None: If specified object id is not found
                MultipleInvalid: If input object is invaild
        """
        newObj = self.validation(newObj)
        for index in xrange(0, len(self.model.db)):
            if self.model.db[index]["id"] != id:
                continue

            newObj["id"] = id
            self.model.db[index] = self._cast_model(newObj)
            if not self._batch.enable.is_set():
                self.model.save_db()
            return self.model.db[index]

        return None

    def getAll(self):
        """Get all objects
            Returns:
                List: list of all objects
        """
        objs = []
        for obj in self.model.db:
            objs.append(self._cast_model(obj))

        return objs
Esempio n. 27
0
class IPRoute(Sanji):
    """
    A model to handle IP Route configuration.

    Attributes:
        model: database with json format.
    """

    update_interval = 60

    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            self.bundle_env = kwargs["bundle_env"]
        except KeyError:
            self.bundle_env = os.getenv("BUNDLE_ENV", "debug")

        path_root = os.path.abspath(os.path.dirname(__file__))
        if self.bundle_env == "debug":  # pragma: no cover
            path_root = "%s/tests" % path_root

        self.interfaces = []
        try:
            self.load(path_root)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

    def run(self):
        while True:
            sleep(self.update_interval)
            try:
                self.try_update_default(self.model.db)
            except Exception as e:
                _logger.debug(e)

    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("route", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def list_interfaces(self):
        """
        List available interfaces.
        """
        # retrieve all interfaces
        try:
            ifaces = ip.addr.interfaces()
        except:
            return {}

        # list connected interfaces
        data = []
        for iface in ifaces:
            try:
                iface_info = ip.addr.ifaddresses(iface)
            except:
                continue
            if 1 == iface_info["link"]:
                inet_ip = [inet["ip"]
                           for inet in iface_info["inet"]
                           if "" != inet["ip"]]
                if len(inet_ip):
                    data.append(iface)
        return data

    def get_default(self):
        """
        Retrieve the default gateway

        Return:
            default: dict format with "interface" and/or "gateway"
        """
        gws = netifaces.gateways()
        default = {}
        if gws['default'] != {} and netifaces.AF_INET in gws['default']:
            gw = gws['default'][netifaces.AF_INET]
        else:
            return default

        default["gateway"] = gw[0]
        default["interface"] = gw[1]
        return default

    def update_wan_info(self, interface):
        """
        Update WAN interface to default gateway's interface.

        Args:
            default: interface name
        """
        self.publish.event.put("/network/wan", data={"interface": interface})

        # TODO: modify DNS to listen `/network/wan` instead
        res = self.publish.put("/network/dns", data={"source": interface})
        if res.code != 200:
            raise RuntimeWarning(res.data["message"])

    def update_default(self, default):
        """
        Update default gateway. If updated failed, should recover to previous
        one.

        Args:
            default: dict format with "interface" required and "gateway"
                     optional.
        """
        # delete the default gateway
        if not default or ("interface" not in default and
                           "gateway" not in default):
            ip.route.delete("default")

        # change the default gateway
        # FIXME: only "gateway" without interface is also available
        # FIXME: add "secondary" default route rule
        else:
            ip.route.delete("default")
            if "gateway" in default and "interface" in default:
                ip.route.add("default", default["interface"],
                             default["gateway"])
            elif "interface" in default:
                ip.route.add("default", default["interface"])
            elif "gateway" in default:
                ip.route.add("default", "", default["gateway"])
            else:
                raise IPRouteError("Invalid default route.")

            # update DNS
            if "interface" in default:
                self.update_wan_info(default["interface"])

    def _try_update_default(self, routes):
        """
        Try to update the default gateway.

        Args:
            routes: dict format including default gateway interface and
                    secondary default gateway interface.
                    For example:
                    {
                        "default": "wwan0",
                        "secondary": "eth0"
                    }
        """
        ifaces = self.list_interfaces()
        if not ifaces:
            raise IPRouteError("Interfaces should be UP.")

        default = {}
        if routes["default"] in ifaces:
            default["interface"] = routes["default"]
        elif routes["secondary"] in ifaces:
            default["interface"] = routes["secondary"]
        else:
            self.update_default({})
            return

        # find gateway by interface
        for iface in self.interfaces:
            if iface["interface"] == default["interface"]:
                default = iface
                break

        current = self.get_default()
        if current != default:
            self.update_default(default)

    def try_update_default(self, routes):
        with _update_default_lock:
            try:
                self._try_update_default(routes)
            except IPRouteError as e:
                _logger.debug(e)

    def update_router(self, interface):
        """
        Save the interface name with its gateway and update the default
        gateway if needed.

        If gateway is not specified, use the previous value. Only delete the
        gateway when gateway attribute is empty.

        Args:
            interface: dict format with interface "name" and/or "gateway".
        """
        # update the router information
        for iface in self.interfaces:
            if iface["interface"] == interface["name"]:
                if "gateway" in interface:
                    iface["gateway"] = interface["gateway"]
                break
        else:
            iface = {}
            iface["interface"] = interface["name"]
            if "gateway" in interface:
                iface["gateway"] = interface["gateway"]
            self.interfaces.append(iface)

        # check if the default gateway need to be modified
        self.try_update_default(self.model.db)

    def set_default(self, default, is_default=True):
        """
        Update default / secondary gateway.
        """
        if is_default:
            def_type = "default"
        else:
            def_type = "secondary"

        # save the setting
        # if no interface but has gateway, do not update anything
        if "interface" in default:
            self.model.db[def_type] = default["interface"]
        elif "gateway" not in default:
            self.model.db[def_type] = ""
        self.save()

        try:
            if is_default:
                self.update_default(default)
        except Exception as e:
            # try database if failed
            try:
                self.try_update_default(self.model.db)
            except IPRouteError as e2:
                _logger.debug(
                    "Failed to recover the default gateway: {}".format(e2))
            error = "Update default gateway failed: {}".format(e)
            _logger.error(error)
            raise IPRouteError(error)

    @Route(methods="get", resource="/network/routes/interfaces")
    def _get_interfaces(self, message, response):
        """
        Get available interfaces.
        """
        return response(data=self.list_interfaces())

    @Route(methods="get", resource="/network/routes/default")
    def _get_default(self, message, response):
        """
        Get default gateway.
        """
        return response(data=self.get_default())

    put_default_schema = Schema({
        Optional("interface"): Any(str, unicode),
        Extra: object})

    @Route(methods="put", resource="/network/routes/default")
    def _put_default(self, message, response, schema=put_default_schema):
        """
        Update the default gateway, delete default gateway if data is None or
        empty.
        """
        try:
            self.set_default(message.data)
        except Exception as e:
            return response(code=404,
                            data={"message": e})
        return response(data=self.get_default())

    @Route(methods="put", resource="/network/routes/secondary")
    def _put_secondary(self, message, response, schema=put_default_schema):
        """
        Update the secondary default gateway, delete default gateway if data
        is None or empty.
        """
        try:
            self.set_default(message.data, False)
        except Exception as e:
            return response(code=404,
                            data={"message": e})
        return response(data=message.data)

    def set_router_db(self, message, response):
        """
        Update router database batch or by interface.
        """
        if type(message.data) is list:
            for iface in message.data:
                self.update_router(iface)
            return response(data=self.interfaces)
        elif type(message.data) is dict:
            self.update_router(message.data)
            return response(data=message.data)
        return response(code=400,
                        data={"message": "Wrong type of router database."})

    @Route(methods="put", resource="/network/routes/db")
    def _set_router_db(self, message, response):
        return self.set_router_db(message, response)

    @Route(methods="get", resource="/network/routes/db")
    def _get_router_db(self, message, response):
        return response(data=self.interfaces)

    @Route(methods="put", resource="/network/interface")
    def _event_router_db(self, message):
        self.update_router(message.data)
Esempio n. 28
0
class Ethernet(Sanji):
    """
    A model to handle Ethernet interfaces' configuration.

    Attributes:
        model: Ethernet interfaces' database with json format.
    """
    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            bundle_env = kwargs["bundle_env"]
        except KeyError:
            bundle_env = os.getenv("BUNDLE_ENV", "debug")

        self.path_root = os.path.abspath(os.path.dirname(__file__))
        if bundle_env == "debug":  # pragma: no cover
            self.path_root = "%s/tests" % self.path_root

        # Find all ethernet interfaces and load the configuration
        ifaces = ip.interfaces()
        ifaces = [x for x in ifaces if x.startswith("eth")]
        if 0 == len(ifaces):
            _logger.info("No interfaces to be configured.")
            self.stop()
            raise ValueError("No interfaces to be configured.")

        try:
            self.load(self.path_root, ifaces)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

        # Apply the configuration
        for iface in self.model.db:
            self.apply(iface)

    def run(self):
        for iface in self.model.db:
            self.publish.event.put("/network/interface", data=iface)

    def load(self, path, ifaces):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with the given interfaces and install them.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
            ifaces: A list of interfaces name.
        """
        self.model = ModelInitiator("ethernet", path, backup_interval=-1)
        if not self.model.db:
            raise IOError("Cannot load any configuration.")

        # Initialise the interfaces
        # TODO: 2nd iface's type is "LAN"; another is "WAN"
        if 1 == len(self.model.db) and "id" not in self.model.db[0]:
            _logger.debug("factory install")
            default_db = self.model.db.pop()
            ip_3_def = int(default_db["ip"].split(".")[2]) - 1
            for iface in ifaces:
                ifaddr = ip.ifaddresses(iface)
                db = copy.deepcopy(default_db)
                db["name"] = iface
                db["id"] = int(iface.replace("eth", "")) + 1

                ip_3 = ip_3_def + db["id"]
                db["ip"] = "192.168.%d.127" % ip_3
                db["subnet"] = "192.168.%d.0" % ip_3
                db["gateway"] = "192.168.%d.254" % ip_3

                db["currentStatus"] = ifaddr["link"]
                db["mac"] = ifaddr["mac"]
                self.model.db.append(db)
            self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def apply(self, data):
        """
        Apply the configuration to an interface.

        Args:
            data: Information for the interface to be applied (with dictionary
                format)
        """
        iface = "eth%d" % (data["id"]-1)

        ip.ifupdown(iface, True if data["enable"] else False)
        if not data["enable"]:
            return

        if data["enableDhcp"]:
            ip.ifconfig(iface, True, script="%s/hooks/dhclient-script" %
                        self.path_root)
        else:
            ip.ifconfig(iface, False, data["ip"], data["netmask"],
                        data["gateway"])

    def read(self, id, restart=False, config=True):
        """
        Read the setting for an interface.

        Args:
            id: Interface id, interface name will be eth(id+1).
        """
        for data in self.model.db:
            if data["id"] == id:
                break
        else:
            return None

        # deepcopy to prevent settings be modified
        data = copy.deepcopy(data)

        if not restart and "restart" in data:
            data.pop("restart")

        iface = "eth%d" % (data["id"]-1)
        ifaddr = ip.ifaddresses(iface)
        data["currentStatus"] = ifaddr["link"]
        data["mac"] = ifaddr["mac"]

        # """Use configuration data instead of realtime retrieving
        if True is config:
            return data

        data["ip"] = ""
        data["netmask"] = ""
        data["subnet"] = ""
        data["broadcast"] = ""
        # data["gateway"] = ""
        if ifaddr["inet"] and len(ifaddr["inet"]):
            data["ip"] = ifaddr["inet"][0]["ip"]
            data["netmask"] = ifaddr["inet"][0]["netmask"]
            if "subnet" in ifaddr["inet"][0]:
                data["subnet"] = ifaddr["inet"][0]["subnet"]
            else:
                data.pop("subnet")
            if "broadcast" in ifaddr["inet"][0]:
                data["broadcast"] = ifaddr["inet"][0]["broadcast"]
            elif "broadcast" in data:
                data.pop("broadcast")
        # """
        return data

    @staticmethod
    def schema_validate(message):
        """
        Validate the received data, ensure the schema is correct.
        """
        # TODO: ip validation
        schema = Schema({
            "id": Range(min=1),
            "enable": In(frozenset([0, 1])),
            Optional("wan"): In(frozenset([0, 1])),
            Optional("enableDhcp"): In(frozenset([0, 1])),
            Optional("ip"): Any(str, unicode),
            Optional("netmask"): Any(str, unicode),
            Optional("subnet"): Any(str, unicode),
            Optional("gateway"): Any(str, unicode),
            Optional("dns"): [Any(str, unicode)],
            Extra: object
        }, required=True)

        if not hasattr(message, "data"):
            raise KeyError("Invalid input: \"data\" attribute is required.")

        if type(message.data) is list:
            if 0 == len(message.data):
                raise KeyError("Invalid input: empty \"data\".")
            for item in message.data:
                try:
                    schema(item)
                except Exception, e:
                    raise KeyError("Invalid input: %s." % e)

        if type(message.data) is dict:
            try:
                schema(message.data)
            except Exception, e:
                raise KeyError("Invalid input: %s." % e)
Esempio n. 29
0
class IPRoute(Sanji):
    """
    A model to handle IP Route configuration.

    Attributes:
        model: database with json format.
    """

    update_interval = 60

    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            self.bundle_env = kwargs["bundle_env"]
        except KeyError:
            self.bundle_env = os.getenv("BUNDLE_ENV", "debug")

        path_root = os.path.abspath(os.path.dirname(__file__))
        if self.bundle_env == "debug":  # pragma: no cover
            path_root = "%s/tests" % path_root

        self.interfaces = []
        try:
            self.load(path_root)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

    def run(self):
        while True:
            sleep(self.update_interval)
            try:
                self.try_update_default(self.model.db)
            except Exception as e:
                _logger.debug(e)

    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("route", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def list_interfaces(self):
        """
        List available interfaces.
        """
        # retrieve all interfaces
        try:
            ifaces = ip.addr.interfaces()
        except:
            return {}

        # list connected interfaces
        data = []
        for iface in ifaces:
            try:
                iface_info = ip.addr.ifaddresses(iface)
            except:
                continue
            if 1 == iface_info["link"]:
                inet_ip = [
                    inet["ip"] for inet in iface_info["inet"]
                    if "" != inet["ip"]
                ]
                if len(inet_ip):
                    data.append(iface)
        return data

    def get_default(self):
        """
        Retrieve the default gateway

        Return:
            default: dict format with "interface" and/or "gateway"
        """
        gws = netifaces.gateways()
        default = {}
        if gws['default'] != {} and netifaces.AF_INET in gws['default']:
            gw = gws['default'][netifaces.AF_INET]
        else:
            return default

        default["gateway"] = gw[0]
        default["interface"] = gw[1]
        return default

    def update_wan_info(self, interface):
        """
        Update WAN interface to default gateway's interface.

        Args:
            default: interface name
        """
        self.publish.event.put("/network/wan", data={"interface": interface})

        # TODO: modify DNS to listen `/network/wan` instead
        res = self.publish.put("/network/dns", data={"source": interface})
        if res.code != 200:
            raise RuntimeWarning(res.data["message"])

    def update_default(self, default):
        """
        Update default gateway. If updated failed, should recover to previous
        one.

        Args:
            default: dict format with "interface" required and "gateway"
                     optional.
        """
        # delete the default gateway
        if not default or ("interface" not in default
                           and "gateway" not in default):
            ip.route.delete("default")

        # change the default gateway
        # FIXME: only "gateway" without interface is also available
        # FIXME: add "secondary" default route rule
        else:
            ip.route.delete("default")
            if "gateway" in default and "interface" in default:
                ip.route.add("default", default["interface"],
                             default["gateway"])
            elif "interface" in default:
                ip.route.add("default", default["interface"])
            elif "gateway" in default:
                ip.route.add("default", "", default["gateway"])
            else:
                raise IPRouteError("Invalid default route.")

            # update DNS
            if "interface" in default:
                self.update_wan_info(default["interface"])

    def _try_update_default(self, routes):
        """
        Try to update the default gateway.

        Args:
            routes: dict format including default gateway interface and
                    secondary default gateway interface.
                    For example:
                    {
                        "default": "wwan0",
                        "secondary": "eth0"
                    }
        """
        ifaces = self.list_interfaces()
        if not ifaces:
            raise IPRouteError("Interfaces should be UP.")

        default = {}
        if routes["default"] in ifaces:
            default["interface"] = routes["default"]
        elif routes["secondary"] in ifaces:
            default["interface"] = routes["secondary"]
        else:
            self.update_default({})
            return

        # find gateway by interface
        for iface in self.interfaces:
            if iface["interface"] == default["interface"]:
                default = iface
                break

        current = self.get_default()
        if current != default:
            self.update_default(default)

    def try_update_default(self, routes):
        with _update_default_lock:
            try:
                self._try_update_default(routes)
            except IPRouteError as e:
                _logger.debug(e)

    def update_router(self, interface):
        """
        Save the interface name with its gateway and update the default
        gateway if needed.

        If gateway is not specified, use the previous value. Only delete the
        gateway when gateway attribute is empty.

        Args:
            interface: dict format with interface "name" and/or "gateway".
        """
        # update the router information
        for iface in self.interfaces:
            if iface["interface"] == interface["name"]:
                if "gateway" in interface:
                    iface["gateway"] = interface["gateway"]
                break
        else:
            iface = {}
            iface["interface"] = interface["name"]
            if "gateway" in interface:
                iface["gateway"] = interface["gateway"]
            self.interfaces.append(iface)

        # check if the default gateway need to be modified
        self.try_update_default(self.model.db)

    def set_default(self, default, is_default=True):
        """
        Update default / secondary gateway.
        """
        if is_default:
            def_type = "default"
        else:
            def_type = "secondary"

        # save the setting
        # if no interface but has gateway, do not update anything
        if "interface" in default:
            self.model.db[def_type] = default["interface"]
        elif "gateway" not in default:
            self.model.db[def_type] = ""
        self.save()

        try:
            if is_default:
                self.update_default(default)
        except Exception as e:
            # try database if failed
            try:
                self.try_update_default(self.model.db)
            except IPRouteError as e2:
                _logger.debug(
                    "Failed to recover the default gateway: {}".format(e2))
            error = "Update default gateway failed: {}".format(e)
            _logger.error(error)
            raise IPRouteError(error)

    @Route(methods="get", resource="/network/routes/interfaces")
    def _get_interfaces(self, message, response):
        """
        Get available interfaces.
        """
        return response(data=self.list_interfaces())

    @Route(methods="get", resource="/network/routes/default")
    def _get_default(self, message, response):
        """
        Get default gateway.
        """
        return response(data=self.get_default())

    put_default_schema = Schema({
        Optional("interface"): Any(str, unicode),
        Extra: object
    })

    @Route(methods="put", resource="/network/routes/default")
    def _put_default(self, message, response, schema=put_default_schema):
        """
        Update the default gateway, delete default gateway if data is None or
        empty.
        """
        try:
            self.set_default(message.data)
        except Exception as e:
            return response(code=404, data={"message": e})
        return response(data=self.get_default())

    @Route(methods="put", resource="/network/routes/secondary")
    def _put_secondary(self, message, response, schema=put_default_schema):
        """
        Update the secondary default gateway, delete default gateway if data
        is None or empty.
        """
        try:
            self.set_default(message.data, False)
        except Exception as e:
            return response(code=404, data={"message": e})
        return response(data=message.data)

    def set_router_db(self, message, response):
        """
        Update router database batch or by interface.
        """
        if type(message.data) is list:
            for iface in message.data:
                self.update_router(iface)
            return response(data=self.interfaces)
        elif type(message.data) is dict:
            self.update_router(message.data)
            return response(data=message.data)
        return response(code=400,
                        data={"message": "Wrong type of router database."})

    @Route(methods="put", resource="/network/routes/db")
    def _set_router_db(self, message, response):
        return self.set_router_db(message, response)

    @Route(methods="get", resource="/network/routes/db")
    def _get_router_db(self, message, response):
        return response(data=self.interfaces)

    @Route(methods="put", resource="/network/interface")
    def _event_router_db(self, message):
        self.update_router(message.data)
Esempio n. 30
0
 def setUp(self):
     """
     " Prepare
     """
     os.makedirs(self.model_path)
     self.model_initaitor = ModelInitiator(self.model_name, self.model_path)
Esempio n. 31
0
 def init(self, *args, **kwargs):
     self.path_root = os.path.abspath(os.path.dirname(__file__))
     self.model = ModelInitiator("bootstrap", self.path_root)
     self.modes_config = load_mode_config(self.path_root)
     self.keeper = SanjiKeeper()
Esempio n. 32
0
class Index(Sanji):

    PUT_SCHEMA = Schema({
        Optional("time"): Timestamp,
        Optional("timezone"): All(str, Length(0, 255)),
        Optional("ntp"): {
            "enable": bool,
            "server": All(str, Length(1, 2048)),
            "interval": All(int, Range(min=60, max=60*60*24*30))
        }
    }, extra=REMOVE_EXTRA)

    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("ntp", path_root)
        self.ntp = Ntp(self.model)

    @Route(methods="get", resource="/system/time")
    def get(self, message, response):
        realtime_data = {
            "time": SysTime.get_system_time()
        }

        return response(
            data=dict(self.model.db.items() + realtime_data.items()))

    @Route(methods="get", resource="/system/zoneinfo")
    def get_zoneinfo(self, message, response):
        zoneinfo = SysTime.get_system_timezone_list()

        return response(data=dict(zoneinfo))

    @Route(methods="put", resource="/system/time", schema=PUT_SCHEMA)
    def put(self, message, response):
        rc = None
        try:
            # update ntp settings
            if "ntp" in message.data:
                rc = self.ntp.update(message.data["ntp"])
                if rc is False:
                    raise RuntimeWarning("Update ntp settings failed.")
                self.model.db["ntp"] = dict(self.model.db["ntp"].items() +
                                            message.data["ntp"].items())

            # change timezone
            if "timezone" in message.data:
                rc = SysTime.set_system_timezone(message.data["timezone"])
                if rc is False:
                    raise RuntimeWarning("Change timezone failed.")
                self.model.db["timezone"] = message.data["timezone"]

            # manual change sys time
            if "time" in message.data:
                if self.model.db["ntp"]["enable"] is True:
                    _logger.debug("NTP enabled. skipping time setup.")
                else:
                    rc = SysTime.set_system_time(message.data["time"])
                    if rc is False:
                        raise RuntimeWarning("Change system time failed.")

            if rc is None:
                return response(code=400,
                                data={"message": "No input paramters."})
            else:
                self.model.save_db()
        except Exception as e:
            _logger.debug(e, exc_info=True)
            code = 400 if not isinstance(e, RuntimeWarning) else 500
            return response(code=code, data={"message": str(e)})

        realtime_data = {
            "time": SysTime.get_system_time()
        }

        # operation successed
        return response(
            data=dict(self.model.db.items() + realtime_data.items()))
Esempio n. 33
0
class TestModelInitiatorClass(unittest.TestCase):
    """
    " Test class
    """

    model_name = "test_myself"
    model_path = "/tmp/sanji-sdk/tests/test_myself"
    model_db_folder = "/tmp/sanji-sdk/tests/test_myself/data"
    model_factory_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json.factory"
    model_backup_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json.backup"
    model_db = "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json"

    def setUp(self):
        """
        " Prepare
        """
        factory_data = {"name": "factory"}
        if not os.path.exists(self.model_db_folder):
            os.makedirs(self.model_db_folder)
        with open(self.model_factory_db, "w") as fp:
            json.dump(factory_data, fp, indent=4)

        self.model_initiator = ModelInitiator(
            self.model_name, self.model_path, backup_interval=-1)

    def tearDown(self):
        """
        " Clean up
        """
        self.model_initiator.stop_backup()
        self.model_initiator = None

        if os.path.exists(self.model_path):
            removeall(self.model_db_folder)

    def test_init(self):
        """
        " Test __init__()
        """
        # case 1: check name
        self.assertEquals(self.model_initiator.model_name, self.model_name)

        # case 2: thread
        self.model_initiator = None
        self.model_initiator = ModelInitiator(
            self.model_name, self.model_path, backup_interval=1)
        self.assertEquals(self.model_initiator.model_name, self.model_name)

    def test_db_manager(self):
        # case 1: existing
        self.model_initiator.save_db()
        self.model_initiator.db_manager()
        self.assertEqual(self.model_initiator.db_status, "existing")

        # case 2: factory
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db")as create_db:
            create_db.return_value = 1
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator = ModelInitiator(
                self.model_name, self.model_path, backup_interval=-1)

            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "factory")

        # case 3: backup
        with patch(
                "sanji.model_initiator.ModelInitiator.create_db")as create_db:
            create_db.return_value = 1
            self.model_initiator.save_db()
            self.model_initiator.backup_db()
            if os.path.exists(self.model_db):
                os.remove(self.model_db)
            self.model_initiator.db_manager()
            self.assertTrue(os.path.exists(self.model_db))
            self.assertEqual(self.model_initiator.db_status, "backup")

    def test_create_db(self):
        """
        " It should generate a factory db if there is no db.
        """
        if os.path.exists(self.model_db_folder):
            shutil.rmtree(self.model_db_folder)

        os.makedirs(self.model_db_folder)

        try:
            with open(self.model_factory_db, "a"):
                os.utime(self.model_factory_db, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        # case 1: general case
        result = self.model_initiator.create_db()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db))

        # case 2: no factory db
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        if os.path.exists(self.model_factory_db):
            os.remove(self.model_factory_db)

        with self.assertRaises(RuntimeError):
            result = self.model_initiator.create_db()

        # case 3: sql type
        self.db_type = "sql"
        with self.assertRaises(RuntimeError):
            result = self.model_initiator.create_db()

    def test_recover_db(self):
        # case 1: Check file which restore from backup db
        self.model_initiator.db = {}
        self.model_initiator.db["name"] = "backup"
        self.model_initiator.db["type"] = "json"
        self.model_initiator.save_db()
        self.model_initiator.backup_db()
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        self.assertFalse(os.path.exists(self.model_db))
        self.model_initiator.recover_db(self.model_backup_db)
        self.assertTrue(os.path.exists(self.model_db))
        # case 2: Check data which restore from backup db
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "backup", "type": "json"})

        # case 3: Check file which restore from factory db
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
        self.assertFalse(os.path.exists(self.model_db))
        self.model_initiator.recover_db(self.model_factory_db)
        self.assertTrue(os.path.exists(self.model_db))

        # case 4: Check data which restore from factory db
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "factory"})

        # case 5: no file
        try:
            self.assertRaises(
                self.model_initiator.recover_db("/tmp/1234555555.txt"))
        except IOError:
            pass
        else:
            self.fail("No file to load but pass.")

    def test_backup_db(self):
        """
        " Test backup db
        """
        if os.path.exists(self.model_backup_db):
            os.remove(self.model_backup_db)
        # case 1: Check file exist
        self.model_initiator.backup_db()
        self.assertTrue(os.path.exists(self.model_backup_db))

        # case 2: Check data
        with open(self.model_backup_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "factory"})

    def test_load_db(self):
        """
        " It should load json db as a dictionary.
        """
        # case 1: No folder
        self.model_initiator.db = None
        self.assertEqual(type(self.model_initiator.db), type(None))
        self.model_initiator.create_db()
        try:
            with open(self.model_factory_db, "a"):
                os.utime(self.model_factory_db, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        # case 2: data
        data = {"enable": 1}
        with open(self.model_factory_db, "w") as fp:
            json.dump(data, fp, indent=4)

        shutil.copyfile(self.model_factory_db, self.model_db)
        self.model_initiator.load_db()
        self.assertEqual(self.model_initiator.db, data)

        # case 3:
        if os.path.exists(self.model_db):
            os.remove(self.model_db)
            try:
                self.assertRaises(self.model_initiator.load_db())
            except Exception:
                pass

    def test_save_db(self):
        """
        " Test save db
        """
        # case 1: data of saving
        self.model_initiator.db = {}
        self.model_initiator.db["name"] = "John"
        self.model_initiator.db["age"] = 33
        self.model_initiator.save_db()
        db_data = None
        with open(self.model_db) as fp:
            db_data = json.load(fp)

        self.assertEqual(db_data, {"name": "John", "age": 33})

        # case 2: non dictionary or array type.
        self.model_initiator.db = "string type"
        rc = self.model_initiator.save_db()
        self.assertFalse(rc)

        # case 3: open with no file. (coverage)
        if os.path.exists(self.model_db_folder):
            shutil.rmtree(self.model_db_folder)

        try:
            self.assertRaises(self.model_initiator.save_db())
        except Exception:
            pass

    @patch("sanji.model_initiator.Thread")
    def test_start_backup_thread_is_not_alive(self, Thread):
        self.model_initiator._backup_thread.is_alive = Mock(return_value=False)
        Thread.return_value = MagicMock(daemon=False, start=Mock())
        self.model_initiator.start_backup()
        self.assertTrue(self.model_initiator._backup_thread.daemon)
        self.model_initiator._backup_thread.start.assert_called_once_with()

    def test_start_backup_thread_is_alive(self):
        with self.assertRaises(RuntimeError):
            self.model_initiator._backup_thread.is_alive =\
                Mock(return_value=True)
            self.model_initiator.start_backup()
        self.model_initiator.stop_backup = Mock()

    def test_stop_backup(self):
        rc = self.model_initiator.stop_backup()
        self.assertFalse(rc)
        self.model_initiator.start_backup()
        rc = self.model_initiator.stop_backup()
        self.assertTrue(rc)

        self.model_initaitor.db_type = "json"
        self.model_initaitor.factory_json_db_path = "/1231231235566"
        result = self.model_initaitor.create_db()
        self.assertFalse(result)
Esempio n. 34
0
 def init(self, *args, **kwargs):
     path_root = os.path.abspath(os.path.dirname(__file__))
     self.model = ModelInitiator("ntp", path_root)
     self.ntp = Ntp(self.model)
Esempio n. 35
0
class Index(Sanji):

    CONF_SCHEMA = Schema(
        {
            "id": int,
            Required("enable"): bool,
            Required("pdpContext"): {
                Required("static"):
                bool,
                Required("id"):
                int,
                Required("retryTimeout", default=120):
                All(int, Any(0, Range(min=10, max=86400 - 1))),
                Required("primary"): {
                    Required("apn", default="internet"):
                    All(Any(unicode, str), Length(0, 100)),
                    Optional("type", default="ipv4v6"):
                    In(frozenset(["ipv4", "ipv6", "ipv4v6"]))
                },
                Required("secondary", default={}): {
                    Optional("apn"):
                    All(Any(unicode, str), Length(0, 100)),
                    Optional("type", default="ipv4v6"):
                    In(frozenset(["ipv4", "ipv6", "ipv4v6"]))
                }
            },
            Required("pinCode", default=""): Any(Match(r"[0-9]{4,4}"), ""),
            Required("keepalive"): {
                Required("enable"):
                bool,
                Required("targetHost"):
                str,
                Required("intervalSec"):
                All(int, Any(0, Range(min=60, max=86400 - 1))),
                Required("reboot", default={
                    "enable": False,
                    "cycles": 1
                }): {
                    Required("enable", default=False):
                    bool,
                    Required("cycles", default=1):
                    All(int, Any(0, Range(min=1, max=48))),
                }
            }
        },
        extra=REMOVE_EXTRA)

    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("cellular", path_root)
        self.model.db[0] = Index.CONF_SCHEMA(self.model.db[0])

        self._dev_name = None
        self._mgr = None
        self._vnstat = None

        self.__init_monit_config(
            enable=(self.model.db[0]["enable"]
                    and self.model.db[0]["keepalive"]["enable"] and True
                    and self.model.db[0]["keepalive"]["reboot"]["enable"]
                    and True),
            target_host=self.model.db[0]["keepalive"]["targetHost"],
            iface=self._dev_name,
            cycles=self.model.db[0]["keepalive"]["reboot"]["cycles"])
        self._init_thread = Thread(name="sanji.cellular.init_thread",
                                   target=self.__initial_procedure)
        self._init_thread.daemon = True
        self._init_thread.start()

    def __initial_procedure(self):
        """
        Continuously check Cellular modem existence.
        Set self._dev_name, self._mgr, self._vnstat properly.
        """
        cell_mgmt = CellMgmt()
        wwan_node = None

        for retry in xrange(0, 4):
            if retry == 3:
                return

            try:
                wwan_node = cell_mgmt.m_info().wwan_node
                break
            except CellMgmtError:
                _logger.warning("get wwan_node failure: " + format_exc())
                cell_mgmt.power_cycle(timeout_sec=60)

        self._dev_name = wwan_node
        self.__init_monit_config(
            enable=(self.model.db[0]["enable"]
                    and self.model.db[0]["keepalive"]["enable"] and True
                    and self.model.db[0]["keepalive"]["reboot"]["enable"]
                    and True),
            target_host=self.model.db[0]["keepalive"]["targetHost"],
            iface=self._dev_name,
            cycles=self.model.db[0]["keepalive"]["reboot"]["cycles"])
        self.__create_manager()

        self._vnstat = VnStat(self._dev_name)

    def __create_manager(self):
        pin = self.model.db[0]["pinCode"]
        if "primary" in self.model.db[0]["pdpContext"]:
            pdpc_primary_apn = \
                self.model.db[0]["pdpContext"]["primary"].get(
                    "apn", "internet")
            pdpc_primary_type = \
                self.model.db[0]["pdpContext"]["primary"].get("type", "ipv4v6")
        else:
            pdpc_primary_apn = "internet"
            pdpc_primary_type = "ipv4v6"
        if "secondary" in self.model.db[0]["pdpContext"]:
            pdpc_secondary_apn = \
                self.model.db[0]["pdpContext"]["secondary"].get("apn", "")
            pdpc_secondary_type = \
                self.model.db[0]["pdpContext"]["secondary"].get(
                    "type", "ipv4v6")
        else:
            pdpc_secondary_apn = ""
            pdpc_secondary_type = "ipv4v6"
        pdpc_retry_timeout = self.model.db[0]["pdpContext"]["retryTimeout"]

        self._mgr = Manager(
            dev_name=self._dev_name,
            enabled=self.model.db[0]["enable"],
            pin=None if pin == "" else pin,
            pdp_context_static=self.model.db[0]["pdpContext"]["static"],
            pdp_context_id=self.model.db[0]["pdpContext"]["id"],
            pdp_context_primary_apn=pdpc_primary_apn,
            pdp_context_primary_type=pdpc_primary_type,
            pdp_context_secondary_apn=pdpc_secondary_apn,
            pdp_context_secondary_type=pdpc_secondary_type,
            pdp_context_retry_timeout=pdpc_retry_timeout,
            keepalive_enabled=self.model.db[0]["keepalive"]["enable"],
            keepalive_host=self.model.db[0]["keepalive"]["targetHost"],
            keepalive_period_sec=self.model.db[0]["keepalive"]["intervalSec"],
            log_period_sec=60)

        # clear PIN code if pin error
        if self._mgr.status() == Manager.Status.pin_error and pin != "":
            self.model.db[0]["pinCode"] = ""
            self.model.save_db()

        self._mgr.set_update_network_information_callback(
            self._publish_network_info)

        self._mgr.start()

    def __init_completed(self):
        if self._init_thread is None:
            return True

        self._init_thread.join(0)
        if self._init_thread.is_alive():
            return False

        self._init_thread = None
        return True

    def __init_monit_config(self,
                            enable=False,
                            target_host="8.8.8.8",
                            iface="",
                            cycles=1):
        if enable is False:
            rm("-rf", "/etc/monit/conf.d/keepalive")
            service("monit", "restart")
            return

        ifacecmd = "" if iface == "" or iface is None \
                   else "-I {}".format(iface)
        config = """check program ping-test with path "/bin/ping {target_host} {ifacecmd} -c 3 -W 20"
    if status != 0
    then exec "/bin/bash -c '/usr/sbin/cell_mgmt power_off force && /bin/sleep 5 && /sbin/reboot -i -f -d'"
    every {cycles} cycles
"""  # noqa
        with open("/etc/monit/conf.d/keepalive", "w") as f:
            f.write(
                config.format(target_host=target_host,
                              ifacecmd=ifacecmd,
                              cycles=cycles))
        service("monit", "restart")

    @Route(methods="get", resource="/network/cellulars")
    def get_list(self, message, response):
        if not self.__init_completed():
            return response(code=200, data=[])

        if (self._dev_name is None or self._mgr is None
                or self._vnstat is None):
            return response(code=200, data=[])

        return response(code=200, data=[self._get()])

    @Route(methods="get", resource="/network/cellulars/:id")
    def get(self, message, response):
        if not self.__init_completed():
            return response(code=400, data={"message": "resource not exist"})

        id_ = int(message.param["id"])
        if id_ != 1:
            return response(code=400, data={"message": "resource not exist"})

        return response(code=200, data=self._get())

    PUT_SCHEMA = CONF_SCHEMA

    @Route(methods="put", resource="/network/cellulars/:id", schema=PUT_SCHEMA)
    def put(self, message, response):
        if not self.__init_completed():
            return response(code=400, data={"message": "resource not exist"})

        id_ = int(message.param["id"])
        if id_ != 1:
            return response(code=400, data={"message": "resource not exist"})

        _logger.info(str(message.data))

        data = Index.PUT_SCHEMA(message.data)
        data["id"] = id_

        _logger.info(str(data))

        # always use the 1st PDP context for static
        if data["pdpContext"]["static"] is True:
            data["pdpContext"]["id"] = 1

        # since all items are required in PUT,
        # its schema is identical to cellular.json
        self.model.db[0] = data
        self.model.save_db()

        if self._mgr is not None:
            self._mgr.stop()
            self._mgr = None

        self.__create_manager()
        self.__init_monit_config(
            enable=(self.model.db[0]["enable"]
                    and self.model.db[0]["keepalive"]["enable"] and True
                    and self.model.db[0]["keepalive"]["reboot"]["enable"]
                    and True),
            target_host=self.model.db[0]["keepalive"]["targetHost"],
            iface=self._dev_name,
            cycles=self.model.db[0]["keepalive"]["reboot"]["cycles"])

        # self._get() may wait until start/stop finished
        return response(code=200, data=self.model.db[0])

    def _get(self):
        name = self._dev_name
        if name is None:
            name = "n/a"

        config = self.model.db[0]

        status = self._mgr.status()
        sinfo = self._mgr.static_information()
        cinfo = self._mgr.cellular_information()
        ninfo = self._mgr.network_information()
        try:
            pdpc_list = self._mgr.pdp_context_list()
        except CellMgmtError:
            pdpc_list = []

        try:
            self._vnstat.update()
            usage = self._vnstat.get_usage()

        except VnStatError:
            usage = {"txkbyte": -1, "rxkbyte": -1}

        # clear PIN code if pin error
        if (config["pinCode"] != "" and status == Manager.Status.pin):
            config["pinCode"] = ""

            self.model.db[0] = config
            self.model.save_db()

        config["pdpContext"]["list"] = pdpc_list

        return {
            "id": config["id"],
            "name": name,
            "mode": "n/a" if cinfo is None else cinfo.mode,
            "signal": {
                "csq": 0,
                "rssi": 0,
                "ecio": 0.0
            } if cinfo is None else {
                "csq": cinfo.signal_csq,
                "rssi": cinfo.signal_rssi_dbm,
                "ecio": cinfo.signal_ecio_dbm
            },
            "operatorName": "n/a" if cinfo is None else cinfo.operator,
            "lac": "n/a" if cinfo is None else cinfo.lac,
            "tac": "n/a" if cinfo is None else cinfo.tac,
            "nid": "n/a" if cinfo is None else cinfo.nid,
            "cellId": "n/a" if cinfo is None else cinfo.cell_id,
            "bid": "n/a" if cinfo is None else cinfo.bid,
            "imsi": "n/a" if sinfo is None else sinfo.imsi,
            "iccId": "n/a" if sinfo is None else sinfo.iccid,
            "imei": "n/a" if sinfo is None else sinfo.imei,
            "pinRetryRemain":
            (-1 if sinfo is None else sinfo.pin_retry_remain),
            "status": status.name,
            "ip": "n/a" if ninfo is None else ninfo.ip,
            "netmask": "n/a" if ninfo is None else ninfo.netmask,
            "gateway": "n/a" if ninfo is None else ninfo.gateway,
            "dns": [] if ninfo is None else ninfo.dns_list,
            "usage": {
                "txkbyte": usage["txkbyte"],
                "rxkbyte": usage["rxkbyte"]
            },
            "enable": config["enable"],
            "pdpContext": config["pdpContext"],
            "pinCode": config["pinCode"],
            "keepalive": {
                "enable": config["keepalive"]["enable"],
                "targetHost": config["keepalive"]["targetHost"],
                "intervalSec": config["keepalive"]["intervalSec"],
                "reboot": {
                    "enable": config["keepalive"]["reboot"]["enable"],
                    "cycles": config["keepalive"]["reboot"]["cycles"]
                }
            }
        }

    def _publish_network_info(self, nwk_info):

        name = self._dev_name
        if name is None:
            _logger.error("device name not available")
            return

        data = {
            "name": name,
            "wan": True,
            "type": "cellular",
            "mode": "dhcp",
            "status": nwk_info.status,
            "ip": nwk_info.ip,
            "netmask": nwk_info.netmask,
            "gateway": nwk_info.gateway,
            "dns": nwk_info.dns_list
        }
        _logger.info("publish network info: " + str(data))
        self.publish.event.put("/network/interfaces/{}".format(name),
                               data=data)
Esempio n. 36
0
class Index(Sanji):

    HOSTNAME_SCHEMA = Schema({
        Required("hostname"): All(Any(unicode, str), Length(1, 255))
    }, extra=REMOVE_EXTRA)

    PASSWORD_SCHEMA = Schema({
        Required("password"): All(Any(unicode, str), Length(1, 255))
    }, extra=REMOVE_EXTRA)

    GPS_SCHEMA = Schema({
        "lat": Any(int, float),
        "lng": Any(int, float),
    }, extra=REMOVE_EXTRA)

    ALIASNAME_SCHEMA = Schema(All(Any(unicode, str), Length(0, 255)))

    PROPERTIES_SCHEMA = {
        "aliasName": ALIASNAME_SCHEMA,
        "gps": GPS_SCHEMA
    }

    UPDATE_PROPERTY_SCHEMA = Schema({
        "data": Any(list, dict, str, unicode, int, float)
    }, extra=REMOVE_EXTRA)

    def init(self, *args, **kwargs):
        path_root = os.path.abspath(os.path.dirname(__file__))
        self.status = status.Status(name="status", path=path_root)
        self.properties = ModelInitiator(
            model_name="properties", model_path=path_root)

        # Check aliasName
        if self.properties.db.get("aliasName", "$ModelName") == "$ModelName":
            self.set_alias()

    def set_alias(self):
        try:
            version = sh.pversion()
            self.properties.db["aliasName"] = version.split()[0]
        except Exception:
            self.properties.db["aliasName"] = "ThingsPro"
        self.properties.save_db()

    @Route(methods="get", resource="/system/status")
    def get_status(self, message, response):
        if message.query.get("fields") is None:
            return response(
                data={
                    "hostname": self.status.get_hostname(),
                    "version": self.status.get_product_version(),
                    "uptimeSec": self.status.get_uptime(),
                    "cpuUsage": self.status.get_cpu_usage(),
                    "memoryUsage": self.status.get_memory_usage(),
                    "memory": self.status.get_memory(),
                    "disks": self.status.get_disks()
                }
            )

        fields = [_.strip() for _ in message.query.get("fields").split(',')]
        data = {}
        if "hostname" in fields:
            data["hostname"] = self.status.get_hostname()
        if "version" in fields:
            data["version"] = self.status.get_product_version()
        if "uptimeSec" in fields:
            data["uptimeSec"] = self.status.get_uptime()
        if "cpuUsage" in fields:
            data["cpuUsage"] = self.status.get_cpu_usage()
        if "memoryUsage" in fields:
            data["memoryUsage"] = self.status.get_memory_usage()
        if "memory" in fields:
            data["memory"] = self.status.get_memory()
        if "disks" in fields:
            data["disks"] = self.status.get_disks()

        return response(data=data)

    @Route(methods="put", resource="/system/status")
    def put_status(self, message, response, schema=HOSTNAME_SCHEMA):
        self.status.set_hostname(message.data['hostname'])
        return response(data=message.data)

    @Route(methods="get", resource="/network/interfaces")
    def get_net_interface(self, message, response):
        ifaces = self.status.get_net_interfaces()
        return response(data=ifaces)

    @Route(methods="post", resource="/system/syslog")
    def post_syslog(self, message, response):
        output = status.tar_syslog_files(
            "/run/shm/syslog-%s.tar.gz" %
            (datetime.datetime.now().strftime("%Y%m%d%H%M")))
        headers = message.data.get("headers", {})
        r = requests.post(
            message.data["url"],
            files={output: open(output, "rb")},
            headers=headers,
            verify=False
        )

        if r.status_code != requests.codes.ok:
            return response(
                code=r.status_code,
                data={"message": "Can't upload config."}
            )

        sh.rm("-rf", sh.glob("/run/shm/syslog-*.tar.gz"))
        resp = r.json()
        if "url" not in resp:
            return response(
                code=500, data={"message": "Can't get file link."})

        return response(data={"url": resp["url"]})

    @Route(methods="post", resource="/system/reboot")
    def post_reboot(self, message, response):
        response()
        sleep(3)
        self.status.reboot()

    @Route(methods="put", resource="/system/password", schema=PASSWORD_SCHEMA)
    def post_passwd(self, message, response):
        set_password(message.data["password"])
        return response()

    @Route(methods="get", resource="/system/properties")
    def get_properties(self, message, response):
        return response(data=self.properties.db)

    @Route(methods="get", resource="/system/properties/:key")
    def get_property(self, message, response):
        val = self.properties.db.get(message.param["key"], None)
        if val is None:
            return response(code=404)
        return response(data=val)

    @Route(methods="put", resource="/system/properties/:key",
           schema=UPDATE_PROPERTY_SCHEMA)
    def put_property(self, message, response):
        key = message.param["key"]
        if key not in Index.PROPERTIES_SCHEMA:
            return response(code=400, data={"message": "wrong key."})
        data = Index.PROPERTIES_SCHEMA.get(key)(message.data["data"])
        self.properties.db[key] = data
        self.properties.save_db()
        return response(data=self.properties.db[key])

    @Route(methods="get", resource="/mxc/system/equipments")
    def get_system_equipments(self, message, response):
        equs = [
            {
                "equipmentName": "SYSTEM",
                "equipmentTags": [
                    {
                        "name": "cpu_usage",
                        "dataType": "float64",
                        "access": "ro",
                        "size": 8,
                        "description": "CPU Usage"
                    },
                    {
                        "name": "memory_usage",
                        "dataType": "float64",
                        "access": "ro",
                        "size": 8,
                        "description": "Memory Usage"
                    },
                    {
                        "name": "disk_usage",
                        "dataType": "float64",
                        "access": "ro",
                        "size": 8,
                        "description": "Disk Usage"
                    }
                ]
            }
        ]
        return response(data=equs)
Esempio n. 37
0
class IPRoute(Sanji):
    """
    A model to handle IP Route configuration.

    Attributes:
        model: database with json format.
    """

    update_interval = 60

    def init(self, *args, **kwargs):
        try:  # pragma: no cover
            self.bundle_env = kwargs["bundle_env"]
        except KeyError:
            self.bundle_env = os.getenv("BUNDLE_ENV", "debug")

        self._path_root = os.path.abspath(os.path.dirname(__file__))
        if self.bundle_env == "debug":  # pragma: no cover
            self._path_root = "%s/tests" % self._path_root

        self.interfaces = {}
        try:
            self.load(self._path_root)
        except:
            self.stop()
            raise IOError("Cannot load any configuration.")

        # find correct interface if shell command is required
        self._cmd_regex = re.compile(r"\$\(([\S\s]+)\)")
        self._routes = self._get_routes()

    def _get_routes(self):
        routes = []
        for iface in self.model.db:
            match = self._cmd_regex.match(iface)
            if not match:
                routes.append(iface)
                continue
            try:
                with open("{}/iface_cmd.sh".format(self._path_root), "w") as f:
                    f.write(match.group(1))
                _iface = sh.sh("{}/iface_cmd.sh".format(self._path_root))
                routes.append(str(_iface).rstrip())
            except Exception as e:
                _logger.debug(e)
        return routes

    def run(self):
        while True:
            sleep(self.update_interval)
            try:
                self.try_update_default(self._routes)
            except Exception as e:
                _logger.debug(e)

    def load(self, path):
        """
        Load the configuration. If configuration is not installed yet,
        initialise them with default value.

        Args:
            path: Path for the bundle, the configuration should be located
                under "data" directory.
        """
        self.model = ModelInitiator("route", path, backup_interval=-1)
        if self.model.db is None:
            raise IOError("Cannot load any configuration.")
        self.save()

    def save(self):
        """
        Save and backup the configuration.
        """
        self.model.save_db()
        self.model.backup_db()

    def list_interfaces(self):
        """
        List available interfaces.
        """
        # retrieve all interfaces
        try:
            ifaces = ip.addr.interfaces()
        except:
            return {}

        # list connected interfaces
        data = []
        for iface in ifaces:
            try:
                iface_info = ip.addr.ifaddresses(iface)
            except:
                continue
            if iface_info["link"] is True:
                inet_ip = [inet["ip"]
                           for inet in iface_info["inet"]
                           if "" != inet["ip"]]
                if len(inet_ip) and \
                        (iface in self.interfaces and
                         self.interfaces[iface]["status"] is True and
                         self.interfaces[iface]["wan"] is True):
                    data.append(iface)
        return data

    def get_default(self):
        """
        Retrieve the default gateway

        Return:
            default: dict format with "interface" and/or "gateway"
        """
        gws = netifaces.gateways()
        default = {}
        if gws['default'] != {} and netifaces.AF_INET in gws['default']:
            gw = gws['default'][netifaces.AF_INET]
        else:
            return default

        default["wan"] = True
        default["status"] = True
        default["gateway"] = gw[0]
        default["interface"] = gw[1]
        return default

    def update_wan_info(self, interface):
        """
        Update WAN interface to default gateway's interface.

        Args:
            default: interface name
        """
        self.publish.event.put("/network/wan", data={"interface": interface})

    def update_default(self, default):
        """
        Update default gateway. If updated failed, should recover to previous
        one.

        Args:
            default: dict format with "interface" required and "gateway"
                     optional.
        """
        # delete the default gateway
        if not default or ("interface" not in default and
                           "gateway" not in default):
            ip.route.delete("default")

        # change the default gateway
        # FIXME: only "gateway" without interface is also available
        # FIXME: add "secondary" default route rule
        else:
            ip.route.delete("default")
            if "gateway" in default and "interface" in default:
                ip.route.add("default", default["interface"],
                             default["gateway"])
            elif "interface" in default:
                ip.route.add("default", default["interface"])
            elif "gateway" in default:
                ip.route.add("default", "", default["gateway"])
            else:
                raise IPRouteError("Invalid default route.")

            # update DNS
            if "interface" in default:
                self.update_wan_info(default["interface"])

    def _try_update_default(self, routes):
        """
        Try to update the default gateway.

        Args:
            routes: array format of default gateway list with priority.
                    For example:
                    ["wwan0", "eth0"]
        """
        ifaces = self.list_interfaces()
        if not ifaces:
            # FIXME: keep or clean?
            # self.update_default({})
            raise IPRouteError("Interfaces should be UP.")

        default = {}
        for iface in routes:
            if iface in ifaces:
                default["interface"] = iface
                break
        else:
            self.update_default({})
            return

        # find gateway by interface
        default.update(self.interfaces[default["interface"]])

        current = self.get_default()
        if current.get("interface", "") != default.get("interface", "") or \
                current.get("gateway", "") != default.get("gateway", ""):
            self.update_default(default)

    def try_update_default(self, routes):
        with _update_default_lock:
            try:
                self._try_update_default(routes)
            except IPRouteError as e:
                _logger.debug(e)

    def update_router(self, iface):
        """
        Save the interface name with its gateway and update the default
        gateway if needed.

        If gateway is not specified, use the previous value. Only delete the
        gateway when gateway attribute is empty.

        Args:
            interface: dict format with interface "name" and/or "gateway".
        """
        if "status" not in iface:
            iface["status"] = True
        if "wan" not in iface:
            iface["wan"] = True

        # update the router information
        if iface["name"] not in self.interfaces:
            self.interfaces[iface["name"]] = {}
        self.interfaces[iface["name"]]["status"] = iface["status"]
        self.interfaces[iface["name"]]["wan"] = iface["wan"]
        if "gateway" in iface:
            self.interfaces[iface["name"]]["gateway"] = iface["gateway"]

        # check if the default gateway need to be modified
        self.try_update_default(self._routes)

    def get_default_routes(self):
        """
        Get default gateway list.
        """
        return self._routes

    def set_default_routes(self, defaults):
        """
        Update default gateway list.
        """
        # save the setting
        # if no interface but has gateway, do not update anything
        self.model.db = defaults
        self.save()
        self._routes = self._get_routes()

        try:
            self.update_default(defaults)
        except Exception as e:
            # try database if failed
            try:
                self.try_update_default(self._routes)
            except IPRouteError as e2:
                _logger.debug(
                    "Failed to recover the default gateway: {}".format(e2))
            error = "Update default gateway failed: {}".format(e)
            _logger.error(error)
            raise IPRouteError(error)

    @Route(methods="get", resource="/network/routes/default")
    def _get_default(self, message, response):
        """
        Get default gateway and priority list.
        """
        data = self.get_default()
        if data is None:
            data = {}
        data["priorityList"] = self.get_default_routes()
        return response(data=data)

    put_default_schema = Schema({
        Required("priorityList"): [Any(str, unicode, Length(1, 255))]
    }, extra=REMOVE_EXTRA)

    @Route(methods="put", resource="/network/routes/default")
    def _put_default_routes(self, message, response,
                            schema=put_default_schema):
        """
        Update the default gateway, delete default gateway if data is None or
        empty.
        """
        try:
            self.set_default_routes(message.data["priorityList"])
        except Exception as e:
            return response(code=404,
                            data={"message": e})

        data = {}
        data["priorityList"] = self.get_default_routes()
        return response(data=data)

    def set_router_db(self, message, response):
        """
        Update router database batch or by interface.
        """
        if type(message.data) is list:
            for iface in message.data:
                self.update_router(iface)
            return response(data=self.interfaces)
        elif type(message.data) is dict:
            self.update_router(message.data)
            return response(data=message.data)
        return response(code=400,
                        data={"message": "Wrong type of router database."})

    @Route(methods="put", resource="/network/routes/db")
    def _set_router_db(self, message, response):
        return self.set_router_db(message, response)

    @Route(methods="get", resource="/network/routes/db")
    def _get_router_db(self, message, response):
        return response(data=self.interfaces)

    @Route(methods="put", resource="/network/interfaces/:name")
    def _event_router_db(self, message):
        message.data["name"] = message.param["name"]
        self.update_router(message.data)
Esempio n. 38
0
class TestModelInitiatorClass(unittest.TestCase):
    """
    " Test class
    """

    model_name = "test_myself"
    model_path = "/tmp/sanji-sdk/tests/test_myself"
    model_db_folder = "/tmp/sanji-sdk/tests/test_myself/data"
    model_factory_db = \
        "/tmp/sanji-sdk/tests/test_myself/data/test_myself.factory.json"
    model_db = "/tmp/sanji-sdk/tests/test_myself/data/test_myself.json"

    def setUp(self):
        """
        " Prepare
        """
        os.makedirs(self.model_path)
        self.model_initaitor = ModelInitiator(self.model_name, self.model_path)

    def tearDown(self):
        """
        " Clean up
        """
        if os.path.exists(self.model_path):
            shutil.rmtree(self.model_path)

        self.model_initaitor = None

    def test_init(self):
        """
        " Test __init__()
        """
        self.assertEquals(self.model_initaitor.model_name, self.model_name)

    def test_mkdir(self):
        """
        " It Should generate a data folder.
        """
        result = self.model_initaitor.mkdir()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db_folder))

    def test_create_db(self):
        """
        " It should generate a factory db.
        """
        self.model_initaitor.mkdir()
        try:
            with open(self.model_initaitor.factory_json_db_path, 'a'):
                os.utime(self.model_initaitor.factory_json_db_path, None)
        except Exception:
            self.fail("Maybe there is no folder to create file.")

        result = self.model_initaitor.create_db()
        self.assertTrue(result)
        self.assertTrue(os.path.exists(self.model_db))

        self.model_initaitor.db_type = "sql"
        result = self.model_initaitor.create_db()
        self.assertFalse(result)

        self.model_initaitor.db_type = "json"
        self.model_initaitor.factory_json_db_path = "/1231231235566"
        result = self.model_initaitor.create_db()
        self.assertFalse(result)
Esempio n. 39
0
class Model(object):

    def __init__(self, name, path, schema=None, model_cls=dict):
        self.model_cls = model_cls
        self.schema = schema

        if schema is not None and not isinstance(schema, Schema):
            raise TypeError("schema should be instance of voluptuous.Schema")

        if not issubclass(model_cls, dict):
            raise TypeError("model_cls should be derivative dict class")

        self.model = ModelInitiator(
            model_name=name,
            model_path=path
        )
        self._batch = ModelBatch(self.model)

    def batch(self):
        return self._batch

    def _cast_model(self, obj):
        return self.model_cls(obj)

    @property
    def maxId(self):
        """int: current max id of objects"""
        if len(self.model.db) == 0:
            return 0

        return max(map(lambda obj: obj["id"], self.model.db))

    def validation(self, instance):
        """Valid input instance is vaild or not
            Args:
                Object: input instance
            Returns:
                Object: Instance after vaildation or original instance
                        if schema is None
            Raises:
                Error: If vaildation failed
        """
        if self.schema is None:
            return instance

        return self.schema(instance)

    def add(self, obj):
        """Add a object
            Args:
                Object: Object will be added
            Returns:
                Object: Object with id
            Raises:
                TypeError: If add object is not a dict
                MultipleInvalid: If input object is invaild
        """
        if not isinstance(obj, dict):
            raise TypeError("Add object should be a dict object")
        obj = self.validation(obj)
        obj["id"] = self.maxId + 1
        obj = self._cast_model(obj)
        self.model.db.append(obj)

        if not self._batch.enable.is_set():
            self.model.save_db()
        return obj

    def get(self, id):
        """Get a object by id
            Args:
                id (int): Object id

            Returns:
                Object: Object with specified id
                None: If object not found
        """
        for obj in self.model.db:
            if obj["id"] == id:
                return self._cast_model(obj)

        return None

    def remove(self, id):
        """Remove a object by id
            Args:
                id (int): Object's id should be deleted
            Returns:
                len(int): affected rows
        """
        before_len = len(self.model.db)
        self.model.db = [t for t in self.model.db if t["id"] != id]
        if not self._batch.enable.is_set():
            self.model.save_db()
        return before_len - len(self.model.db)

    def removeAll(self):
        """Remove all objects
            Returns:
                len(int): affected rows
        """
        before_len = len(self.model.db)
        self.model.db = []
        if not self._batch.enable.is_set():
            self.model.save_db()
        return before_len - len(self.model.db)

    def update(self, id, newObj):
        """Update a object
            Args:
                id (int): Target Object ID
                newObj (object): New object will be merged into original object
            Returns:
                Object: Updated object
                None: If specified object id is not found
                MultipleInvalid: If input object is invaild
        """
        newObj = self.validation(newObj)
        for obj in self.model.db:
            if obj["id"] != id:
                continue

            newObj.pop("id", None)
            obj.update(newObj)
            obj = self._cast_model(obj)
            if not self._batch.enable.is_set():
                self.model.save_db()
            return obj

        return None

    def set(self, id, newObj):
        """Set a object
            Args:
                id (int): Target Object ID
                newObj (object): New object will be set
            Returns:
                Object: New object
                None: If specified object id is not found
                MultipleInvalid: If input object is invaild
        """
        newObj = self.validation(newObj)
        for index in xrange(0, len(self.model.db)):
            if self.model.db[index]["id"] != id:
                continue

            newObj["id"] = id
            self.model.db[index] = self._cast_model(newObj)
            if not self._batch.enable.is_set():
                self.model.save_db()
            return self.model.db[index]

        return None

    def getAll(self):
        """Get all objects
            Returns:
                List: list of all objects
        """
        objs = []
        for obj in self.model.db:
            objs.append(self._cast_model(obj))

        return objs
Esempio n. 40
0
 def init(self, *args, **kwargs):
     path_root = os.path.abspath(os.path.dirname(__file__))
     self.model = ModelInitiator("ntp", path_root)
     self.ntp = Ntp(self.model)
Esempio n. 41
0
class Index(Sanji):

    def init(self, *args, **kwargs):
        self.path_root = os.path.abspath(os.path.dirname(__file__))
        self.model = ModelInitiator("bootstrap", self.path_root)
        self.modes_config = load_mode_config(self.path_root)
        self.keeper = SanjiKeeper()

    def start_keeper(self):
        bundles_home = os.getenv(
            "BUNDLES_HOME", os.path.dirname(__file__) +
            "/tests/mock_bundles/")
        _logger.info("enableMode: %s" % self.model.db["enableMode"])
        watchdog_thread = Thread(target=watchdog, args=[self.keeper])
        watchdog_thread.daemon = True
        watchdog_thread.start()
        self.keeper.start(
            bundles_home,
            self.modes_config[self.model.db["enableMode"]].get(
                "omittedBundleNames", []))

    def run(self):
        if self.model.db["enableMode"] not in self.modes_config:
            _logger.info("enableMode is not set. Waitting...")
            return

        self.start_keeper()

    def before_stop(self):
        if self.keeper:
            self.keeper.stop()

    @Route(resource="/system/sanjikeeper", methods="get")
    def get(self, message, response):
        response(
            data=[meta.instance.bundle.profile for meta
                  in self.keeper.running_bundles.itervalues()])

    @Route(resource="/system/mode", methods="get")
    def get_system_mode(self, message, response):
        response(data=self.model.db)

    @Route(resource="/system/mode", methods="put")
    def put_system_mode(
            self, message, response, schema=_SYSTEM_MODE_SCHEMA):
        mode_name = message.data["enableMode"]
        if mode_name not in self.modes_config:
            response(code=400, data={"message": "Mode is not exist."})
            return

        if self.model.db["enableMode"] != "none":
            response(
                code=400,
                data={"message":
                      "Can't change system mode. Please reset to default."})
            return

        self.model.db["enableMode"] = mode_name
        self.model.save_db()
        self.start_keeper()
        response(data=self.model.db)