From 770a7da908beafc6d9d8e813bb81594c451dcb45 Mon Sep 17 00:00:00 2001 From: pgrondek Date: Tue, 23 Jun 2020 02:16:47 +0200 Subject: [PATCH] Add config for router --- inventory/hosts.yml | 1 + main.yml | 4 + roles/router/files/monitor.sh | 88 +++++++++++ roles/router/files/tplink_smartplug.py | 141 ++++++++++++++++++ roles/router/tasks/main.yml | 2 + roles/router/tasks/restart-wan-on-failure.yml | 22 +++ 6 files changed, 258 insertions(+) create mode 100755 roles/router/files/monitor.sh create mode 100755 roles/router/files/tplink_smartplug.py create mode 100644 roles/router/tasks/main.yml create mode 100644 roles/router/tasks/restart-wan-on-failure.yml diff --git a/inventory/hosts.yml b/inventory/hosts.yml index f075127..4e0a039 100644 --- a/inventory/hosts.yml +++ b/inventory/hosts.yml @@ -3,6 +3,7 @@ all: hosts: prusa.lan: iron-man.lan: + router.lan: children: docker_cluster: hosts: diff --git a/main.yml b/main.yml index 8af6c79..e553e6e 100644 --- a/main.yml +++ b/main.yml @@ -16,3 +16,7 @@ - docker-cluster - ubuntu +- hosts: router.lan + roles: + - router + diff --git a/roles/router/files/monitor.sh b/roles/router/files/monitor.sh new file mode 100755 index 0000000..67161d3 --- /dev/null +++ b/roles/router/files/monitor.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +WAN=eth0 +HOSTS=( + '1.1.1.1' + '1.0.0.1' + '8.8.8.8' +) +OUTLET_IP=192.168.60.14 + +PING_TIMEOUT=5 +SOFT_WAIT_TIME=30 +DHCP_WAIT_TIME=30 +MODEM_RESET_WAIT_TIME=60 + +PING="/bin/ping -c 1 -W ${PING_TIMEOUT} -w ${PING_TIMEOUT}" +RESET_MODEM_SCRIPT="/config/scripts/tplink_smartplug.py" + +DEBUG=true +DISABLE_SOFT_RESTART=true + +function debug() { + if [[ ${DEBUG} ]]; then + echo $@ + fi +} + +function soft_restart() { + if [[ ${DISABLE_SOFT_RESTART} ]] ; then + return + fi + + debug "Releasing DHCP IP lease on ${WAN}" + release dhcp interface ${WAN} + + debug "Disabling ${WAN} interface" + set interfaces ethernet ${WAN} disable + + debug "Waiting ${SOFT_WAIT_TIME}" + sleep ${SOFT_WAIT_TIME} + + debug "Enabling ${WAN} interface" + set interfaces ethernet ${WAN} enable + + debug "Renewing DHCP IP lease on ${WAN}" + renew dhcp interface ${WAN} +} + +function hard_restart() { + debug "Turning off router outlet" + ${RESET_MODEM_SCRIPT} -t ${OUTLET_IP} -c off >> /dev/null 2>&1 + + debug "Waiting ${SOFT_WAIT_TIME}" + sleep ${SOFT_WAIT_TIME} + + debug "Turning off router outlet" + ${RESET_MODEM_SCRIPT} -t ${OUTLET_IP} -c on >> /dev/null 2>&1 +} + +function ping() { + for host in "${HOSTS[@]}"; do + ${PING} ${host} >> /dev/null 2>&1 + if [[ $? == 0 ]] ; then + debug "Ping successful, exiting" + exit 0 + else + debug "Ping failed on ${host}" + fi + done +} + +function main() { + ping + soft_restart + + debug "Waiting for ip lease from dhcp (${DHCP_WAIT_TIME}s)" + sleep ${DHCP_WAIT_TIME} + + ping + hard_restart + + debug "Waiting for modem to bootup (${DHCP_WAIT_TIME}s)" + sleep ${MODEM_RESET_WAIT_TIME} + ping + soft_restart +} + +main diff --git a/roles/router/files/tplink_smartplug.py b/roles/router/files/tplink_smartplug.py new file mode 100755 index 0000000..f88c2e7 --- /dev/null +++ b/roles/router/files/tplink_smartplug.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# TP-Link Wi-Fi Smart Plug Protocol Client +# For use with TP-Link HS-100 or HS-110 +# +# by Lubomir Stroetmann +# Copyright 2016 softScheck GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import socket +import argparse +from struct import pack + +version = 0.3 + +# Check if hostname is valid +def validHostname(hostname): + try: + socket.gethostbyname(hostname) + except socket.error: + parser.error("Invalid hostname.") + return hostname + +# Check if port is valid +def validPort(port): + try: + port = int(port) + except ValueError: + parser.error("Invalid port number.") + + if ((port <= 1024) or (port >65535)) : + parser.error("Invalid port number.") + + return port + + +# Predefined Smart Plug Commands +# For a full list of commands, consult tplink_commands.txt +commands = { + 'on' : '{"system":{"set_relay_state":{"state":1}}}', + 'off' : '{"system":{"set_relay_state":{"state":0}}}', + 'reboot' : '{"system":{"reboot":{"delay":1}}}' +} + +# Encryption and Decryption of TP-Link Smart Home Protocol +# XOR Autokey Cipher with starting key = 171 +# Python 3.x Version +if sys.version_info[0] > 2: + def encrypt(string): + key = 171 + result = pack('>I', len(string)) + for i in string: + a = key ^ ord(i) + key = a + result += bytes([a]) + return result + + def decrypt(string): + key = 171 + result = "" + for i in string: + a = key ^ i + key = i + result += chr(a) + return result + +# Python 2.x Version +else: + def encrypt(string): + key = 171 + result = pack('>I', len(string)) + for i in string: + a = key ^ ord(i) + key = a + result += chr(a) + return result + + def decrypt(string): + key = 171 + result = "" + for i in string: + a = key ^ ord(i) + key = ord(i) + result += chr(a) + return result + +# Parse commandline arguments +parser = argparse.ArgumentParser(description="TP-Link Wi-Fi Smart Plug Client v" + str(version)) +parser.add_argument("-t", "--target", metavar="", required=True, help="Target hostname or IP address", type=validHostname) +parser.add_argument("-p", "--port", metavar="", default=9999, required=False, help="Target port", type=validPort) +parser.add_argument("-q", "--quiet", dest='quiet', action='store_true', help="Only show result") +parser.add_argument("--timeout", default=10, required=False, help="Timeout to establish connection") +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("-c", "--command", metavar="", help="Preset command to send. Choices are: "+", ".join(commands), choices=commands) +group.add_argument("-j", "--json", metavar="", help="Full JSON string of command to send") +args = parser.parse_args() + + +# Set target IP, port and command to send +ip = args.target +port = args.port +if args.command is None: + cmd = args.json +else: + cmd = commands[args.command] + + + +# Send command and receive reply +try: + sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_tcp.settimeout(int(args.timeout)) + sock_tcp.connect((ip, port)) + sock_tcp.settimeout(None) + sock_tcp.send(encrypt(cmd)) + data = sock_tcp.recv(2048) + sock_tcp.close() + + decrypted = decrypt(data[4:]) + + if args.quiet: + print(decrypted) + else: + print("Sent: ", cmd) + print("Received: ", decrypted) + +except socket.error: + quit("Could not connect to host " + ip + ":" + str(port)) diff --git a/roles/router/tasks/main.yml b/roles/router/tasks/main.yml new file mode 100644 index 0000000..8c513d7 --- /dev/null +++ b/roles/router/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- import_tasks: restart-wan-on-failure.yml diff --git a/roles/router/tasks/restart-wan-on-failure.yml b/roles/router/tasks/restart-wan-on-failure.yml new file mode 100644 index 0000000..1e0c71e --- /dev/null +++ b/roles/router/tasks/restart-wan-on-failure.yml @@ -0,0 +1,22 @@ +--- +- name: copy monitor script + copy: + src: monitor.sh + dest: /config/scripts/monitor.sh + mode: '0755' + +- name: copy script for reseting modem + copy: + src: tplink_smartplug.py + dest: /config/scripts/tplink_smartplug.py + mode: '0755' + +- name: setup cron task + vars: + ansible_connection: network_cli + ansible_network_os: edgeos + edgeos_config: + lines: + - set system task-scheduler task restartWan executable path /config/scripts/monitor.sh + - set system task-scheduler task restartWan interval 5m +