Esempio n. 1
0
    def render(self, request):

        session = getSession(ADMIN_SESSION_ID)
        
        host_name = os.path.basename(request.path)
        
        #XXX: Verify MAC format here
        
        log_debug("Received hostlookup request for %s" % host_name)

        # Lookup mac
        sql = "SELECT h.host_name, h.ip_address, am.asset_id, am.mac FROM " \
                "host h LEFT JOIN asset_macs am ON h.asset_id=" \
                "am.asset_id WHERE h.host_name=%s LIMIT 1"
        try:
            res = session.query(sql, (host_name.strip()))
        except:
            (type, value, tb) = sys.exc_info()
            log_warn("Could not lookup host!", sys.exc_info())
            request.setResponseCode(500, "Database query failed - %s" % \
                    value)
            request.finish()
            return server.NOT_DONE_YET
        
        if len(res)!=1:
            request.setResponseCode(404, "Specified Host not found!")
            request.finish()
            return server.NOT_DONE_YET
        
        # Write out info about the host
        rs = "FOUND=1\nASSET_ID=%s\nHOST_NAME=%s\nHOST_IP=%s\nMAC=%s\n" % \
                (res[0]["asset_id"], res[0]["host_name"], \
                res[0]["ip_address"], res[0]["mac"])
        return rs
Esempio n. 2
0
    def __init__(self, session_id=None):
        
        # Setup parameters expected by the base class 
        if session_id is None:
            session_id = ADMIN_SESSION_ID
        self.mParentSession = getSession(session_id)
        self.mChangeset = self.mParentSession.changeset

        # Call base constructor
        ccs_revision.__init__(self, self.mParentSession, self.mChangeset, \
                False)
        
        # Setup a working directory for this revision
        self.rDir = mkdtemp("", "ccsd")

        # Checkout the current configuration HEAD to this directory
        try:
            rev = core.svn_opt_revision_t()
            rev.kind = core.svn_opt_revision_head
            client.svn_client_checkout("%s/ca" % self.svnroot, self.rDir, \
                    rev, True, self.ctx, self.pool)
            self.mCurRev = rev
        except core.SubversionException:
            # CA not initialised
            raise ccs_ca_error("infrastructure not found in repository!")
        
        # Check basic repository structure
        if self.mParentSession is not None and self.mChangeset is not None:
            self.checkRepoStructure()
        
        # Start with no errors
        self.mErrors = {}
Esempio n. 3
0
    def __init__(self, session_id, host_id):
        """Initialises a new class for a specified host.

        The specified session must be valid and have appropriate access to
        the database for the tasks you intend to perform with the class. All
        database access / configuration manipulation triggered by this
        instance will pass through the specified session.
        """

        self._errMsg = ""
        self._commit = 0
        self._csInit = ""
        
        session = getSession(session_id)
        if session is None:
            raise ccs_host_error("Invalid session id")
        self._session_id = session_id
        
        # See if the specified host id makes sense
        sql = "SELECT * FROM host WHERE host_id=%s"
        res = session.query(sql, (host_id))
        if len(res) < 1:
            raise ccs_host_error("Invalid host. Unable to retrieve " 
                "details")
        
        # Store details 
        self.host_id = host_id
        self._properties = res[0]
Esempio n. 4
0
def doRollover(type):
    """Handles resetting customer caps when their usage rolls over"""
    session = getSession(ADMIN_SESSION_ID)
    
    sql = "SELECT * FROM rollover WHERE cap_period=%s"
    res = session.query(sql, (type))
    for ro in res:
        try:           
            # Update the broadband_account table to reset the cap
            sql = "UPDATE broadband_account SET plan_start=CURRENT_TIMESTAMP, "\
                "cap_at_mb=%s, used_mb=0, radius_plan=%s WHERE login_id=%s"
            session.execute(sql, (ro["included_mb"], ro["radius_plan"], \
                    ro["login_id"]))
        except:
            log_error("Failed to reset cap and state for contact #%s" % \
                    ro["login_id"], sys.exc_info())

    #Reset the warning sent flags
    try:           
        sql = "UPDATE broadband_account SET warning_sent=false where exists " \
            "(select billing_plan.plan_id, billing_plan.cap_period from " \
            "billing_plan where broadband_account.plan_id = " \
            "billing_plan.plan_id AND billing_plan.cap_period=%s)"
        session.execute(sql, (type))
    except:
        log_error("Failed to reset warning_sent field")
Esempio n. 5
0
def forgotPassword(username):
    """Emails the user a new password when they have forgotten it"""
    session = getSession(ADMIN_SESSION_ID)

    username, domain = username.split('@')
    
    contact = session.query("SELECT l.*, a.admin_id, a.email, fl.enabled AS flstate FROM " \
            "(logins l INNER JOIN admins a ON a.login_id=l.login_id) LEFT JOIN first_login fl ON l.login_id=" \
            "fl.login_id WHERE username=%s AND domain=%s", (username, domain))
    if len(contact) != 1:
        log_warn("New pasword request for unknown user: %s@%s" % (username, domain))
        return 0

    # create a password
    plain = createPassword(8)
    salt = createSalt()
    passwd = crypt.crypt(plain, salt)

    # Update the password
    session.execute("UPDATE logins SET passwd=%s, enabled=1 WHERE " \
            "login_id=%s", (passwd, contact[0]['login_id']))
    if contact[0]["flstate"] is None or contact[0]["flstate"]=="":
        sql = "INSERT INTO first_login (login_id, enabled) VALUES " \
                "(%s, %s)"
        session.execute(sql, (contact[0]["login_id"], contact[0]["enabled"]))
    
    # Send email
    try:
        _sendPasswordEmail(contact[0]["email"], plain)
    except:
        log_warn("Unable to send forgotten password notification!", \
                sys.exc_info())
    log_info("New password emailed to %s@%s" % (username, domain))

    return 0
Esempio n. 6
0
    def __init__(self, session_id, site_id):
        """Initialises a new class for a specified site.
        
        The specified session must be valid and have appropriate access to
        the database for the tasks you intend to perform with the class. All
        database access / configuration manipulation triggered by this
        instance will pass through the specified session.
        """

        self._errMsg = ""
        self._commit = 0
        self._csInit = ""

        session = getSession(session_id)
        if session is None:
            raise ccs_site_error("Invalid session specified!")
        self._session_id = session_id
        
        # See if the specified site_id id makes sense
        try:
            sql = "SELECT * FROM site WHERE site_id=%s"
            res = session.query(sql, (site_id))
        except:
            raise ccs_site_error("Unable to retrieve site details!")
        
        # Store details 
        self.site_id = site_id
        self._properties = res[0]
Esempio n. 7
0
    def __init__(self, session_id, link_id):
        """Initialises a new class for a specified link.

        Parent must be a reference to the core crcnetd class.
        
        The specified session must be valid and have appropriate access to
        the database for the tasks you intend to perform with the class. All
        database access / configuration manipulation triggered by this
        instance will pass through the specified session.
        """

        self._errMsg = ""
        self._commit = 0
        self._csInit = ""

        session = getSession(session_id)
        if session is None:
            raise ccs_interface_error("Invalid session id")
        self._session_id = session_id

        # See if the specified link id makes sense
        sql = "SELECT * FROM link WHERE link_id=%s"
        res = session.query(sql, (link_id))
        if len(res) != 1:
            # DB Query failed
            raise ccs_link_error("Invalid link. Unable to retrieve " "details")

        # Store details
        self._properties = res[0]
        self.link_id = link_id
Esempio n. 8
0
def init_ca():
    """Called during server startup to initialise the CA environment
    
    The CA must not be initialised until after the cfengine module so that the
    subversion repository is ready to use
    """
    global certParams
    
    # Load the repository to check for a CA
    session_id = ADMIN_SESSION_ID
    session = getSession(session_id)
    changeset = session.changeset
                      
    revision = ccs_revision(session, changeset);
 
    #If the CA does not exist, make it and add it to the repository
    if not revision.fileExists("ca/cacert.pem"):
        log_info("Creating CA for the certificates")
        siteName = config_get("network","site_name")
        if siteName == "":
            siteName = "CRCnet Default Site"
        wDir = revision.getWorkingDir()
        (fdi, fdo) = os.popen2("openssl req -new -x509 -nodes -keyout "\
            "%s/ca/cakey.pem -out %s/ca/cacert.pem -days 3650 2>&1"\
             % (wDir,wDir))
        fdi.write("NZ\n") #country
        fdi.write(".\n")  #ignore state of province
        fdi.write(".\n")  #ignore location
        fdi.write("%s\n" % siteName) #Organisation
        fdi.write("ccsd\n") #Organisation Unit
        fdi.write("Certification Authority\n") #Common Name
        fdi.write(".\n")  #ignore email
        fdo.close()
        fdi.close()
        time.sleep(1)
        revision.checkin("Added CA", ["%s/ca/cacert.pem" % (wDir),\
            "%s/ca/cakey.pem" % (wDir)])
    
    # Load the CA
    try:
        ca = ccs_ca()
        certParams = ca.getCAParameters()
    except ccs_ca_error:
        (type, value, tb) = sys.exc_info()
        log_fatal("CA: Unable to initialise: %s" % value, \
                (type, value, tb))
    
    # Ensure there is a server key, and a client key for the web interface
    # and the pxeboot scripts
    for name in ["server", "ccsweb", "pxe-scripts"]:
        if not ca.ensureCertificateExists(name):
            log_fatal("CA: %s is a required key. Exiting!" % name)
Esempio n. 9
0
def checkUsage(*args, **kwargs):
    """Check customer data usage. Perform cap actions and warnings as 
       necessary"""
    session = getSession(ADMIN_SESSION_ID)

    #Do warnings first to ensure that a warning email in generated
    sql = "SELECT cs.*, c.* FROM broadband_account cs, customers c WHERE " \
            "cs.login_id=c.login_id AND cs.used_mb > cs.cap_at_mb*0.8 " \
            "AND cs.warning_sent=false AND cs.charged=true"
    res = session.query(sql, ())
    for state in res:
        doCapWarn(state)
        
    #Do any cap ations required
    sql = "SELECT cs.*, c.* FROM broadband_account cs, v_customers c WHERE " \
            "cs.login_id=c.login_id AND cs.used_mb > cs.cap_at_mb"
    res = session.query(sql, ())
    for state in res:
        doCapAction(state)
Esempio n. 10
0
    def __init__(self, session_id, service_id):
        """Initialises a new class for a specified service.

        The specified session must be valid and have appropriate access to
        the database for the tasks you intend to perform with the class. All
        database access / configuration manipulation triggered by this
        instance will pass through the specified session.
        """

        self._errMsg = ""
        self._commit = 0
        self._csInit = ""
        self._service = {}
        self._structure = {}

        session = getSession(session_id)
        if session is None:
            raise ccs_service_error("Invalid session id")
        self._session_id = session_id

        # See if the specified service id makes sense
        sql = "SELECT * FROM service WHERE service_id=%s"
        res = session.query(sql, (service_id))
        if len(res) < 1:
            raise ccs_service_error("Invalid service. Unable to retrieve " 
                "details")

        # Store details 
        if not hasattr(self, "service_id"):
            self.service_id = service_id
        if not hasattr(self, "serviceName"):
            self.serviceName = res[0]["service_name"]
        self._service = res[0]
        self._properties = {}

        # Read service property structure & data 
        res = session.query("SELECT * FROM service_prop WHERE service_id=%s", \
                (self.service_id))
        for prop in res:
            prop["value"] = None
            prop["network_value"] = None
            self._properties[prop["prop_name"]] = prop
Esempio n. 11
0
def doCapWarn(customer):
    session = getSession(ADMIN_SESSION_ID)

    sql = "SELECT cap_action, cap_param, cap_price, description FROM " \
            "billing_plan WHERE plan_id=%s"
    res = session.query(sql, (customer["plan_id"]))
    
    sql = "UPDATE broadband_account SET warning_sent='t' WHERE " \
        "login_id=%s"
    session.execute(sql,(customer["login_id"]))    

    subject = "Data Cap Notice: 80% used" 
    if res[0]["cap_action"] == "throttle":
        sendCapEmail(customer, subject, "throttle-warn")
    elif res[0]["cap_action"] == "purchase":
        customer["purchase_mb"] = res[0]["cap_param"]
        customer["purchase_price"] = "$%.2f" % (res[0]["cap_price"]*1.125)
        sendCapEmail(customer, subject, "purchase-warn")
    else:
        log_error("Unknown cap action '%s' for contact '%s'" % \
                (res[0]["cap_action"], customer['username']))
Esempio n. 12
0
    def __init__(self, session_id, host_id, interval=-1):
        """Initialises a new class for a specified host.

        The specified session must be valid and have appropriate access to
        the database.
        """
        
        self.lock = threading.RLock()

        session = getSession(session_id)
        if session is None:
            raise ccs_host_error("Invalid session id")
        self._session_id = session_id
        
        # See if the specified host id makes sense
        self.host = ccs_host(session_id, host_id)
        self._hostname = self.host["host_name"]
        self._ip = self.host["ip_address"]
        
        # Initialise the status information
        self.reachable = False
        self.operatingRevision = -1
        self.operatingRevisionStatus = STATUS_UNKNOWN
        self.operatingRevisionText = "Unknown"
        self.operatingRevisionHint = "Status not yet fetched"
        self.currentLoad = (-1,-1,-1)
        self.uptime = 0
        self.infoUpdatedAt = 0
        self.lastCheckedAt = time.time()
        self.interval = interval
        self.attempt = 0
        # Retrieve locally available information
        self.activeRevision = getActiveRevision(self._hostname)
        self.generatedRevision = getGeneratedRevision(self._hostname)

        # Will this object be updated?
        if interval != -1:
            ccs_host_status.queue(time.time(), self)
        log_info("Initialised host status monitor for %s" % self._hostname)
Esempio n. 13
0
def doBilling(as_at=None):
    """Called on the monthly event to charge the customer for their plan
    
    The monthly event is called on the last day of the month, so we add one
    to the current date to report the month that we're billing the customer
    for.
    """
    session = getSession(ADMIN_SESSION_ID)
    print "DOING BILLING"
    # Generate a billing record for each plan that is active in the system
    sql = "SELECT * FROM rollover"
    res = session.query(sql, ())

    n = timedelta(1)
    # Select all unbilled records up till NOW or the time specified, 
    # date the invoice one day earlier
    if as_at is None:
        datestr = datetime.strftime(datetime.now() + n, "%b %Y")
        db_time = datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
    else:
        datestr = datetime.fromtimestamp(as_at).strftime("%b %Y")
        db_time = datetime.fromtimestamp(as_at).strftime("%Y-%m-%d %H:%M:%S")

    
    for ro in res:
        if ro['charged'] == 'f':
            continue

        sql = "INSERT INTO billing_record (customer_id, record_date, " \
            "description, amount, quantity, record_type) VALUES ((select customer_id from customers where login_id=%s), %s, " \
            "%s, %s, 1, 'Plan');"
        desc = "%s: %s" % (datestr, ro["description"])
        try:
            session.execute(sql, (ro["login_id"], db_time, desc, ro["price"]))
        except:
            log_error("Failed to generate billing record for contact #%s" % \
                    ro["login_id"], sys.exc_info())
Esempio n. 14
0
def registerService(res_class, **kwargs):
    """Function to register a service
    
    The service must be derived from ccsd_service. The service must have
    at least one private member called serviceName that provides
    a textual description of the service. Other private members as described
    in the ccsd_service class must also be present.
    """
    session = getSession(ADMIN_SESSION_ID)
    
    try:
        name = res_class.serviceName

        if name in _services:
            raise ccs_service_error("Service name already registed!")

        # Check it is a child of ccsd_service
        if ccsd_service not in inspect.getmro(res_class):
            raise ccs_service_error("Invalid service class type!")

        # Check it has an entry in the service table
        res = session.query("SELECT * FROM service WHERE service_name=%s", \
                (name))
        if len(res) != 1:
            # Call the services setup function
            res_class.initialiseService()
        else:
            # Store the service ID in the class for reference
            res_class.service_id = res[0]["service_id"]

        # Register the service
        log_info("Registering service '%s'." % name)
        _services[name] = res_class

    except:
        log_error("Failed to register service!", sys.exc_info())
Esempio n. 15
0
def doCapAction(customer):
    """Called when a customer has exceeded their cap.

    Rate limits or purchases more data for the customer, based on their chosen
    plan.
    """
    session = getSession(ADMIN_SESSION_ID)

    sql = "SELECT cap_action, cap_param, cap_price, description FROM " \
            "billing_plan WHERE plan_id=%s"
    res = session.query(sql, (customer["plan_id"]))
    
    used_mb = int(customer["used_mb"])
    subject = "Data Cap Notice: %.1fGB used." % (used_mb/1024.0) 
    
    if res[0]["cap_action"] == "throttle":
        # Customer has chosen to be throttled
        current_plan =  customer["radius_plan"]
        if current_plan == res[0]["cap_param"]:
            # Already throttled
            return
        else:
            # Throttle and notify
            sql = "UPDATE broadband_account SET radius_plan=%s WHERE " \
                    "login_id=%s"
            session.execute(sql,(res[0]["cap_param"], customer["login_id"]))
            log_info("Moved '%s' to plan %s (throttled)" % \
                    (customer["username"], res[0]["description"]))
            # Tell the customer
            sendCapEmail(customer, subject, "throttle")
    elif res[0]["cap_action"] == "purchase":
        # Customer has chosen to purchase more data
        cap_at_mb = customer["cap_at_mb"]
        used_mb = customer["used_mb"]
        
        # If the customer is leeching at full rate, it's possible that we may
        # have to purchase multiple extra blocks of usage to get their cap
        # back above their current data usage.
        extra_mb = 0
        extra_price = 0
        while cap_at_mb < used_mb:
            try:
                sql = "UPDATE broadband_account SET cap_at_mb=cap_at_mb+%s " \
                        "WHERE login_id=%s"
                session.execute(sql,(res[0]["cap_param"],
                    customer["login_id"]))
                
                sql = "INSERT INTO billing_record (customer_id, record_date, " \
                        "description, amount, quantity,record_type) VALUES (%%s, " \
                        "CURRENT_TIMESTAMP, '%sMB data pack', %%s, 1, 'Data')" % \
                        (res[0]["cap_param"])
                session.execute(sql, (customer["customer_id"], 
                    res[0]["cap_price"]))
                cap_at_mb += res[0]["cap_param"]
                log_info("Purchased %sMB block for '%s'. New cap: %sMB." % \
                        (res[0]["cap_param"], customer["username"], cap_at_mb))
                extra_mb += res[0]["cap_param"]
                extra_price += res[0]["cap_price"]
            except:
                log_error("Failed to purchase new block for '%s'!" % \
                        customer["username"], sys.exc_info())
                break
        # Tell the customer
        customer["purchased_mb"] = extra_mb
        customer["purchased_price"] = "$%.2f" % (extra_price*1.125)
        sendCapEmail(customer, subject, "purchase")
    else:
        log_error("Unknown cap action '%s' for contact '%s'" % \
                (res[0]["cap_action"], customer['username']))
Esempio n. 16
0
def validateLink(session_id, details, isAdd=False):
    """Validates that the specified link details are correct"""

    # Link ID
    if "link_id" in details.keys():
        validateLinkId(session_id, details["link_id"])
    elif not isAdd:
        raise ccs_link_error("Link ID must be specified!")

    # Required props
    if isAdd:
        rprops = ["description", "type", "mode", "class_id", "network_address", "shared_network"]
        for prop in rprops:
            if prop not in details.keys():
                raise ccs_link_error("%s is a required link property!" % prop)

    # Check values
    if "description" in details.keys():
        if len(details["description"]) <= 0:
            raise ccs_link_error("Description must be specified!")
        if len(details["description"]) > 250:
            raise ccs_link_error("Description must be less than 250 chars!")

    if "mode" in details.keys() and "type" in details.keys():
        validateLinkMode(session_id, details["type"], details["mode"])
    if "class_id" in details.keys():
        validateLinkClassId(session_id, details["class_id"])
    if "type" in details.keys() and details["type"] in WIRELESS_LINK_TYPES:
        if "essid" in details.keys():
            if len(details["essid"]) <= 0:
                raise ccs_link_error("ESSID must be specified!")
            if len(details["essid"]) > 64:
                raise ccs_link_error("ESSID must be less than 64 chars!")
            essid_valid = re.compile("[a-zA-z0-9\-]*")
            if essid_valid.match(details["essid"]) == None:
                raise ccs_link_error("Invalid character in ESSID!")
        if "channel" in details.keys():
            try:
                c = int(details["channel"])
                if c == 0:
                    raise ccs_link_error("Channel must not be 0!")
            except:
                raise ccs_link_error("Invalid channel specified!")
        if "key" in details.keys() and details["key"] != "":
            if len(details["key"]) != 32:
                raise ccs_link_error("Key must be 32 characters!")
            key_valid = re.compile("[a-zA-z0-9]*")
            if key_valid.match(details["key"]) == None:
                raise ccs_link_error("Invalid character in key!")
    if "network_address" in details.keys() and len(details["network_address"]) > 0:
        validateCIDR(details["network_address"])

        session = getSession(session_id)
        if session is None:
            raise ccs_interface_error("Invalid session id")

        # Must ensure that no two links can have same ip range
        sql = "SELECT link_id FROM link WHERE network_address=%s"
        p = [details["network_address"]]
        if "link_id" in details.keys():
            sql += " AND link_id != %s"
            p.append(details["link_id"])
        res = session.query(sql, p)
        if len(res) != 0:
            raise ccs_link_error("Network address already in used!")

    if "shared_network" in details.keys():
        if details["shared_network"] not in ["t", "f"]:
            raise ccs_link_error("Shared Network flag must be 't' or 'f'!")

    # All OK
    return