コード例 #1
0
class DataUsageCheck(object):
    """
    Establish global variables.

    Set rate shaping values (in Kbps) for 70, 80, 90 & 100% rate tiers.
    e.g. minbwup_70 & minbwdown_70 refers to upload & download at 70%
    
    Each of the rate tiers have a default throttling limit set below:
    70% - 6000Kbps Tx/Rx
    80% - 3000Kbps Tx/Rx
    90% - 1500Kbps Tx/Rx
    100% - 600Kbps Tx/Rx
    """

    minbwup_70 = 6000
    minbwdown_70 = 6000
    minbwup_80 = 3000
    minbwdown_80 = 3000
    minbwup_90 = 1500
    minbwdown_90 = 1500
    minbwup_100 = 600
    minbwdown_100 = 600
    STATUS_DEVS_PATH = '/status/wan/devices'
    STATUS_DATACAP_PATH = '/status/wan/datacap'
    CFG_RULES2_PATH = '/config/wan/rules2'

    def __init__(self):
        self.cp = EventingCSClient(app_name)

    def find_modems(self):
        while True:
            devs = self.cp.get(self.STATUS_DEVS_PATH)
            modems_list = [x for x in devs if x.startswith('mdm-')]
            self.cp.log(f'modems_list: {modems_list}')
            num_modems = len(modems_list)
            if not num_modems:
                self.cp.log('No Modems found at all yet')
                time.sleep(10)
                continue
            else:
                return modems_list

    def find_modem_profiles(self):
        wan_ifcs = self.cp.get(self.CFG_RULES2_PATH)
        modem_profiles_list = [
            x['_id_'] for x in wan_ifcs
            if x['trigger_string'].startswith('type|is|mdm')
        ]
        self.cp.log(f'modem_profiles_list: {modem_profiles_list}')
        return modem_profiles_list

    def reset_throttle(self, modem_profiles_list, monthlyreset):
        for mdm in modem_profiles_list:
            if monthlyreset:
                self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                               '/bandwidth_egress')
                self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                               '/bandwidth_ingress')
            else:
                if 'bandwidth_egress' in self.cp.get(self.CFG_RULES2_PATH +
                                                     '/' + mdm):
                    self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                                   '/bandwidth_egress')
                if 'bandwidth_ingress' in self.cp.get(self.CFG_RULES2_PATH +
                                                      '/' + mdm):
                    self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                                   '/bandwidth_ingress')
        self.cp.put('config/qos/enabled', False)
        if monthlyreset:
            self.cp.log(
                'Monthly data usage reset - disabling reduced LTE data rate')
            message = (
                f'Monthly data usage reset - disabling reduced LTE data rate '
                f'for {self.system_id} - {self.product_name} - Router ID: '
                f'{self.router_id}')
            self.cp.alert(message)

    def set_throttle(self, modem_profiles_list, minbwup, minbwdown, tierset):
        for mdm in modem_profiles_list:
            self.cp.put(self.CFG_RULES2_PATH + '/' + mdm + '/bandwidth_egress',
                        minbwup)
            self.cp.put(
                self.CFG_RULES2_PATH + '/' + mdm + '/bandwidth_ingress',
                minbwdown)
        self.cp.put('config/qos/enabled', True)
        self.cp.log('Exceeded monthly data usage threshold - ' + str(tierset) +
                    '% tier - reducing LTE data rate')
        message = (
            f'Exceeded monthly data usage threshold - reducing LTE data rate '
            f'for {self.system_id} - {self.product_name} - Router ID: '
            f'{self.router_id}')
        self.cp.alert(message)

    def run(self):
        # Get info from router to populate description field in NCM
        # alert message
        self.product_name = self.cp.get('/status/product_info/product_name')
        self.system_id = self.cp.get('/config/system/system_id')
        self.router_id = self.cp.get('status/ecm/client_id')
        # Retrieve list of modems and their profiles
        modems_list = [str(x.split('-')[1]) for x in self.find_modems()]
        modem_profiles_list = self.find_modem_profiles()
        # Reset any throttling to account for router reboots.  If a
        # data cap alert is still active during the monthly cycle, the
        # appropriate rate shaping will be re-applied
        monthlyreset = False
        self.reset_throttle(modem_profiles_list, monthlyreset)
        time.sleep(5)

        currtierset = 0

        while True:
            if self.cp.get(self.STATUS_DATACAP_PATH + '/completed_alerts/'):
                alerts = self.cp.get(self.STATUS_DATACAP_PATH +
                                     '/completed_alerts/')
                limitreached = 0
                tierset = 0
                for indalert in alerts:
                    for modem in modems_list:
                        if (indalert['alerts']
                                and indalert['rule_id'] == modem + '-monthly'):
                            if 'email_alert' in indalert['alerts']:
                                limitreached += 1
                                tierset = 100
                                minbwup = self.minbwup_100
                                minbwdown = self.minbwdown_100
                                continue
                            elif 'early_email-90.0' in indalert['alerts']:
                                limitreached += 1
                                tierset = 90
                                minbwup = self.minbwup_90
                                minbwdown = self.minbwdown_90
                                continue
                            elif 'early_email-80.0' in indalert['alerts']:
                                limitreached += 1
                                tierset = 80
                                minbwup = self.minbwup_80
                                minbwdown = self.minbwdown_80
                                continue
                            elif 'early_email-70.0' in indalert['alerts']:
                                limitreached += 1
                                tierset = 70
                                minbwup = self.minbwup_70
                                minbwdown = self.minbwdown_70
                                continue
                if limitreached > 0 and currtierset != tierset:
                    currtierset = tierset
                    self.set_throttle(modem_profiles_list, minbwup, minbwdown,
                                      currtierset)
                elif limitreached == 0 and currtierset > 0:
                    currtierset = 0
                    monthlyreset = True
                    self.reset_throttle(modem_profiles_list, monthlyreset)
            elif currtierset > 0:
                currtierset = 0
                monthlyreset = True
                self.reset_throttle(modem_profiles_list, monthlyreset)
            time.sleep(10)
コード例 #2
0
ファイル: hspt.py プロジェクト: shawnrausch/sdk-samples
"""
Copyright (c) 2016 CradlePoint, Inc. <www.cradlepoint.com>.  All rights
reserved.

This file contains confidential information of CradlePoint, Inc. and your use
of this file is subject to the CradlePoint Software License Agreement
distributed with this file. Unauthorized reproduction or distribution of this
file is subject to civil and criminal penalties.
"""
import os
import sys
from csclient import EventingCSClient


def mkroutes(route, directory):
    location = "%s/%s" % (os.getcwd(), directory)
    route_map.append((route, location))


cp = EventingCSClient('hspt')

# Build route maps for / and /resources
route_map = []
sys.path.append('.')
mkroutes('/(.*)', '')
mkroutes('/resources/(.*)', 'resources/')

value = {'action': 'start', 'routes': route_map, 'server': 'hotspotServer'}
cp.put('/control/system/httpserver', value)
コード例 #3
0
                        """If standby WWAN found, keep checking for an active one"""
                        continue

                    elif 'error' in summary:
                        continue
            """If no active/standby WANs are found, show offline"""
            if is_available_wwan == 0 and is_configured_wwan == 1:
                ports_status += "WWAN: ⚫️ "

            ipverifys = cp.get('/status/ipverify')
            if ipverifys:
                ports_status += "IPV:"

                for ipverify in ipverifys:
                    testpass = cp.get(
                        '/status/ipverify/{}/pass'.format(ipverify))
                    if testpass:
                        ports_status += " 🟢 "
                    else:
                        ports_status += " ⚫️ "
            """Write string to description field"""
            if DEBUG:
                cp.log("WRITING DESCRIPTION")
                cp.log(ports_status)
            cp.put('config/system/desc', ports_status)

    except Exception as err:
        cp.log("Failed with exception={} err={}".format(type(err), str(err)))
    """Wait 5 seconds before checking again"""
    time.sleep(5)
コード例 #4
0
class SIMSpeedTest(object):
    MIN_DOWNLOAD_SPD = 0.0  # Mbps
    MIN_UPLOAD_SPD = 0.0  # Mbps
    SCHEDULE = 0  # Run Boot2 every {SCHEDULE} minutes. 0 = Only run on boot.
    NUM_ACTIVE_SIMS = 0  # Number of fastest (download) SIMs to keep active.  0 = all; do not disable SIMs
    ONLY_RUN_ONCE = False  # True means do not run if Boot2 has been run on this device before.

    STATUS_DEVS_PATH = '/status/wan/devices'
    CFG_RULES2_PATH = '/config/wan/rules2'
    CTRL_WAN_DEVS_PATH = '/control/wan/devices'
    API_URL = 'https://www.cradlepointecm.com/api/v2'
    CONNECTION_STATE_TIMEOUT = 7 * 60  # 7 Min
    NETPERF_TIMEOUT = 5 * 60  # 5 Min
    sims = {}

    def __init__(self):
        self.client = EventingCSClient('Boot2')

    def check_if_run_before(self):
        if self.ONLY_RUN_ONCE:
            if self.client.get('/config/wan/rules2/0/_id_'
                               ) == '00000000-1234-1234-1234-1234567890ab':
                self.client.log(
                    'ERROR - Boot2 has been run before! /config/wan/rules2/0/_id = 00000000-1234-1234-1234-1234567890ab'
                )
                raise RunBefore(
                    'ERROR - Boot2 has been run before! /config/wan/rules2/0/_id = 00000000-1234-1234-1234-1234567890ab'
                )
        return False

    def wait_for_ncm_sync(self):
        # WAN connection_state
        if self.client.get('status/wan/connection_state') != 'connected':
            self.client.log('Waiting until WAN is connected...')
        timeout_count = 500
        while self.client.get('/status/wan/connection_state') != 'connected':
            timeout_count -= 1
            if not timeout_count:
                raise Timeout('WAN not connecting')
            time.sleep(2)

        # ECM State
        if self.client.get('status/ecm/state') != 'connected':
            self.client.log('Waiting until NCM is connected...')
            self.client.put('/control/ecm', {'start': True})
        timeout_count = 500
        while self.client.get('/status/ecm/state') != 'connected':
            timeout_count -= 1
            if not timeout_count:
                raise Timeout('NCM not connecting')
            time.sleep(2)

        # ECM Sync
        if self.client.get('status/ecm/sync') != 'ready':
            self.client.log('Waiting until NCM is synced...')
            self.client.put('/control/ecm', {'start': True})
        timeout_count = 500
        while self.client.get('/status/ecm/sync') != 'ready':
            self.client.put('/control/ecm', {'start': True})
            timeout_count -= 1
            if not timeout_count:
                raise Timeout('NCM not connecting')
            time.sleep(2)

        return

    def NCM_suspend(self):
        self.client.log('Stopping NCM')
        timeout_count = 500
        while not 'ready' == self.client.get('/status/ecm/sync'):
            timeout_count -= 1
            if not timeout_count:
                raise Timeout('NCM sync not ready')
            time.sleep(2)
        self.client.put('/control/ecm', {'stop': True})
        timeout_count = 500
        while not 'stopped' == self.client.get('/status/ecm/state'):
            timeout_count -= 1
            if not timeout_count:
                raise Timeout('NCM not stopping')
            time.sleep(2)

    def find_sims(self):
        while True:
            sims = {}
            wan_devs = self.client.get(self.STATUS_DEVS_PATH) or {}
            for uid, status in wan_devs.items():
                if uid.startswith('mdm-'):
                    error_text = status.get('status', {}).get('error_text', '')
                    if error_text:
                        if 'NOSIM' in error_text:
                            continue
                    sims[uid] = status
            num_sims = len(sims)
            if not num_sims:
                self.client.log('No SIMs found at all yet')
                time.sleep(10)
                continue
            if num_sims < 2:
                self.client.log('Only 1 SIM found!')
                raise OneModem('Only 1 SIM found!')
            else:
                break
        self.client.log(f'Found SIMs: {sims.keys()}')
        self.sims = sims
        return True

    def modem_state(self, sim, state):
        # Blocking call that will wait until a given state is shown as the modem's status
        timeout_counter = 0
        sleep_seconds = 0
        conn_path = '%s/%s/status/connection_state' % (self.STATUS_DEVS_PATH,
                                                       sim)
        self.client.log(f'Connecting {self.port_sim(sim)}')
        while True:
            sleep_seconds += 5
            conn_state = self.client.get(conn_path)
            self.client.log(
                f'Waiting for {self.port_sim(sim)} to connect.  Current State={conn_state}'
            )
            if conn_state == state:
                break
            if timeout_counter > self.CONNECTION_STATE_TIMEOUT:
                self.client.log(f'Timeout waiting on {self.port_sim(sim)}')
                raise Timeout(conn_path)
            time.sleep(min(sleep_seconds, 45))
            timeout_counter += sleep_seconds
        self.client.log(f'{self.port_sim(sim)} connected.')
        return True

    def iface(self, sim):
        iface = self.client.get('%s/%s/info/iface' %
                                (self.STATUS_DEVS_PATH, sim))
        return iface

    def port_sim(self, sim):
        return f'{self.sims[sim]["info"]["port"]} {self.sims[sim]["info"]["sim"]}'

    def run_speedtest(self, speedtest):
        self.client.put('/state/system/netperf', {"run_count": 0})
        res = self.client.put("/control/netperf", speedtest)
        self.client.log(f'Starting Speedtest... {res}')

        timeout_counter = 0
        # wait for results
        delay = speedtest['input']['options']['limit']['time'] + 8
        status_path = "/control/netperf/output/status"
        while True:
            time.sleep(delay)
            status = self.client.get(status_path)
            if status == 'complete':
                break
            if timeout_counter > self.NETPERF_TIMEOUT:
                self.client.log(
                    f"Timeout waiting on speedtest for {speedtest['input']['options']['ifc_wan']}"
                )
                raise Timeout(status_path)
            timeout_counter += delay

        if status != 'complete':
            self.client.log(f"ERROR: status=%s expected 'complete' {status}")
            return None

        # now get the result
        results_path = self.client.get("/control/netperf/output/results_path")

        results = None
        while not results:
            results = self.client.get(results_path)
            time.sleep(2)
        self.client.log('Speedtest Complete.')
        return results

    def do_speedtest(self, sim):
        default_speedtest['input']['options']['ifc_wan'] = self.iface(sim)
        default_speedtest['input']['options']['send'] = False
        default_speedtest['input']['options']['recv'] = True
        tcp_down = self.run_speedtest(default_speedtest).get('tcp_down')
        default_speedtest['input']['options']['send'] = True
        default_speedtest['input']['options']['recv'] = False
        tcp_up = self.run_speedtest(default_speedtest).get('tcp_up')

        if not tcp_up:
            self.client.log('do_speedtest tcp_up results missing!')
            default_speedtest['input']['options']['send'] = True
            default_speedtest['input']['options']['recv'] = False
            results = self.run_speedtest(default_speedtest)
            tcp_up = results.get('tcp_up') or None

        if not tcp_down:
            self.client.log('do_speedtest tcp_down results missing!')
            default_speedtest['input']['options']['send'] = False
            default_speedtest['input']['options']['recv'] = True
            results = self.run_speedtest(default_speedtest)
            tcp_down = results.get('tcp_down') or None

        down = float(tcp_down.get('THROUGHPUT', 0.0)) if tcp_down else 0.0
        up = float(tcp_up.get('THROUGHPUT', 0.0)) if tcp_up else 0.0
        return down, up

    def test_sim(self, device):
        try:
            if self.modem_state(device, 'connected'):

                # Get diagnostics and log it
                diagnostics = self.client.get(
                    f'{self.STATUS_DEVS_PATH}/{device}/diagnostics')
                self.sims[device]['diagnostics'] = diagnostics
                self.client.log(
                    f'Modem Diagnostics: {self.port_sim(device)} RSRP:{diagnostics.get("RSRP")}'
                )

                # Do speedtest and log results
                self.sims[device]['download'], self.sims[device][
                    'upload'] = self.do_speedtest(self.sims[device])
                self.client.log(
                    f'Speedtest Results: {self.port_sim(device)} TCP Download: '
                    f'{self.sims[device]["download"]}Mbps TCP Upload: {self.sims[device]["upload"]}Mbps'
                )

                # Verify minimum speeds
                if self.sims[device].get(
                        'download',
                        0.0) > self.MIN_DOWNLOAD_SPD and self.sims[device].get(
                            'upload', 0.0) > self.MIN_UPLOAD_SPD:
                    return True
                else:  # Did not meet minimums
                    self.client.log(
                        f'{self.port_sim(device)} Failed to meet minimums! MIN_DOWNLOAD_SPD: {self.MIN_DOWNLOAD_SPD} MIN_UPLOAD_SPD: {self.MIN_UPLOAD_SPD}'
                    )
                    return False

        except Timeout:
            message = f'Timed out running speedtest on {self.port_sim(device)}'
            self.client.log(message)
            self.client.alert(message)
            self.sims[device]['download'] = self.sims[device]['upload'] = 0.0
            return False

    def create_message(self, uid, *args):
        message = ''
        for arg in args:
            if arg == 'download':
                message = "DL:{:.2f}Mbps".format(
                    self.sims[uid]['download']) if not message else ' '.join([
                        message, "DL:{:.2f}Mbps".format(
                            self.sims[uid]['download'])
                    ])
            elif arg == 'upload':
                message = "UL:{:.2f}Mbps".format(
                    self.sims[uid]['upload']) if not message else ' '.join([
                        message, "UL:{:.2f}Mbps".format(
                            self.sims[uid]['upload'])
                    ])
            elif arg in ['PRD', 'HOMECARRID',
                         'RFBAND']:  # Do not include labels for these fields
                message = "{}".format(
                    self.sims[uid]['diagnostics'][arg]
                ) if not message else ' '.join(
                    [message, "{}".format(self.sims[uid]['diagnostics'][arg])])
            else:  # Include field labels (e.g. "RSRP:-82")
                message = "{}:{}".format(
                    arg, self.sims[uid]['diagnostics']
                    [arg]) if not message else ' '.join([
                        message, "{}:{}".format(
                            arg, self.sims[uid]['diagnostics'][arg])
                    ])
        return message

    def lock_sim(self, sim):
        rules = [{
            "_id_":
            "00000000-1234-1234-1234-123456789000",
            "priority":
            0,
            "trigger_name":
            f"{self.sims[sim]['info']['port']} {self.sims[sim]['info']['sim']}",
            "trigger_string":
            f"type|is|mdm%sim|is|{self.sims[sim]['info']['sim']}%port|is|{self.sims[sim]['info']['port']}"
        }]
        for i, uid in enumerate(self.sims):
            if uid != sim:
                rule = {
                    "_id_": f"0000000{i+1}-1234-1234-1234-123456789000",
                    "priority": -9 + i,
                    "trigger_name":
                    f"{self.sims[sim]['info']['port']} {self.sims[sim]['info']['sim']}",
                    "trigger_string":
                    f"type|is|mdm%sim|is|{self.sims[sim]['info']['sim']}%port|is|{self.sims[sim]['info']['port']}",
                    "disabled": True
                }
                rules.append(rule)
        self.client.put('config/wan/rules2', rules)
        time.sleep(2)

    def create_rules(self, sim_list):
        wan_rules = [{
            "_id_": "00000000-1234-1234-1234-1234567890ab",
            "priority": -10,
            "trigger_name": "Ethernet",
            "trigger_string": "type|is|ethernet"
        }]
        for i in range(0, len(sim_list)):
            rule = {
                "_id_":
                f"0000000{i+1}-1234-1234-1234-123456789000",
                "priority":
                -9 + i,
                "trigger_name":
                f"{self.sims[sim_list[i]]['info']['port']} {self.sims[sim_list[i]]['info']['sim']}",
                "trigger_string":
                f"type|is|mdm%sim|is|{self.sims[sim_list[i]]['info']['sim']}%port|is|{self.sims[sim_list[i]]['info']['port']}"
            }
            if self.NUM_ACTIVE_SIMS and i >= self.NUM_ACTIVE_SIMS:
                rule['disabled'] = True
            wan_rules.append(rule)
        req = self.client.put('config/wan/rules2/', wan_rules)
        time.sleep(2)
        if self.client.get('config/wan/rules2/0/_id_'
                           ) == '00000000-1234-1234-1234-1234567890ab':
            self.client.log(f'Updated WAN rules')
        else:
            self.client.log(f'WAN Rules not updated! : {req}')
        return

    def run(self):  # *** Main Application Starts Here ***
        self.client.log(
            f'Boot2 Starting... MIN_DOWNLOAD_SPD:{self.MIN_DOWNLOAD_SPD} MIN_UPLOAD_SPD:{self.MIN_UPLOAD_SPD} '
            f'SCHEDULE:{self.SCHEDULE} NUM_ACTIVE_SIMS:{self.NUM_ACTIVE_SIMS} ONLY_RUN_ONCE:{self.ONLY_RUN_ONCE}'
        )

        self.check_if_run_before()

        self.wait_for_ncm_sync()

        # Get info from router
        product_name = self.client.get("/status/product_info/product_name")
        system_id = self.client.get("/config/system/system_id")
        router_id = self.client.get('status/ecm/client_id')

        self.find_sims()  # Find active SIM slots

        # Send startup alert
        message = f'Boot2 Starting! {system_id} - {product_name} - Router ID: {router_id}'
        self.client.log(f'Sending alert to NCM: {message}')
        self.client.alert(message)

        # Pause for 3 seconds to allow NCM Alert to be sent before suspending NCM
        time.sleep(3)
        self.NCM_suspend()

        success = False  # Boot2 Success Status - Becomes True when a SIM meets minimum speeds

        # Test the connected SIM first
        primary_device = self.client.get('status/wan/primary_device')
        if 'mdm-' in primary_device:  # make sure its a modem
            if self.test_sim(primary_device):
                success = True

        # test remaining SIMs
        for sim in self.sims:
            if not self.sims[sim].get('download'):
                self.lock_sim(sim)
                if self.test_sim(sim):
                    success = True

        # Prioritizes SIMs based on download speed
        sorted_results = sorted(self.sims,
                                key=lambda x: self.sims[x]['download'],
                                reverse=True)

        # Create WAN rules
        self.create_rules(sorted_results)
        time.sleep(3)

        # Build text for custom1 field
        results_text = datetime.datetime.now().strftime(
            '%m/%d/%y %H:%M:%S')  # Start with a timestamp
        if not success:
            results_text += f' FAILED TO MEET MINIMUMS! MIN_DOWNLOAD_SPD:{self.MIN_DOWNLOAD_SPD} MIN_UPLOAD_SPD:{self.MIN_UPLOAD_SPD}'
        for uid in sorted_results:  # Add the results of each SIM with the fields specified:
            results_text = ' | '.join([
                results_text,
                self.create_message(uid, 'PRD', 'HOMECARRID', 'RFBAND', 'RSRP',
                                    'download', 'upload')
            ])

        # put messages to NCM custom fields
        self.wait_for_ncm_sync()
        if apikeys.get('X-ECM-API-ID') != 'YOUR':
            self.client.log(
                f'X-ECM-API-ID: {apikeys["X-ECM-API-ID"]} X-CP-API-ID: {apikeys["X-CP-API-ID"]}'
            )
            req = requests.put(f'{self.API_URL}/routers/{router_id}/',
                               headers=apikeys,
                               json={'custom1': results_text[:255]})
            self.client.log(f'NCM PUT Custom1 Result: {req.status_code}')
        else:
            self.client.log(
                'No NCM API Keys configured, skipping PUT to custom1')

        # Complete!  Send results.
        message = f"Boot2 Complete! {system_id} Results: {results_text}"
        self.client.log(message)
        self.client.alert(message)
コード例 #5
0
class DataUsageCheck(object):
    """
    Establish global variables.
    
    Set rate shaping values (in Kbps) 
    """

    # Modem Defaults (as of 7.0.40) - Not used when QoS is Disabled
    maxbwup = 25000
    maxbwdown = 25000

    minbwup = 512
    minbwdown = 512
    capreached = 0
    STATUS_DEVS_PATH = '/status/wan/devices'
    STATUS_DATACAP_PATH = '/status/wan/datacap'
    CFG_RULES2_PATH = '/config/wan/rules2'

    def __init__(self):
        self.cp = EventingCSClient(app_name)

    def find_modems(self):
        while True:
            devs = self.cp.get(self.STATUS_DEVS_PATH)
            modems_list = [x for x in devs if x.startswith('mdm-')]
            self.cp.log(f'modems_list: {modems_list}')
            num_modems = len(modems_list)
            if not num_modems:
                self.cp.log('No Modems found at all yet')
                time.sleep(10)
                continue
            else:
                return modems_list

    def find_modem_profiles(self):
        wan_ifcs = self.cp.get(self.CFG_RULES2_PATH)
        modem_profiles_list = [
            x['_id_'] for x in wan_ifcs
            if x['trigger_string'].startswith('type|is|mdm')
        ]
        self.cp.log(f'modem_profiles_list: {modem_profiles_list}')
        return modem_profiles_list

    def reset_throttle(self, modem_profiles_list, monthlyreset):
        for mdm in modem_profiles_list:
            if monthlyreset:
                self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                               '/bandwidth_egress')
                self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                               '/bandwidth_ingress')
            else:
                if 'bandwidth_egress' in self.cp.get(self.CFG_RULES2_PATH +
                                                     '/' + mdm):
                    self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                                   '/bandwidth_egress')
                if 'bandwidth_ingress' in self.cp.get(self.CFG_RULES2_PATH +
                                                      '/' + mdm):
                    self.cp.delete(self.CFG_RULES2_PATH + '/' + mdm +
                                   '/bandwidth_ingress')
        self.cp.put('config/qos/enabled', False)
        if monthlyreset:
            self.cp.log(
                'Monthly data usage reset - disabling reduced LTE data rate')
            message = (
                f'Monthly data usage reset - disabling reduced LTE data rate '
                f'for {self.system_id} - {self.product_name} - Router ID: '
                f'{self.router_id}')
            self.cp.alert(message)
            self.capreached = 0

    def set_throttle(self, modem_profiles_list):
        for mdm in modem_profiles_list:
            self.cp.put(self.CFG_RULES2_PATH + '/' + mdm + '/bandwidth_egress',
                        self.minbwup)
            self.cp.put(
                self.CFG_RULES2_PATH + '/' + mdm + '/bandwidth_ingress',
                self.minbwdown)
        self.cp.put('config/qos/enabled', True)
        self.cp.log(
            'Exceeded monthly data usage threshold - reducing LTE data rate')
        message = (
            f'Exceeded monthly data usage threshold - reducing LTE data rate '
            f'for {self.system_id} - {self.product_name} - Router ID: '
            f'{self.router_id}')
        self.cp.alert(message)
        self.capreached = 1

    def run(self):
        # Get info from router to populate description field in NCM
        # alert message
        self.product_name = self.cp.get('/status/product_info/product_name')
        self.system_id = self.cp.get('/config/system/system_id')
        self.router_id = self.cp.get('status/ecm/client_id')
        # Retrieve list of modems and their profiles
        modems_list = [str(x.split('-')[1]) for x in self.find_modems()]
        modem_profiles_list = self.find_modem_profiles()
        # Reset any throttling to account for router reboots.  If a
        # data cap alert is still active during the monthly cycle, the
        # appropriate rate shaping will be re-applied
        monthlyreset = False
        self.reset_throttle(modem_profiles_list, monthlyreset)
        time.sleep(5)

        while True:
            if self.cp.get(self.STATUS_DATACAP_PATH + '/completed_alerts/'):
                alerts = self.cp.get(self.STATUS_DATACAP_PATH +
                                     '/completed_alerts/')
                limitreached = 0
                for modem in modems_list:
                    if [
                            x['rule_id'] for x in alerts
                            if x['rule_id'] == modem + '-monthly'
                            if 'email_alert' in x['alerts']
                    ]:
                        limitreached += 1
                if limitreached > 0 and self.capreached == 0:
                    self.set_throttle(modem_profiles_list)
                elif limitreached == 0 and self.capreached == 1:
                    monthlyreset = True
                    self.reset_throttle(modem_profiles_list, monthlyreset)
            elif self.capreached == 1:
                monthlyreset = True
                self.reset_throttle(modem_profiles_list, monthlyreset)
            time.sleep(10)