Привет, хабр!
Наступило лето и очень многие уезжают из города. Кто-то на время отпуска, а кто-то и на все лето (если работа позволяет). Но одна из главных проблем за городом (для всех людей, так или иначе связанных с IT) — отсутствие нормального проводного быстрого интернета. Но это частично решается благодаря существованию LTE-сетей.
В моем регионе крупных провайдеров LTE всего два: Мегафон и Yota. Мегафон существенно дешевле, но у него есть одна крайне неприятная особенность: ограничение в 20гб трафика в месяц даже на максимальном тарифе.
Поэтому выбор оператора, на мой взгляд, очевиден. Но все же платить за 20 мегабит в два раза больше чем за 100 дома — сомнительное удовольствие. Но при этом, в отличие от других операторов, Yota позволяет в в любой момент бесплатно изменять текущий тариф в личном кабинете с перерасчетом оплаченного времени. Нужна скорость — выкручиваем ползунок на максимум. Нет? Тогда можно снизить скорость и платить меньше. Ну как тут можно удержаться и не автоматизировать этот процесс?
На хабре уже были статьи (раз, два), описывающие попытки йотоводов-автоматизаторов. Однако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипедОднако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипед.
А начнем мы, пожалуй, с разбора причин, по которым мне не подошли чужие велосипеды. Особо нетерпеливые / материально заинтересованные могут сразу перейти к разделу, описывающему реализацию.
Вариант linx56 мне не подошел ну совсем никак, потому как он работает phantomjs, для работы которого нужно довольно нехилое железо (которым Raspberry PI не является). Да и даже если я поставлю это на свой основной компьютер и оставлю его работать круглосуточно, забив в cron расписание по которому нужно отключать интернет, то яполучу счет за электричество больше, чем сэкономлю все равно не получу нормального результата, потому как интернет используется довольно непредсказуемо (и не только мной, но и членами семьи, для которых главное чтобы «просто работало»).
Второй вариант от bambrman существенно выигрывает у первого тем, что вместо phantomjs использует сильно менее прожерливый curl. Но в остальном все то же самое.
В итоге было решено написать еще один велосипед, в основе которого лежало бы не расписание (или руки человека), а текущая скорость, потребляемая всеми устройствами в локальной сети.
Я использовал Raspberry Pi и скрипт на Python, который каждые n секунд подключается по SSH к главному домашнему роутеру (TP-LINK WDR4300 с OpenWrt на борту) и при помощи ifconfig стягивает статистику нужного интерфейса.
После этого он считает среднюю скорость за промежуток между последним и предыдущим замером и отправляет ее в модуль, который отвечает за переключение тарифов в соответствии с текущей скоростью и настройками конфига. Чтобы было понятнее, приведу здесь его часть:
Как вы видите, в качестве ключа словаря выступает скорость в килобитах, а в качестве значения — тариф йоты.
По результатам замера текущей скорости у нас получилось, скажем, 1530 килобит. Округляем в меньшую сторону до значения ближайшего ключа в конфиге и получаем 900. Соответственно, должен использоваться тариф со скоростью 2.1 мегабит. В случае, если этот тариф уже активен, ничего не происходит. Но вот если тариф нужно повышать, то тут есть одна особенность.
Проблема в том, что если интернет долгое время не использовался (и активировался тариф в 320 кбит/с), а потом кто-то внезапно включил YouTube, то последовательное переключение с 320 кбит/с до 3.1 мбит/с может занять немало времени (не говоря уже о вечно лагающем ЛК Yota). С другой стороны, прыгать каждый раз на максимальный тариф, когда кто-то решил загрузить пару страниц и несколько картинок — тоже плохая идея. В общем я не придумал ничего лучше, чем добавить в конфиг
Этот параметр указывает на размер шага апгрейда между тарифами. То есть при установке speed_increase_step = 5, апгрейд с 320 кбит/с произойдет сразу до 3.1 мегабит, а дальше 15.0 и 20.0. И если возможная скорость не будет использоваться полностью, это будет компенсировано механизмом даунгрейда.
В случае, если активный тариф долгое время не используется в полную силу (текущая скорость не превышает нужного для текущего тарифа порога), скрипт ждет n минут и выполняет переключение на тариф, который удовлетворяет текущим запросам локальной сети (на основании средней скорости). Это самое количество минут задается в параметре hold_high_speed_time в минутах.
По сути, основной функционал на этом завершается. Правда, есть еще кое-что, что, возможно, кого-нибудь да заинтересует:
Писалось, как и все остальное, в первую очередь для себя. Но, честно честно говоря, так и не пригодилось, потому как генерирует слишком много спама. Но может кому понравится ;)
Включается это дело все там же, в конфиге:
Как видите, здесь все довольно просто. pb_enabled принимает значения true/false. Назначение очевидно.
pb_api — ваш ключ со страницы www.pushbullet.com/account
pb_devices — список идентификаторов устройств, на которые рассылаются уведомления. Взять можно тут: api.pushbullet.com/v2/devices. При входе вас попросят авторизоваться: просто введите ключ api в поле логина. Полное описание метода тут: docs.pushbullet.com/v2/devices
Ну что же.Дело сделано, сообщение доставлено. Теперь можно и код показать рассказать, как все это дело готовить.
Вот, собственно, и все. Понимаю, что это некоторое злоупотребление щедростью Yota (хотя, учитывая тарифы, щедростью ли?), но тем не менее смею надеяться, что никто ничего менять не собирается. В конце концов, людей, способных развернуть у себя такое, не так много. А если учесть, что мало кто из них пользуется Yota, то так и вообще мелочь.
Наступило лето и очень многие уезжают из города. Кто-то на время отпуска, а кто-то и на все лето (если работа позволяет). Но одна из главных проблем за городом (для всех людей, так или иначе связанных с IT) — отсутствие нормального проводного быстрого интернета. Но это частично решается благодаря существованию LTE-сетей.
В моем регионе крупных провайдеров LTE всего два: Мегафон и Yota. Мегафон существенно дешевле, но у него есть одна крайне неприятная особенность: ограничение в 20гб трафика в месяц даже на максимальном тарифе.
Поэтому выбор оператора, на мой взгляд, очевиден. Но все же платить за 20 мегабит в два раза больше чем за 100 дома — сомнительное удовольствие. Но при этом, в отличие от других операторов, Yota позволяет в в любой момент бесплатно изменять текущий тариф в личном кабинете с перерасчетом оплаченного времени. Нужна скорость — выкручиваем ползунок на максимум. Нет? Тогда можно снизить скорость и платить меньше. Ну как тут можно удержаться и не автоматизировать этот процесс?
На хабре уже были статьи (раз, два), описывающие попытки йотоводов-автоматизаторов. Однако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипедОднако, в силу некоторых особенностей, их творения мне не подошли и пришлось написать свой, существенно отличающийся, велосипед.
А начнем мы, пожалуй, с разбора причин, по которым мне не подошли чужие велосипеды. Особо нетерпеливые / материально заинтересованные могут сразу перейти к разделу, описывающему реализацию.
Постановка задачи
Вариант linx56 мне не подошел ну совсем никак, потому как он работает phantomjs, для работы которого нужно довольно нехилое железо (которым Raspberry PI не является). Да и даже если я поставлю это на свой основной компьютер и оставлю его работать круглосуточно, забив в cron расписание по которому нужно отключать интернет, то я
Второй вариант от bambrman существенно выигрывает у первого тем, что вместо phantomjs использует сильно менее прожерливый curl. Но в остальном все то же самое.
В итоге было решено написать еще один велосипед, в основе которого лежало бы не расписание (или руки человека), а текущая скорость, потребляемая всеми устройствами в локальной сети.
Реализация
Я использовал Raspberry Pi и скрипт на Python, который каждые n секунд подключается по SSH к главному домашнему роутеру (TP-LINK WDR4300 с OpenWrt на борту) и при помощи ifconfig стягивает статистику нужного интерфейса.
После этого он считает среднюю скорость за промежуток между последним и предыдущим замером и отправляет ее в модуль, который отвечает за переключение тарифов в соответствии с текущей скоростью и настройками конфига. Чтобы было понятнее, приведу здесь его часть:
modes: {
0: 320,
290: 512,
495: 768,
700: 1.0,
900: 2.1,
1900: 3.1,
2900: 4.1,
3900: 6.5,
6500: 8.0,
8000: 10.0,
10000: 15.0,
14800: 20.0
}
Как вы видите, в качестве ключа словаря выступает скорость в килобитах, а в качестве значения — тариф йоты.
По результатам замера текущей скорости у нас получилось, скажем, 1530 килобит. Округляем в меньшую сторону до значения ближайшего ключа в конфиге и получаем 900. Соответственно, должен использоваться тариф со скоростью 2.1 мегабит. В случае, если этот тариф уже активен, ничего не происходит. Но вот если тариф нужно повышать, то тут есть одна особенность.
Проблема в том, что если интернет долгое время не использовался (и активировался тариф в 320 кбит/с), а потом кто-то внезапно включил YouTube, то последовательное переключение с 320 кбит/с до 3.1 мбит/с может занять немало времени (не говоря уже о вечно лагающем ЛК Yota). С другой стороны, прыгать каждый раз на максимальный тариф, когда кто-то решил загрузить пару страниц и несколько картинок — тоже плохая идея. В общем я не придумал ничего лучше, чем добавить в конфиг
speed_increase_step: 1
Этот параметр указывает на размер шага апгрейда между тарифами. То есть при установке speed_increase_step = 5, апгрейд с 320 кбит/с произойдет сразу до 3.1 мегабит, а дальше 15.0 и 20.0. И если возможная скорость не будет использоваться полностью, это будет компенсировано механизмом даунгрейда.
Даунгрейд
В случае, если активный тариф долгое время не используется в полную силу (текущая скорость не превышает нужного для текущего тарифа порога), скрипт ждет n минут и выполняет переключение на тариф, который удовлетворяет текущим запросам локальной сети (на основании средней скорости). Это самое количество минут задается в параметре hold_high_speed_time в минутах.
hold_high_speed_time: 3
По сути, основной функционал на этом завершается. Правда, есть еще кое-что, что, возможно, кого-нибудь да заинтересует:
Уведомления о смене тарифа через PushBullet
Писалось, как и все остальное, в первую очередь для себя. Но, честно честно говоря, так и не пригодилось, потому как генерирует слишком много спама. Но может кому понравится ;)
Включается это дело все там же, в конфиге:
pb_enabled: true
pb_api: v13WA7HYfwj99gUz8rwvK2m7eLN1uheVxZujAgP8gn2su
pb_devices: [
ujAgP8gn2sasFDsgsWhxs,
ujAgP8gn2mkasnuH2Gtga
]
Как видите, здесь все довольно просто. pb_enabled принимает значения true/false. Назначение очевидно.
pb_api — ваш ключ со страницы www.pushbullet.com/account
pb_devices — список идентификаторов устройств, на которые рассылаются уведомления. Взять можно тут: api.pushbullet.com/v2/devices. При входе вас попросят авторизоваться: просто введите ключ api в поле логина. Полное описание метода тут: docs.pushbullet.com/v2/devices
Ну что же.
Установка
- Первым делом идем и устанавливаем на свой роутер OpenWrt. Ну или что-нибудь подобное с SSH, авторизацией по ключам и утилитой ifconfig.
- После этого нужно найти что-то вроде Raspberry Pi. То, на чем можно запустить третий питон и где есть ssh-клиент с авторизацией по ключам.
- Настраиваем авторизацию по ключу для подключения к роутеру. Убеждаемся что команда вида (с вашими данными, конечно же) «ssh 192.168.1.1 -p 22 -l root ifconfig» работает без дополнительных вопросов
- Ставим Python 3 и pip. Изначально все писалось и тестировалось на 3.2.3. На совместимость с более ранними версиями тестов не было
- Клонируем репозиторий проекта с GitHub: https://github.com/wutiarn/yota-speed-controller
- Делаем cd в папку с репозиторием
- Выполняем pip3 install -r requirements.txt. Обратите внимание, что pip может иметь другое имя (pip-3.2, например)
- Редактируем config.yaml. Описания большинства его параметров есть вверху. Остальные, как мне кажется, в представлении не нуждаются. Ну разве что кроме interface. По умолчанию там указан eth0.2. Обычно в openwrt именно eth0.2 является wan-интерфейсом, но лучше проверить. Также стоит отметить опцию use_ssh. Если выставить ее в false, то совершенно логично, мониториться будет локальный интерфейс. Ну это на случай если у кого-нибудь в качестве роутера используется полноценный компьютер. Ну или кто-то запустит все это дело прямо на роутере
- После этого запускаем main.py. Можно как напрямую (предварительно дав ему права на исполнения), так и передав интерпретатору питона
- Если все работает, то устанавливаем supervisor (обычно есть в стандартных репозиториях)
- Перемещаем файл yota.conf в папку /etc/supervisor/conf.d/ попутно подправив в нем пути к main.py и, если хотите, к файлу, в который будет писаться лог
- И перезапускаем supervisor командой supervisorctl reload. Демон должен запуститься автоматически. Посмотрите напоследок логи. В них должно быть что-то вроде:
2014-07-06 14:16:01,805 [INFO] yota | Initializing 2014-07-06 14:16:11,497 [INFO] yota.web | CURRENT TARIFF: 512 2014-07-06 14:16:11,712 [INFO] yota | Initialised 2014-07-06 14:16:11,718 [INFO] yota | Starting 2014-07-06 14:16:16,016 [DEBUG] yota.current_speed_provider | Current speed: RX: 5 TX: 9. Summary: 14, Time: 3 2014-07-06 14:16:17,262 [INFO] yota.speed_control | Downgrade started: 2014-07-06 14:19:16.022307 2014-07-06 14:16:18,315 [DEBUG] yota.current_speed_provider | Current speed: RX: 7 TX: 19. Summary: 26, Time: 2 2014-07-06 14:16:20,575 [DEBUG] yota.current_speed_provider | Current speed: RX: 12 TX: 20. Summary: 32, Time: 2
Вот, собственно, и все. Понимаю, что это некоторое злоупотребление щедростью Yota (хотя, учитывая тарифы, щедростью ли?), но тем не менее смею надеяться, что никто ничего менять не собирается. В конце концов, людей, способных развернуть у себя такое, не так много. А если учесть, что мало кто из них пользуется Yota, то так и вообще мелочь.