
import session as s
import sys
import pprint
import time
import base64

def SyncSiteTag(sitename, site_id, tagname, value):
    """ SyncSiteTag() - either add, confirm, or update the tagname->value

    Args:
        sitename - string, name of site, used for messages only
        site_id - int, site id returned from GetSites()
        tagname - string, name of tag to set
        value - string, tag value, even numbers should be passed as strings.
    Returns:
        site_tag_id on Add*
        status code on Update* (1 or not-1 for success or failure)
        None - on Confirm
    """
    tags = s.api.GetSiteTags({'site_id' : site_id, 'tagname' : tagname})
    if len(tags) == 0:
        print "ADDING: site tag to %s tag(%s->%s)" % (sitename, tagname, value)
        return s.api.AddSiteTag(site_id, tagname, value)
    elif len(tags) == 1:
        tag = tags[0]
        if tag['value'] == value:
            print ("Confirmed: site tag on %s tag(%s->%s)" % 
                   (sitename, tagname, value))
        else:
            print ("UPDATE: site tag on %s from tag(%s->%s) to %s" % 
                   (sitename, tagname, tag['value'], value))
            return s.api.UpdateSiteTag(tag['site_tag_id'], tagname, value)
    else:
        print "Error: SyncSiteTag()"
        print "Error: This should never happen, but here we are."
        print "Error: Received multiple tags for tagname(%s)" % tagname
        print "Error: %s" % tags
        sys.exit(1)

def SyncLocation(sitename, location):
    """ SyncLocation - assigns location information to a Site.

        Args:
            sitename - string, name of site, i.e. 'mlabnuq0t'
            location - Location() object or None
        Returns:
            Nothing

        If myplc does not have these tags set automatically, you can create
        them using 'plcsh' and running these commands:
            AddTagType({'tagname' : 'city', 
                        'description' : 'The city where this site is located', 
                        'category' : 'site/location'})
            AddTagType({'tagname' : 'country', 
                        'description' : 'The country where this site is located', 
                        'category' : 'site/location'})
            AddTagType({'tagname' : 'extra', 
                        'description' : 'Extra context information for a site', 
                        'category' : 'site/extra'})
        The commands in this function depend on them existing.
    """
    
if location is None:
        # NOTE: nothing to do.
        return

    # NOTE: AddSiteTag() only recognizes the numeric site_id, :-/
    sites = s.api.GetSites(sitename, ['site_id', 'latitude', 'longitude'])
    if len(sites) != 1: 
        msg = "Error: GetS
ites(%s) returned: %s sites, expected 1"
        print msg % (sitename, len(sites))
        sys.exit(1)

    site_id = sites[0]['site_id']
    SyncSiteTag(sitename, site_id, 'city', location['city'])
    SyncSiteTag(sitename, site_id, 'country', location['country'])

    # NOTE: always update latitude/longitude, 
    update = {}
    if location['latitude'] != sites[0]['latitude']:
        update['latitude'] = location['latitude']
    if location['longitude'] != sites[0]['longitude']:
        update['longitude'] = location['longitude']

    if len(update) != 0:
        print ("UPDATE: site lat/long from %s,%s to %s" % 
                (location['latitude'], location['longitude'], update))
        UpdateSite(site_id, update)

    if 'extra' in location:
        SyncSiteTag(sitename, site_id, 'extra', location['extra'])

def MakeSite(loginbase,name,abbreviated_name, 
                       url="http://www.measurementlab.net/"):
    """
        MakeSite() - adds or confirms the existence of given site.
                     updates are not performed.

        Args:
            loginbase - name of site i.e mlabnuq01
            name - long presentation name of site. i.e. Measurement Lab NUQ01
            abbreviated_name - short presentation name of site, i.e. M-Lab NUQ01
            url - website associated with site.  Used to identify mlab by data
                    collection pipeline.
        Returns:
            site_id
    """
    site = s.api.GetSites({"login_base":loginbase})
    if len(site) == 0:
        print "MakeSite(%s,%s,%s)"%(loginbase,name,abbreviated_name)
        # NOTE: max_slices defaults to zero
        site_id = s.api.AddSite({"name":name,
                     "abbreviated_name":abbreviated_name,
                     "login_base": loginbase,
                     "url" : url, 'max_slices' : 10})
    elif len(site) == 1:
        print "Confirmed: %s is in DB" % loginbase
        site_id = site[0]['site_id']
    else:
        print "Error: PLCAPI returned two sites for a single name."
        print "Error: This should never happen, but here we are."
        print "Error: Double-check sitename(%s) and db" % loginbase
        sys.exit(1)

    return site_id

def MakePerson(first_name, last_name, email):
    persons = s.api.GetPersons({"email":email,"enabled":True},
                               ["site_ids","email","person_id"])
    if len(persons)==0:
        print "Adding person %s" % email
        fields = {"first_name":first_name, "last_name":last_name, 
                  "email":email, "password":"clara_abcdefg"}
        personid = s.api.AddPerson(fields)
        s.api.UpdatePerson(personid, {'enabled': True})
    elif len(persons)==1:
        personid=persons[0]['person_id']
    else:
        personid=-1
    return personid

def AddPersonToSite(email,personid,role,loginbase):
    site = s.api.GetSites({"login_base":loginbase})
    if len(site) != 1:
        print ("WARNING: problem with getting site info for loginbase=%s" % 
                loginbase)
    else:
        site = site[0]
        siteid = site["site_id"]
        if personid not in site['person_ids']:
            print ("Added %s (%d) to site %d (%s)" % 
                    (email, personid, siteid, loginbase))
            s.api.AddPersonToSite(personid,siteid)
            s.api.AddRoleToPerson(role,personid)
        else:
            print ("Confirmed %s (%d) is %s for site %s" % 
                    (email, personid, role, loginbase))

def MakeNode(login_base, hostname):
    node_list = s.api.GetNodes(hostname)
    if len(node_list) == 0:
        print "Adding Node %s to site %s" % (hostname, login_base)
        node_id = s.api.AddNode(login_base, { 'boot_state' : 'reinstall',
                                            'model' : 'unknown',
                                            'hostname' : hostname,} )
    else:
        node_id = node_list[0]['node_id']       

    return node_id
 
def MakePCU(login_base, node_id, pcu_fields):
    pcu_list = s.api.GetPCUs({'hostname' : pcu_fields['hostname']})
    if len(pcu_list) == 0:
        print "Adding PCU to %s: %s", (node_id, pcu_fields )
        pcu_id = s.api.AddPCU(login_base, pcu_fields)
        s.api.AddNodeToPCU(node_id, pcu_id, 1)
    else:
        if node_id in pcu_list[0]['node_ids']:
            print ("Confirmed PCU %s is associated with node %d" % 
                    (pcu_list[0]['hostname'], node_id))
            pcu_id = pcu_list[0]['pcu_id']
        else:
            print "ERROR: need to add pcu node_id %s" % node_id
            sys.exit(1)
    return pcu_id

def SyncNodeTag(hostname, node_id, tagname, value):
    """
       SyncNodeTag() - Adds, Updates, or Confirms the assignment of the given
                       tagname and value on the given hostname/node_id.
       Args:
        hostname - string, used for printing
        node_id - int, node_id of hostname within plcdb, from previous call to
                    GetNodes().
        tagname - string, the name of a pre-existing NodeTagType.
        value - string, the value of this tag.

       Returns:
        None

       Exits on errors.
    """
    # NOTE: accept that node_id is valid.
    # NOTE: lookup all NodeTags with tagname:value
    tags = s.api.GetNodeTags({'node_id': node_id, 
                              'tagname' : tagname})
    # NOTE: only one distinct tagname allowed per node.
    if len(tags) > 1:
        