"""Crawler that sends notifications as soon as servers
on Kimsufi/OVH become available for purchase"""

import tornado.ioloop
import tornado.web
import json
import subprocess
from tornado.httpclient import AsyncHTTPClient
from tornado.gen import coroutine
import logging
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
_logger = logging.getLogger(__name__)

with open('config.json', 'r') as configfile:
    config = json.loads(configfile.read())

URL = "https://ws.ovh.com/dedicated/r2/ws.dispatcher/getAvailability2"

SERVER_TYPES = {
    '142sk1': 'KS-1',
    '142sk7': 'KS-2',
    '142sk3': 'KS-3',
    '142sk4': 'KS-4',
    '142sk5': 'KS-5A',
    '142sk8': 'KS-5B',
    '142sk6': 'KS-6',
}

DATACENTERS = {
    'bhs': 'Beauharnois, Canada (Americas)',
    'gra': 'Gravelines, France',
    'rbx': 'Roubaix, France (Western Europe)',
    'sbg': 'Strasbourg, France (Central Europe)',
    'par': 'Paris, France',
}

STATES = {}


def send_mail(title, text, url=False):
    """Send email notification using SMTP"""
    msg = MIMEMultipart()
    fromaddr = config['from_email']
    # smtp user may be different from email
    fromuser = config.get('from_user', fromaddr)
    frompwd = config['from_pwd']
    toaddr = config['to_email']
    msg = MIMEMultipart()
    msg['From'] = fromaddr
    msg['To'] = toaddr
    msg['Subject'] = title
    body = text + '\nURL: ' + url
    msg.attach(MIMEText(body, 'plain'))
    server = smtplib.SMTP(config['from_smtp_host'], config['from_smtp_port'])
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.login(fromuser, frompwd)
    text = msg.as_string()
    server.sendmail(fromaddr, toaddr, text)


def send_osx_notification(title, text, url=False):
    """Send Mac-OS-X notification using terminal-notifier"""
    subprocess.call(['terminal-notifier',
                     '-title', title,
                     '-message', text,
                     '-open', url
                     ])


def update_state(state, value, message=False):
    """Update state of particular event"""
    # if new value is True and last saved was False
    if state not in STATES:
        STATES[state] = False
    if value is not STATES[state]:
        _logger.info("State change - %s:%s", state, value)
    if value and not STATES[state]:
        send_mail(**message)
    STATES[state] = value


@coroutine
def run_crawler():
    """Run a crawler iteration"""
    http_client = AsyncHTTPClient()
    # request OVH availablility API asynchronously
    response = yield http_client.fetch(URL)
    response_json = json.loads(response.body.decode('utf-8'))
    if not response_json or not response_json['answer']:
        return
    availability = response_json['answer']['availability']
    for item in availability:
        # look for servers of required types in OVH availability list
        if SERVER_TYPES.get(item['reference']) in config['servers']:
            # make a flat list of zones where servers are available
            available_zones = [e['zone'] for e in item['zones']
                     if e['availability'] != 'unavailable']
            # iterate over all tacked zones and set availability states
            for zone in config['zones']:
                server = SERVER_TYPES[item['reference']]
                state_id = '%s_available_in_%s' % (server, zone)
                # update state for each tracked zone
                text = "Server %s is available in %s" % (server, zone)
                message = {
                    'title': "Server %s available" % server,
                    'text': text,
                    'url': "http://www.kimsufi.com/fr/index.xml"
                }
                update_state(state_id, zone in available_zones, message)


if __name__ == "__main__":
    loop = tornado.ioloop.IOLoop.instance()
    tornado.ioloop.PeriodicCallback(run_crawler, 30000).start()
    loop.start()

@@ -168,16 +168,27 @@
 process%0A
+import sys%0A
 from tor
@@ -1061,16 +1061,652 @@
  = %7B%7D%0A%0A%0A
+def check_smtp_server():%0A    %22%22%22Try login to SMTP server to check config%22%22%22%0A    fromaddr = config%5B'from_email'%5D%0A    fromuser = config.get('from_user', fromaddr)%0A    frompwd = config%5B'from_pwd'%5D%0A    server = smtplib.SMTP(config%5B'from_smtp_host'%5D, config%5B'from_smtp_port'%5D)%0A    server.ehlo()%0A    server.starttls()%0A    server.ehlo()%0A    try:%0A        server.login(fromuser, frompwd)%0A    except Exception as ex:%0A        _logger.error(%22Cannot connect to your SMTP accout. %22%0A                      %22Correct your config and try again. Error details:%22)%0A        _logger.error(ex)%0A        sys.exit(1)%0A    _logger.info(%22SMTP server check passed%22)%0A%0A%0A
 def send
@@ -3790,16 +3790,26 @@
 zones'%5D%0A
+          
         
@@ -3860,16 +3860,16 @@
 lable'%5D%0A
-
         
@@ -4527,16 +4527,40 @@
 ain__%22:%0A
+    check_smtp_server()%0A
     loop
@@ -4667,17 +4667,166 @@
     
-loop.start(
+_logger.info(%22Starting IO loop%22)%0A    try:%0A        loop.start()%0A    except KeyboardInterrupt:%0A        _logger.info(%22Terminated by user. Bye.%22)%0A        sys.exit(0
 )%0A

Add SMTP credentials check