diff --git a/ish/__init__.py b/ish/__init__.py index dae38d3..aaa8269 100644 --- a/ish/__init__.py +++ b/ish/__init__.py @@ -28,92 +28,92 @@ def auth_info(): - """ - Description: Get the information necessary to determine whether a user is - properly logged in + """ + Description: Get the information necessary to determine whether a user is + properly logged in - :rtype: tuple: - :return: A tuple of the exit code of 'klist -s' and of the output of 'klist' - """ + :rtype: tuple: + :return: A tuple of the exit code of 'klist -s' and of the output of 'klist' + """ - import subprocess - from tempfile import TemporaryFile - std = TemporaryFile() - subprocess.call(['klist'], stdout=std) - exit_status = subprocess.call(['klist', '-s']) - std.seek(0) - out_lines = std.read().split('\n') - std.close() - ret = {} - ret['code'] = exit_status - for line in out_lines: - if line.startswith('Default'): - (ret['user'], ret['domain']) = line.lower().split(' ')[2].split('@') - return ret + import subprocess + from tempfile import TemporaryFile + std = TemporaryFile() + subprocess.call(['klist'], stdout=std) + exit_status = subprocess.call(['klist', '-s']) + std.seek(0) + out_lines = std.read().split('\n') + std.close() + ret = {} + ret['code'] = exit_status + for line in out_lines: + if line.startswith('Default'): + (ret['user'], ret['domain']) = line.lower().split(' ')[2].split('@') + return ret def check_auth(): - auth = auth_info() - if auth['code'] is not 0: - return False - if not auth['domain'] == "csh.rit.edu": - return False - return True + autho = auth_info() + if autho['code'] is not 0: + return False + if not autho['domain'] == "csh.rit.edu": + return False + return True def auth(): - auth = auth_info() - if auth['code'] is not 0: - return "Ticket expired or invalid. Exit and run kinit" - if not auth['domain'] == "csh.rit.edu": - return "Not in domain 'csh.rit.edu'" - return "Successfully authenticated" + autho = auth_info() + if autho['code'] is not 0: + return "Ticket expired or invalid. Exit and run kinit" + if not autho['domain'] == "csh.rit.edu": + return "Not in domain 'csh.rit.edu'" + return "Successfully authenticated" def get_username(): - auth = auth_info() - if auth['code'] is not 0: - return False - return auth['user'] + autho = auth_info() + if autho['code'] is not 0: + return False + return autho['user'] -def ish_prompt(p, required=True, constraints=None): - val = None - while not val: - if not constraints: - val = raw_input(p + ": ") - else: - #alphabetize everything, lower case - constraints = sorted(constraints, key=str.lower) - opt_lists = [] - options = {} - #create a dictionary with numbers as keys, the constraints as values - if len(constraints) <= 30: - options = dict(zip(range(len(constraints)), constraints)) - else: - for count in range(int(len(constraints) / 30 + 1)): - opt_lists.append(constraints[count * 30:count * 30 + 30]) - for segment in opt_lists: - options[len(options)] = "%s - %s" % (segment[0], segment[-1]) - #use a lambda to make options formatted like: - # [0]: this - # [1]: that - # [2]: other - prompt = ("From choices:\n" + ''.join(map(lambda (k, v): - " [%s]; %s\n" % (k, v), options.items())) + p + ": ") - try: - print required - val = int(raw_input(prompt)) - except ValueError: - if not required: - break - continue - if not val < len(options.values()): - val = None - elif len(constraints) <= 30: - val = options.values()[val] - else: - val = ish_prompt(p, required=required, constraints=opt_lists[val]) - if not required: - break - return val +def ish_prompt(pro, required=True, constraints=None): + val = None + while not val: + if not constraints: + val = raw_input(pro + ": ") + else: + #alphabetize everything, lower case + constraints = sorted(constraints, key=str.lower) + opt_lists = [] + options = {} + #create a dictionary with numbers as keys, the constraints as values + if len(constraints) <= 30: + options = dict(zip(range(len(constraints)), constraints)) + else: + for count in range(int(len(constraints) / 30 + 1)): + opt_lists.append(constraints[count * 30:count * 30 + 30]) + for segment in opt_lists: + options[len(options)] = "%s - %s" % (segment[0], segment[-1]) + #use a lambda to make options formatted like: + # [0]: this + # [1]: that + # [2]: other + prompt = ("From choices:\n" + ''.join(map(lambda (k, v): + " [%s]; %s\n" % (k, v), options.items())) + pro + ": ") + try: + print required + val = int(raw_input(prompt)) + except ValueError: + if not required: + break + continue + if not val < len(options.values()): + val = None + elif len(constraints) <= 30: + val = options.values()[val] + else: + val = ish_prompt(pro, required=required, constraints=opt_lists[val]) + if not required: + break + return val diff --git a/ish/resources/__init__.py b/ish/resources/__init__.py index 30ac605..037eaf5 100644 --- a/ish/resources/__init__.py +++ b/ish/resources/__init__.py @@ -29,345 +29,348 @@ class ImpulseDBError(Exception): - """ - Description: Raised whenever an Impulse query is rejected, typically because - some data does not validate. - """ - def __init__(self, message): - # Call the base class constructor with the parameters it needs - Exception.__init__(self, message) + """ + Description: Raised whenever an Impulse query is rejected, typically because + some data does not validate. + """ + def __init__(self, message): + # Call the base class constructor with the parameters it needs + Exception.__init__(self, message) class ConnectionSingleton(object): - _db_conn = None - - def __init__(self): - """ - Description: A singleton to manage database connection. - - It reads authentication information from the Impulse Shell configuration - file, which is located in /etc/ish.cfg - """ - super(ConnectionSingleton, self).__init__() - - @property - def db_conn(self): - if not self._db_conn: - from ConfigParser import ConfigParser - config = ConfigParser() - config.read(CONFIG_LOCATION) - self._db_conn = DBConnection( - config.get('DB', 'database'), - config.get('DB', 'user'), - config.get('DB', 'password'), - config.get('DB', 'host'), - config.get('DB', 'port')) - return self._db_conn - - def execute(self, query, results=False): - try: - return self.db_conn.api_query(query, results) - except InternalError, e: - raise ImpulseDBError(e.message) - finally: - self._db_conn = None + _db_conn = None + + def __init__(self): + """ + Description: A singleton to manage database connection. + + It reads authentication information from the Impulse Shell configuration + file, which is located in /etc/ish.cfg + """ + super(ConnectionSingleton, self).__init__() + + @property + def db_conn(self): + if not self._db_conn: + from ConfigParser import ConfigParser + config = ConfigParser() + config.read(CONFIG_LOCATION) + self._db_conn = DBConnection( + config.get('DB', 'database'), + config.get('DB', 'user'), + config.get('DB', 'password'), + config.get('DB', 'host'), + config.get('DB', 'port')) + return self._db_conn + + def execute(self, query, results=False): + try: + return self.db_conn.api_query(query, results) + except InternalError, error: + raise ImpulseDBError(error.message) + finally: + self._db_conn = None class DBConnection(object): - def __init__(self, dbname, uname, passwd, host, port): - """ - Description: A connection to PGSQL to run queries on. - """ - import psycopg2 - self.dbname = dbname - self.uname = uname - self.passwd = passwd - self.host = host - self.port = port - self.connection = psycopg2.connect(database=dbname, user=uname, - password=passwd, host=host, port=port) - - def api_query(self, query, results=False): - """ - Description: Run a query on the PGSQL connection. By default, no results - are returned. If results are desired/needed then be sure to call as: - api_query(query, results=True) - """ - try: - if self.connection.closed: - import psycopg2 - self.connection = psycopg2.connect(database=self.dbname, - user=self.uname, password=self.passwd, host=self.host, port=self.port) - cursor = self.connection.cursor() - - #initialize our session in the database so we can use Impulse's - #create/remove functions, and let Impulse deal with who's an RTP, etc - cursor.execute("SELECT api.initialize('%s');" % get_username()) - - #Finally actually run our query - cursor.execute(query) - if results: - ret = cursor.fetchall() - cursor.close() - - #commit the changes we made - self.connection.commit() - self.connection.close() - - except Exception, error: - raise error - if results: - return ret + def __init__(self, dbname, uname, passwd, host, port): + """ + Description: A connection to PGSQL to run queries on. + """ + import psycopg2 + self.dbname = dbname + self.uname = uname + self.passwd = passwd + self.host = host + self.port = port + self.connection = psycopg2.connect(database=dbname, user=uname, + password=passwd, host=host, port=port) + + def api_query(self, query, results=False): + """ + Description: Run a query on the PGSQL connection. By default, no results + are returned. If results are desired/needed then be sure to call as: + api_query(query, results=True) + """ + try: + if self.connection.closed: + import psycopg2 + self.connection = psycopg2.connect(database=self.dbname, + user=self.uname, password=self.passwd, host=self.host, + port=self.port) + cursor = self.connection.cursor() + + #initialize our session in the database so we can use Impulse's + #create/remove functions, and let Impulse deal with who's an RTP, etc + cursor.execute("SELECT api.initialize('%s');" % get_username()) + + #Finally actually run our query + cursor.execute(query) + if results: + ret = cursor.fetchall() + cursor.close() + + #commit the changes we made + self.connection.commit() + self.connection.close() + + except Exception, error: + raise error + if results: + return ret def singleton(cls): - """ - A decorator to make a class into a singleton. + """ + A decorator to make a class into a singleton. - When a class is wrapped in this decorator, this decorator will always make - any calls on that class (or an instance of it) goes to the same instance of - a given class. + When a class is wrapped in this decorator, this decorator will always make + any calls on that class (or an instance of it) goes to the same instance of + a given class. - See PEP 318 for clarification. - http://www.python.org/dev/peps/pep-0318/#examples - """ - instances = {} - def getinstance(): - if cls not in instances: - instances[cls] = cls() - return instances[cls] - return getinstance + See PEP 318 for clarification. + http://www.python.org/dev/peps/pep-0318/#examples + """ + instances = {} + def getinstance(): + if cls not in instances: + instances[cls] = cls() + return instances[cls] + return getinstance @singleton class ConstraintRetriever(dict): - pass + pass class ImmutabilityMeta(type): - """A metaclass to prevent the alteration of certain key attributes that - shouldn't be messed with.""" - - def __setattr__(cls, name, value): - """ - Description: Override the setattr method on any inheriting classes such - that certain properties cannot be altered, because that would break - stuff. - """ - immutables = ('table_name', 'schema_name', 'required_properties', - 'optional_properties', 'pkey', 'removal_query', 'modification_query', 'creation_query') - if name in immutables: - raise AttributeError("Cannot modify .%s" % name) - return type.__setattr__(cls, name, value) - - def __delattr__(cls, name): - """ - Description: Override the delattr method on any inheriting classes such - that certain properties cannot be deleted, because that would break - stuff. - """ - immutables = ('table_name', 'schema_name', 'required_properties', - 'optional_properties', 'pkey', 'removal_query', 'creation_query') - if name in immutables: - raise AttributeError("Cannot delete .%s" % name) - return type.__delattr__(cls, name) + """A metaclass to prevent the alteration of certain key attributes that + shouldn't be messed with.""" + + def __setattr__(mcs, name, value): + """ + Description: Override the setattr method on any inheriting classes such + that certain properties cannot be altered, because that would break + stuff. + """ + immutables = ('table_name', 'schema_name', 'required_properties', + 'optional_properties', 'pkey', 'removal_query', + 'modification_query', 'creation_query') + if name in immutables: + raise AttributeError("Cannot modify .%s" % name) + return type.__setattr__(mcs, name, value) + + def __delattr__(mcs, name): + """ + Description: Override the delattr method on any inheriting classes such + that certain properties cannot be deleted, because that would break + stuff. + """ + immutables = ('table_name', 'schema_name', 'required_properties', + 'optional_properties', 'pkey', 'removal_query', 'creation_query') + if name in immutables: + raise AttributeError("Cannot delete .%s" % name) + return type.__delattr__(mcs, name) class ImpulseObject(object): - _conn = ConnectionSingleton() - __metaclass__ = ImmutabilityMeta - - def __init__(self): - """ - Description: Parent Impulse Object, all other objects inherit from this - one and it provides things like immutable attributes, the put() method, - and the find() and search() methods. There should never be a reason to - instantiate a plain ImpulseObject, as it can't actually do anything on - its own. - """ - if str(self.__class__) == "": - raise NotImplementedError("This is a parent class.") - - def remove(self, debug=False): - """ - Description: Delete this object from the database. - This operation is not undoable, period. Be careful - """ - #run this query on the db - query = self.removal_query % (self.__dict__[self.pkey]) - if debug: - print query - self._conn.execute(query) - - @classmethod - def find(cls, pkey): - """Return a single object based on the primary key for the objects in - that table""" - if isinstance(cls, ImpulseObject): - raise NotImplementedError("Can't run this on a generic" + - " ImpulseObject.") - column_query = ("""select * from information_schema.columns""" + - """ where table_name = '%s'""") - # Get all the columns in the specified table - column_result = cls._conn.execute(column_query % cls.table_name, - results=True) - if not column_result: - raise Exception("Cannot find table %s" % cls.table_name) - # pull only the column names using list comprehension - columns = [res[3] for res in column_result] - - obj_query = """select * from %s.%s where %s = '%s'""" - obj = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name, - cls.pkey, pkey), results=True) - if not obj: - raise Exception("Cannot find object %s with key %s" % - (pkey, cls.pkey)) - - result = cls() - for col, val in zip(columns, obj[0]): - result.__dict__[col] = val - return result - - @classmethod - def all(cls): - """ - Description: Get every single object of a given type. This is a - generator ( http://docs.python.org/tutorial/classes.html#generators ) and - should be used like this: - all_systems = System.all() - first_system = s.next() - - When a generator has run out of items, it raises a StopIteration error. - Using a generator is much faster that returning a (potentially large) - list containing all of a particular type of object. - """ - if isinstance(cls, ImpulseObject): - raise NotImplementedError("Can't run this on a generic" + - " ImpulseObject.") - column_query = ("""select * from information_schema.columns""" + - """ where table_name = '%s'""") - # Get all the columns in the specified table - column_result = cls._conn.execute(column_query % cls.table_name, - results=True) - if not column_result: - raise Exception("Cannot find table %s" % cls.table_name) - # pull only the column names using list comprehension - columns = [res[3] for res in column_result] - - obj_query = """select * from %s.%s;""" - res = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name), - results=True) - if not res: - raise Exception("No results for query %s" % obj_query) - - for item in res: - obj = cls() - for col, val in zip(columns, item): - obj.__dict__[col] = val - yield obj - - @classmethod - def search(cls, **kwargs): - """Return a list objects that match parameters that aren't primary - keys - - For example: object.search(owner=some1, type=atype) - - It is possible to specify many parameters, as long as they are all valid - """ - if len(kwargs.items()) < 1: - raise Exception("This function requires search parameters") - if isinstance(cls, ImpulseObject): - raise NotImplementedError("Can't run this on a generic" + - " ImpulseObject.") - column_query = ("""select * from information_schema.columns""" + - """ where table_name = '%s'""") - # Get all the columns in the specified table - column_result = cls._conn.execute(column_query % cls.table_name, - results=True) - if not column_result: - raise Exception("Cannot find table %s" % cls.table_name) - # pull only the column names using list comprehension - columns = [res[3] for res in column_result] - - obj_query = """select * from %s.%s where %s = '%s' """ - additional = '' - for key, val in kwargs.items()[1:]: - additional += "and %s = '%s' " % (key, val) - additional += ';' - res = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name, - kwargs.items()[0][0], kwargs.items()[0][1]) + additional, results=True) - if not res: - return None - - results = [] - for item in res: - obj = cls() - for col, val in zip(columns, item): - obj.__dict__[col] = val - results.append(obj) - return results - - def get_constraint(self, attr): - """ - Description: Get all allowed values for a particular attribute. Great for - if you need to present all those options to a user, or are just curious - yourself. Used because hardcoding most values is not feasible, such as - valid usernames. - """ - try: - classname = str(self.__class__).split("'")[1].split('.')[-1] - return self._constraints[classname][attr] - except KeyError: - return () - - def configure(self): - """ - Description: Creates a series of prompts asking for values, and specifies - which are optional and which are not. Only use in places where user has - the ability to get data to STDIN. - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - #Display prompts the user for required properties - for prop in self.required_properties: - if prop in self._constraints[classname]: - self.__dict__[prop] = ish_prompt("Value for %s" % prop, - required=True, constraints=self._constraints[classname][prop]) - else: - self.__dict__[classname][prop] = ish_prompt("Value for %s" % prop, - required=True) - - #Display prompts the user for optional properties - for prop in self.optional_properties: - if prop in self._constraints[classname]: - self.__dict__[prop] = ish_prompt("Value for optional property %s" % - prop, required=False, constraints=self._constraints[classname][prop]) - else: - self.__dict__[prop] = ish_prompt("Value for optional property %s" % - prop, required=False) - - def enforce_constraints(self): - """ - Description: Enforce all constraints on properties that have them. If a - value is not within the given constraints, then a ValueError exception is - raised. It is good to check this before saving an object to the database. - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - for key in list(set(self._constraints[classname].keys()) & - set(self.__dict__.keys())): - if self.__dict__[key] not in self._constraints[classname][key]: - raise ValueError("Value '%s' is not within constraints for'%s'" % - (self.__dict__[key], key)) - - @property - def formatted_date_modified(self): - from datetime import datetime - if self.date_modified.strftime("%D") == datetime.now().strftime("%D"): - return self.date_modified.strftime("%H:%M") - return self.date_modified.strftime("%Y-%m-%d") - - def __repr__(self): - if self.name: - return self.name - else: - return object.__repr__(self) + _conn = ConnectionSingleton() + __metaclass__ = ImmutabilityMeta + + def __init__(self): + """ + Description: Parent Impulse Object, all other objects inherit from this + one and it provides things like immutable attributes, the put() method, + and the find() and search() methods. There should never be a reason to + instantiate a plain ImpulseObject, as it can't actually do anything on + its own. + """ + if str(self.__class__) == "": + raise NotImplementedError("This is a parent class.") + + def remove(self, debug=False): + """ + Description: Delete this object from the database. + This operation is not undoable, period. Be careful + """ + #run this query on the db + query = self.removal_query % (self.__dict__[self.pkey]) + if debug: + print query + self._conn.execute(query) + + @classmethod + def find(cls, pkey): + """Return a single object based on the primary key for the objects in + that table""" + if isinstance(cls, ImpulseObject): + raise NotImplementedError("Can't run this on a generic" + + " ImpulseObject.") + column_query = ("""select * from information_schema.columns""" + + """ where table_name = '%s'""") + # Get all the columns in the specified table + column_result = cls._conn.execute(column_query % cls.table_name, + results=True) + if not column_result: + raise Exception("Cannot find table %s" % cls.table_name) + # pull only the column names using list comprehension + columns = [res[3] for res in column_result] + + obj_query = """select * from %s.%s where %s = '%s'""" + obj = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name, + cls.pkey, pkey), results=True) + if not obj: + raise Exception("Cannot find object %s with key %s" % + (pkey, cls.pkey)) + + result = cls() + for col, val in zip(columns, obj[0]): + result.__dict__[col] = val + return result + + @classmethod + def all(cls): + """ + Description: Get every single object of a given type. This is a + generator ( http://docs.python.org/tutorial/classes.html#generators ) and + should be used like this: + all_systems = System.all() + first_system = s.next() + + When a generator has run out of items, it raises a StopIteration error. + Using a generator is much faster that returning a (potentially large) + list containing all of a particular type of object. + """ + if isinstance(cls, ImpulseObject): + raise NotImplementedError("Can't run this on a generic" + + " ImpulseObject.") + column_query = ("""select * from information_schema.columns""" + + """ where table_name = '%s'""") + # Get all the columns in the specified table + column_result = cls._conn.execute(column_query % cls.table_name, + results=True) + if not column_result: + raise Exception("Cannot find table %s" % cls.table_name) + # pull only the column names using list comprehension + columns = [res[3] for res in column_result] + + obj_query = """select * from %s.%s;""" + res = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name), + results=True) + if not res: + raise Exception("No results for query %s" % obj_query) + + for item in res: + obj = cls() + for col, val in zip(columns, item): + obj.__dict__[col] = val + yield obj + + @classmethod + def search(cls, **kwargs): + """Return a list objects that match parameters that aren't primary + keys + + For example: object.search(owner=some1, type=atype) + + It is possible to specify many parameters, as long as they are all valid + """ + if len(kwargs.items()) < 1: + raise Exception("This function requires search parameters") + if isinstance(cls, ImpulseObject): + raise NotImplementedError("Can't run this on a generic" + + " ImpulseObject.") + column_query = ("""select * from information_schema.columns""" + + """ where table_name = '%s'""") + # Get all the columns in the specified table + column_result = cls._conn.execute(column_query % cls.table_name, + results=True) + if not column_result: + raise Exception("Cannot find table %s" % cls.table_name) + # pull only the column names using list comprehension + columns = [res[3] for res in column_result] + + obj_query = """select * from %s.%s where %s = '%s' """ + additional = '' + for key, val in kwargs.items()[1:]: + additional += "and %s = '%s' " % (key, val) + additional += ';' + res = cls._conn.execute(obj_query % (cls.schema_name, cls.table_name, + kwargs.items()[0][0], kwargs.items()[0][1]) + additional, results=True) + if not res: + return None + + results = [] + for item in res: + obj = cls() + for col, val in zip(columns, item): + obj.__dict__[col] = val + results.append(obj) + return results + + def get_constraint(self, attr): + """ + Description: Get all allowed values for a particular attribute. Great for + if you need to present all those options to a user, or are just curious + yourself. Used because hardcoding most values is not feasible, such as + valid usernames. + """ + try: + classname = str(self.__class__).split("'")[1].split('.')[-1] + return self._constraints[classname][attr] + except KeyError: + return () + + def configure(self): + """ + Description: Creates a series of prompts asking for values, and specifies + which are optional and which are not. Only use in places where user has + the ability to get data to STDIN. + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + #Display prompts the user for required properties + for prop in self.required_properties: + if prop in self._constraints[classname]: + self.__dict__[prop] = ish_prompt("Value for %s" % prop, + required=True, constraints=self._constraints[classname][prop]) + else: + self.__dict__[classname][prop] = ish_prompt("Value for %s" % prop, + required=True) + + #Display prompts the user for optional properties + for prop in self.optional_properties: + if prop in self._constraints[classname]: + self.__dict__[prop] = ish_prompt("Value for optional property %s" % + prop, required=False, + constraints=self._constraints[classname][prop]) + else: + self.__dict__[prop] = ish_prompt("Value for optional property %s" % + prop, required=False) + + def enforce_constraints(self): + """ + Description: Enforce all constraints on properties that have them. If a + value is not within the given constraints, then a ValueError exception is + raised. It is good to check this before saving an object to the database. + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + for key in list(set(self._constraints[classname].keys()) & + set(self.__dict__.keys())): + if self.__dict__[key] not in self._constraints[classname][key]: + raise ValueError("Value '%s' is not within constraints for'%s'" % + (self.__dict__[key], key)) + + @property + def formatted_date_modified(self): + from datetime import datetime + if self.date_modified.strftime("%D") == datetime.now().strftime("%D"): + return self.date_modified.strftime("%H:%M") + return self.date_modified.strftime("%Y-%m-%d") + + def __repr__(self): + if self.name: + return self.name + else: + return object.__repr__(self) diff --git a/ish/resources/address.py b/ish/resources/address.py index ceddfdc..b53de0d 100644 --- a/ish/resources/address.py +++ b/ish/resources/address.py @@ -28,278 +28,281 @@ class Address(ImpulseObject): - """ - Description: The Address object, contains an address, what DNS family the - address is in, and how the address is assigned (static, dhcp, etc.) - """ - __metaclass__ = ImmutabilityMeta - pkey = "address" # What parameter does the deletion query require? - table_name = "interface_addresses" - schema_name = 'systems' - required_properties = ('address', 'isprimary', 'family', 'config') - optional_properties = ('mac', 'class', 'comment') - # Query that removes the object - removal_query = """SELECT api.remove_interface_address('%s');""" - modification_query = ("SELECT api.modify_interface_address('{address}', '{field}', '{val}')") - creation_query = ("""SELECT api.create_interface_address(""" + - """'{mac}', '{address}', '{config}', '{klass}', '{isprimary}',""" + - """'{comment}');""") # Query to create an object - _constraints = None - mac = None # MAC address of the interface - address = None - config = None - family = None - isprimary = False - comment = None # Comment on the system (or NULL for no comment) + """ + Description: The Address object, contains an address, what DNS family the + address is in, and how the address is assigned (static, dhcp, etc.) + """ + __metaclass__ = ImmutabilityMeta + pkey = "address" # What parameter does the deletion query require? + table_name = "interface_addresses" + schema_name = 'systems' + required_properties = ('address', 'isprimary', 'family', 'config') + optional_properties = ('mac', 'class', 'comment') + # Query that removes the object + removal_query = """SELECT api.remove_interface_address('%s');""" + modification_query = ("SELECT api.modify_interface_address('{address}'," + + " '{field}', '{val}')") + creation_query = ("""SELECT api.create_interface_address(""" + + """'{mac}', '{address}', '{config}', '{klass}', '{isprimary}',""" + + """'{comment}');""") # Query to create an object + _constraints = None + mac = None # MAC address of the interface + address = None + config = None + family = None + isprimary = False + comment = None # Comment on the system (or NULL for no comment) - @property - def constraints(self): - """ - Description: Return a dict of all the constraints for a given object - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - if not self._constraints: - self._constraints = ConstraintRetriever() - try: - return self._constraints[classname] - except KeyError: - pass - self._constraints[classname] = { - "config": reduce(lambda a, b: a + b, self._conn.execute( - "SELECT config FROM dhcp.config_types;", results=True)), - "class": reduce(lambda a, b: a + b, self._conn.execute( - "SELECT class FROM dhcp.classes;", results=True)), - } - return self._constraints[classname] + @property + def constraints(self): + """ + Description: Return a dict of all the constraints for a given object + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + if not self._constraints: + self._constraints = ConstraintRetriever() + try: + return self._constraints[classname] + except KeyError: + self._constraints[classname] = { + "config": reduce(lambda a, b: a + b, self._conn.execute( + "SELECT config FROM dhcp.config_types;", results=True)), + "class": reduce(lambda a, b: a + b, self._conn.execute( + "SELECT class FROM dhcp.classes;", results=True)), + } + return self._constraints[classname] - def __init__(self, mac=None, name=None, family=None, - address_class=None, config=None, comment=None): - """ - Description: Create an Address object, can accept arguments or not - """ - self.mac = mac - setattr(self, 'class', address_class) - if not address_class: - setattr(self, 'class', 'default') - self.name = name - self.family = family - self.config = config - self.comment = comment - self.constraints - ImpulseObject.__init__(self) + def __init__(self, mac=None, name=None, family=None, + address_class=None, config=None, comment=None): + """ + Description: Create an Address object, can accept arguments or not + """ + self.mac = mac + setattr(self, 'class', address_class) + if not address_class: + setattr(self, 'class', 'default') + self.name = name + self.family = family + self.config = config + self.comment = comment + self.constraints + ImpulseObject.__init__(self) - def put(self): - """ - Description: Save the Address object to the Impulse database - """ - self.enforce_constraints() - try: - self.enforce_constraints() - except ValueError: - return False - if not self.comment: - self.comment = "NULL" - query = self.creation_query.format(mac=self.mac, address=self.address, - config=self.config, klass=getattr(self, 'class'), - isprimary=self.isprimary, comment=self.comment) - try: - self._conn.execute(query) - except IntegrityError: - for key in self.optional_properties + self.required_properties: - query = self.modification_query.format(address=self.address, field=key, val=self.__dict__[key]) - self._conn.execute(query) - obj = self.find(self.address) - self.__dict__ = obj .__dict__ - return obj + def put(self): + """ + Description: Save the Address object to the Impulse database + """ + self.enforce_constraints() + try: + self.enforce_constraints() + except ValueError: + return False + if not self.comment: + self.comment = "NULL" + query = self.creation_query.format(mac=self.mac, address=self.address, + config=self.config, klass=getattr(self, 'class'), + isprimary=self.isprimary, comment=self.comment) + try: + self._conn.execute(query) + except IntegrityError: + for key in self.optional_properties + self.required_properties: + query = self.modification_query.format(address=self.address, + field=key, val=self.__dict__[key]) + self._conn.execute(query) + obj = self.find(self.address) + self.__dict__ = obj .__dict__ + return obj - def use_unused_address(self, ipr=None): - """ - Description: Grab an unused address from an IPRange, specified by its name - If no parameters are given, it puts up a prompt to ask for the IPRange to use - If an IPRange object is given, it gets the address from that object - If a string is given, it gets the IPRange with the name specified - returns the address that was used, and assigns the free address to the - "Address.address" attribute - """ - if not ipr: - ipr = ish_prompt("Select an IP Range", constraints=[i.name for i in IPRange.all()]) - if not isinstance(IPRange, ipr): - iprange = IPRange.find(ipr) - else: iprange = ipr - self.address = iprange.get_unused_address() - return self.address + def use_unused_address(self, ipr=None): + """ + Description: Grab an unused address from an IPRange, specified by its name + If no parameters are given, it puts up a prompt to ask for the IPRange to use + If an IPRange object is given, it gets the address from that object + If a string is given, it gets the IPRange with the name specified + returns the address that was used, and assigns the free address to the + "Address.address" attribute + """ + if not ipr: + ipr = ish_prompt("Select an IP Range", + constraints=[i.name for i in IPRange.all()]) + if not isinstance(IPRange, ipr): + iprange = IPRange.find(ipr) + else: iprange = ipr + self.address = iprange.get_unused_address() + return self.address class Subnet(ImpulseObject): - """ - Description: A Subnet object, including the address range - """ - __metaclass__ = ImmutabilityMeta - pkey = "subnet" # What parameter does the deletion query require? - table_name = "subnets" - schema_name = 'ip' - required_properties = ('subnet', 'name', 'autogen', 'dhcp', 'zone', - 'owner') - optional_properties = ('comment',) - removal_parameter = 'subnet' - # Query that removes the object - removal_query = """SELECT api.remove_ip_subnet('%s');""" - modification_query = ("SELECT api.modify_ip_subnet('{subnet}', '{field}', '{val}')") - creation_query = ("""SELECT api.create_ip_subnet(""" + - """'{subnet}', '{name}', '{comment}', '{autogen}', '{dhcp}',""" + - """'{zone}', '{owner}');""") # Query to create an object - _constraints = None - subnet = None # The subnet in CIDR notation - name = None # The name of this subnet - comment = None # A comment on the subnet (or NULL for no comment) - autogen = None # Autopopulate the IP addresses table (adv. use only) - dhcp = None # TRUE to allow this subnet for DHCP, FALSE for not - zone = None # DNS zone to associate with this subnet - owner = None # The owner of the subnet (or NULL for current user) + """ + Description: A Subnet object, including the address range + """ + __metaclass__ = ImmutabilityMeta + pkey = "subnet" # What parameter does the deletion query require? + table_name = "subnets" + schema_name = 'ip' + required_properties = ('subnet', 'name', 'autogen', 'dhcp', 'zone', + 'owner') + optional_properties = ('comment',) + removal_parameter = 'subnet' + # Query that removes the object + removal_query = """SELECT api.remove_ip_subnet('%s');""" + modification_query = ("SELECT api.modify_ip_subnet('{subnet}', '{field}', '{val}')") + creation_query = ("""SELECT api.create_ip_subnet(""" + + """'{subnet}', '{name}', '{comment}', '{autogen}', '{dhcp}',""" + + """'{zone}', '{owner}');""") # Query to create an object + _constraints = None + subnet = None # The subnet in CIDR notation + name = None # The name of this subnet + comment = None # A comment on the subnet (or NULL for no comment) + autogen = None # Autopopulate the IP addresses table (adv. use only) + dhcp = None # TRUE to allow this subnet for DHCP, FALSE for not + zone = None # DNS zone to associate with this subnet + owner = None # The owner of the subnet (or NULL for current user) - @property - def constraints(self): - """ - Description: Return a dict of all the constraints for a given object - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - if not self._constraints: - self._constraints = ConstraintRetriever() - try: - return self._constraints[classname] - except KeyError: - pass - self._constraints[classname] = { - "autogen": ('TRUE', 'FALSE'), - "dhcp": ('TRUE', 'FALSE'), - } - return self._constraints[classname] + @property + def constraints(self): + """ + Description: Return a dict of all the constraints for a given object + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + if not self._constraints: + self._constraints = ConstraintRetriever() + try: + return self._constraints[classname] + except KeyError: + self._constraints[classname] = { + "autogen": ('TRUE', 'FALSE'), + "dhcp": ('TRUE', 'FALSE'), + } + return self._constraints[classname] - def __init__(self, subnet=None, name=None, comment=None, autogen=None, - dhcp=None, zone=None, owner=None): - """ - Description: Create a Subnet object, can accept arguments or not - """ - self.subnet = subnet - self.name = name - self.comment = comment - self.autogen = autogen - self.dhcp = dhcp - self.zone = zone - self.owner = owner - self.constraints - ImpulseObject.__init__(self) + def __init__(self, subnet=None, name=None, comment=None, autogen=None, + dhcp=None, zone=None, owner=None): + """ + Description: Create a Subnet object, can accept arguments or not + """ + self.subnet = subnet + self.name = name + self.comment = comment + self.autogen = autogen + self.dhcp = dhcp + self.zone = zone + self.owner = owner + self.constraints + ImpulseObject.__init__(self) - def put(self): - """ - Description: Save this object to the Impulse database - """ - try: - self.enforce_constraints() - except ValueError: - return False - if not self.comment: - self.comment = "NULL" - query = self.creation_query.format(name=self.name, - first_ip=self.first_ip, last_ip=self.last_ip, subnet=self.subnet, - use=self.use, in_class=getattr(self, 'class'), - comment=self.comment) - try: - self._conn.execute(query) - except IntegrityError: - for key in self.optional_properties + self.required_properties: - query = self.modification_query.format(subnet=self.subnet, field=key, val=self.__dict__[key]) - self._conn.execute(query) - obj = self.find(self.system_name) - self.__dict__ = obj .__dict__ - return obj + def put(self): + """ + Description: Save this object to the Impulse database + """ + try: + self.enforce_constraints() + except ValueError: + return False + if not self.comment: + self.comment = "NULL" + query = self.creation_query.format(name=self.name, + first_ip=self.first_ip, last_ip=self.last_ip, subnet=self.subnet, + use=self.use, in_class=getattr(self, 'class'), + comment=self.comment) + try: + self._conn.execute(query) + except IntegrityError: + for key in self.optional_properties + self.required_properties: + query = self.modification_query.format(subnet=self.subnet, + field=key, val=self.__dict__[key]) + self._conn.execute(query) + obj = self.find(self.system_name) + self.__dict__ = obj .__dict__ + return obj class IPRange(ImpulseObject): - """ - Description: an IPRange object, which only contains a first and last IP, - along with their subnet and a description of what they are intended to be - used for. - """ - __metaclass__ = ImmutabilityMeta - pkey = "name" # What parameter does the deletion query require? - table_name = "ranges" - schema_name = 'ip' - required_properties = ('name', 'first_ip', 'last_ip', 'subnet', 'use', - 'class') - optional_properties = ('comment',) - removal_parameter = "name" - # Query that removes the object - removal_query = """SELECT api.remove_ip_range('%s');""" - modification_query = ("SELECT api.modify_ip_range('{name}', '{field}', '{val}')") - creation_query = ("""SELECT api.create_ip_range(""" + - """'{name}', '{first_ip}', '{last_ip}', '{subnet}', '{use}',""" + - """'{in_class}', '{comment}');""") # Query to create an object - _constraints = None - name = None # The name of the range - first_ip = None # The first IP address of the range - last_ip = None # The last IP address of the range - subnet = None # The subnet containing the range - use = None # A use (see documentation for uses) - comment = None # A comment on the range (or NULL for no comment + """ + Description: an IPRange object, which only contains a first and last IP, + along with their subnet and a description of what they are intended to be + used for. + """ + __metaclass__ = ImmutabilityMeta + pkey = "name" # What parameter does the deletion query require? + table_name = "ranges" + schema_name = 'ip' + required_properties = ('name', 'first_ip', 'last_ip', 'subnet', 'use', + 'class') + optional_properties = ('comment',) + removal_parameter = "name" + # Query that removes the object + removal_query = """SELECT api.remove_ip_range('%s');""" + modification_query = ("SELECT api.modify_ip_range('{name}', '{field}', '{val}')") + creation_query = ("""SELECT api.create_ip_range(""" + + """'{name}', '{first_ip}', '{last_ip}', '{subnet}', '{use}',""" + + """'{in_class}', '{comment}');""") # Query to create an object + _constraints = None + name = None # The name of the range + first_ip = None # The first IP address of the range + last_ip = None # The last IP address of the range + subnet = None # The subnet containing the range + use = None # A use (see documentation for uses) + comment = None # A comment on the range (or NULL for no comment - def __init__(self, name=None, first_ip=None, last_ip=None, subnet=None, - use=None, inp_class=None, comment=None): - """ - Description: Instantiate an IPRange object, does not require parameters - to be given. - """ - self.name = name - self.first_ip = first_ip - self.last_ip = last_ip - self.subnet = subnet - self.use = use - setattr(self, 'class', inp_class) - self.comment = comment - ImpulseObject.__init__(self) + def __init__(self, name=None, first_ip=None, last_ip=None, subnet=None, + use=None, inp_class=None, comment=None): + """ + Description: Instantiate an IPRange object, does not require parameters + to be given. + """ + self.name = name + self.first_ip = first_ip + self.last_ip = last_ip + self.subnet = subnet + self.use = use + setattr(self, 'class', inp_class) + self.comment = comment + ImpulseObject.__init__(self) - def put(self): - """ - Description: Save the IPRange object to the Impulse database - """ - try: - self.enforce_constraints() - except ValueError: - return False - if not self.comment: - self.comment = "NULL" - query = self.creation_query.format(name=self.name, - first_ip=self.first_ip, last_ip=self.last_ip, subnet=self.subnet, - use=self.use, in_class=getattr(self, 'class'), - comment=self.comment) - try: - self._conn.execute(query) - except IntegrityError: - for key in self.optional_properties + self.required_properties: - query = self.modification_query.format(name=self.name, field=key, val=self.__dict__[key]) - self._conn.execute(query) - obj = self.find(self.system_name) - self.__dict__ = obj .__dict__ - return obj + def put(self): + """ + Description: Save the IPRange object to the Impulse database + """ + try: + self.enforce_constraints() + except ValueError: + return False + if not self.comment: + self.comment = "NULL" + query = self.creation_query.format(name=self.name, + first_ip=self.first_ip, last_ip=self.last_ip, subnet=self.subnet, + use=self.use, in_class=getattr(self, 'class'), + comment=self.comment) + try: + self._conn.execute(query) + except IntegrityError: + for key in self.optional_properties + self.required_properties: + query = self.modification_query.format(name=self.name, + field=key, val=self.__dict__[key]) + self._conn.execute(query) + obj = self.find(self.system_name) + self.__dict__ = obj .__dict__ + return obj - def get_unused_address(self): - """ - Description: Returns the first unused address in a given IPRange. If - there is no unused address, raises ImpulseError. - """ - query = "SELECT api.get_address_from_range('%s');" - # If there are no unused addresses, this will throw an ImpulseError - addr = self._conn.execute(query % self.name, results=True)[0][0] - return addr + def get_unused_address(self): + """ + Description: Returns the first unused address in a given IPRange. If + there is no unused address, raises ImpulseError. + """ + query = "SELECT api.get_address_from_range('%s');" + # If there are no unused addresses, this will throw an ImpulseError + addr = self._conn.execute(query % self.name, results=True)[0][0] + return addr #class AddressRange(ImpulseObject): - #input_first_ip = None # First address of the range - #input_last_ip = None # Last address of the range - #input_subnet = None # Subnet containign the range + #input_first_ip = None # First address of the range + #input_last_ip = None # Last address of the range + #input_subnet = None # Subnet containign the range - #def __init__(self, first_ip=None, last_ip=None, subnet=None): - #self.input_first_ip = first_ip - #self.input_last_ip = last_ip - #self.input_subnet = subnet + #def __init__(self, first_ip=None, last_ip=None, subnet=None): + #self.input_first_ip = first_ip + #self.input_last_ip = last_ip + #self.input_subnet = subnet diff --git a/ish/resources/system.py b/ish/resources/system.py index e01380f..cd1297c 100644 --- a/ish/resources/system.py +++ b/ish/resources/system.py @@ -29,270 +29,271 @@ class System(ImpulseObject): - """ - Description: A System object, represents a system in Impulse - Required properties (to be successfully saved): - system_name: the name of the system (must be DNS-safe) - type: The system type for a full list try System.get_constraint('type') - Some valid ones are: Laptop, Server, Desktop, Firewall, Switch, etc... - os_name: The name of the operating system, again, System.get_constraint - is your friend - Some valid ones: Windows 7, Fedora, Debian, Arch, Slackware, and Solaris + """ + Description: A System object, represents a system in Impulse + Required properties (to be successfully saved): + system_name: the name of the system (must be DNS-safe) + type: The system type for a full list try System.get_constraint('type') + Some valid ones are: Laptop, Server, Desktop, Firewall, Switch, etc... + os_name: The name of the operating system, again, System.get_constraint + is your friend + Some valid ones: Windows 7, Fedora, Debian, Arch, Slackware, and Solaris - A system has several other handy attributes - """ + A system has several other handy attributes + """ - __metaclass__ = ImmutabilityMeta - pkey = "system_name" # What parameter does the deletion query require? - table_name = 'systems' - schema_name = 'systems' - required_properties = ('system_name', 'type', 'os_name') - optional_properties = ('comment', ) - # Query to remove the object - removal_query = """SELECT api.remove_system('%s');""" - modification_query = ("SELECT api.modify_system('{system_name}', '{field}', '{val}')") - creation_query = ("SELECT api.create_system('{system_name}', '{owner}'," - + "'{sys_type}', '{os_name}', '{comment}');") # Query to create an object + __metaclass__ = ImmutabilityMeta + pkey = "system_name" # What parameter does the deletion query require? + table_name = 'systems' + schema_name = 'systems' + required_properties = ('system_name', 'type', 'os_name') + optional_properties = ('comment', ) + # Query to remove the object + removal_query = """SELECT api.remove_system('%s');""" + modification_query = ("SELECT api.modify_system('{system_name}', '{field}', '{val}')") + creation_query = ("SELECT api.create_system('{system_name}', '{owner}'," + + "'{sys_type}', '{os_name}', '{comment}');") # Query to create an object - _interfaces = [] + _interfaces = [] - system_name = None # Name of the system - owner = None # Owning user (NULL for current authenticated user) - type = None # Server, Desktop, Laptop, etc - os_name = None # Primary operating system - comment = None # Comment on the system (or NULL for no comment) - _constraints = None - _address = None - _mac = None + system_name = None # Name of the system + owner = None # Owning user (NULL for current authenticated user) + type = None # Server, Desktop, Laptop, etc + os_name = None # Primary operating system + comment = None # Comment on the system (or NULL for no comment) + _constraints = None + _address = None + _mac = None - @property - def constraints(self): - """ - Description: Return a dict of all the constraints for a given object - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - if not self._constraints: - self._constraints = ConstraintRetriever() - try: - return self._constraints[classname] - except KeyError: - pass - self._constraints[classname] = { - "type": reduce(lambda a, b: a + b, self._conn.execute( - "SELECT type FROM systems.device_types;", results=True)), - "os_name": reduce(lambda a, b: a + b, self._conn.execute( - "SELECT name FROM systems.os;", results=True)), - } - return self._constraints[classname] + @property + def constraints(self): + """ + Description: Return a dict of all the constraints for a given object + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + if not self._constraints: + self._constraints = ConstraintRetriever() + try: + return self._constraints[classname] + except KeyError: + self._constraints[classname] = { + "type": reduce(lambda a, b: a + b, self._conn.execute( + "SELECT type FROM systems.device_types;", results=True)), + "os_name": reduce(lambda a, b: a + b, self._conn.execute( + "SELECT name FROM systems.os;", results=True)), + } + return self._constraints[classname] - @property - def name(self): - return self.system_name + @property + def name(self): + return self.system_name - @property - def address(self): - """ - Description: Calculated property based on the first address of the first - interface. To get all addresses assigned to a system, do this instead: + @property + def address(self): + """ + Description: Calculated property based on the first address of the first + interface. To get all addresses assigned to a system, do this instead: - system = System.find('your_system') - #A system can have many interfaces, loop through them - for interface in system.interfaces: - #since an interface can have many addresses, loop through those too - for address_object in interface.addresses: - print address_object.address - """ + system = System.find('your_system') + #A system can have many interfaces, loop through them + for interface in system.interfaces: + #since an interface can have many addresses, loop through those too + for address_object in interface.addresses: + print address_object.address + """ - if self._address: - return self._address - try: - self._address = self.interfaces[0].addresses[0].address - return self._address - except IndexError: - return '' - except TypeError: - return '' + if self._address: + return self._address + try: + self._address = self.interfaces[0].addresses[0].address + return self._address + except IndexError: + return '' + except TypeError: + return '' - @property - def mac(self): - """ - Description: Calculated property based on the MAC address of the first - interface on the machine, to get all MAC addresses of interfaces on this - system you must loop through all interfaces, or get a list of them like - so: - system = System.find('your_system') - all_mac_addresses = [interface.mac for interface in system.interfaces] - """ + @property + def mac(self): + """ + Description: Calculated property based on the MAC address of the first + interface on the machine, to get all MAC addresses of interfaces on this + system you must loop through all interfaces, or get a list of them like + so: + system = System.find('your_system') + all_mac_addresses = [interface.mac for interface in system.interfaces] + """ - if self._mac: - return self._mac - try: - self._mac = self.interfaces[0].mac - return self._mac - except IndexError: - return '' - except TypeError: - return '' + if self._mac: + return self._mac + try: + self._mac = self.interfaces[0].mac + return self._mac + except IndexError: + return '' + except TypeError: + return '' - @property - def interfaces(self): - """ - Description: Calculated property that returns a list of all interfaces - associated with this system. If there are no interfaces, this value is a - NoneType - """ - if self._interfaces: - return self._interfaces - self._interfaces = Interface.search(system_name=self.system_name) - return self._interfaces + @property + def interfaces(self): + """ + Description: Calculated property that returns a list of all interfaces + associated with this system. If there are no interfaces, this value is a + NoneType + """ + if self._interfaces: + return self._interfaces + self._interfaces = Interface.search(system_name=self.system_name) + return self._interfaces - @interfaces.setter - def interfaces(self, value): - self.interfaces - if not isinstance(value, list): - value = [value, ] - value = filter(lambda val: isinstance(val, Interface), value) - for val in value: - setattr(val, 'system_name', self.system_name) - val.constraints - val.put() - if self._interfaces: - self._interfaces.extend(value) - else: - self.interfaces - if not self._interfaces: - self._interfaces = [] - self._interfaces.extend(value) + @interfaces.setter + def interfaces(self, value): + self.interfaces + if not isinstance(value, list): + value = [value, ] + value = filter(lambda val: isinstance(val, Interface), value) + for val in value: + setattr(val, 'system_name', self.system_name) + val.constraints + val.put() + if self._interfaces: + self._interfaces.extend(value) + else: + self.interfaces + if not self._interfaces: + self._interfaces = [] + self._interfaces.extend(value) - def __init__(self, name=None, sys_type=None, osname=None, comment=None): - """ - Description: The constructor for System. Passing in parameters is - allowed, but not required. Properties are only required when System.put() - is used, as that is when the object is stored in the Impulse database - """ - self.system_name = name - self.type = sys_type - self.os_name = osname - self.comment = comment - self.owner = get_username() - self.constraints - ImpulseObject.__init__(self) - if name and self.type and osname: - self.create() + def __init__(self, name=None, sys_type=None, osname=None, comment=None): + """ + Description: The constructor for System. Passing in parameters is + allowed, but not required. Properties are only required when System.put() + is used, as that is when the object is stored in the Impulse database + """ + self.system_name = name + self.type = sys_type + self.os_name = osname + self.comment = comment + self.owner = get_username() + self.constraints + ImpulseObject.__init__(self) + if name and self.type and osname: + self.create() - def put(self): - """ - Description: Save the System up to the Impulse database. - """ - try: - self.enforce_constraints() - except ValueError: - return False - if not self.comment: - self.comment = "NULL" - query = self.creation_query.format(system_name=self.system_name, - owner=self.owner, sys_type=self.type, os_name=self.os_name, - comment=self.comment) - try: - self._conn.execute(query) - except IntegrityError: - for key in self.optional_properties + self.required_properties: - query = self.modification_query.format(system_name=self.system_name, field=key, val=self.__dict__[key]) - self._conn.execute(query) - obj = self.find(self.system_name) - self.__dict__ = obj .__dict__ - return obj + def put(self): + """ + Description: Save the System up to the Impulse database. + """ + try: + self.enforce_constraints() + except ValueError: + return False + if not self.comment: + self.comment = "NULL" + query = self.creation_query.format(system_name=self.system_name, + owner=self.owner, sys_type=self.type, os_name=self.os_name, + comment=self.comment) + try: + self._conn.execute(query) + except IntegrityError: + for key in self.optional_properties + self.required_properties: + query = self.modification_query.format( + system_name=self.system_name, field=key, + val=self.__dict__[key]) + self._conn.execute(query) + obj = self.find(self.system_name) + self.__dict__ = obj .__dict__ + return obj class Interface(ImpulseObject): - __metaclass__ = ImmutabilityMeta - pkey = "mac" # What parameter does the deletion query require? - table_name = "interfaces" - schema_name = 'systems' - required_properties = ('name', 'mac') - optional_properties = ('system_name', 'comment') - # Query that removes the object - removal_query = """SELECT api.remove_interface('%s');""" - modification_query = ("SELECT api.modify_interface('{mac}', '{field}', '{val}')") - creation_query = ("""SELECT api.create_interface('{sys_name}', '{mac}', """ - + """'{name}', '{comment}');""") # Query to create an object - _constraints = None - _addresses = [] + __metaclass__ = ImmutabilityMeta + pkey = "mac" # What parameter does the deletion query require? + table_name = "interfaces" + schema_name = 'systems' + required_properties = ('name', 'mac') + optional_properties = ('system_name', 'comment') + # Query that removes the object + removal_query = """SELECT api.remove_interface('%s');""" + modification_query = ("SELECT api.modify_interface('{mac}', '{field}', '{val}')") + creation_query = ("""SELECT api.create_interface('{sys_name}', '{mac}', """ + + """'{name}', '{comment}');""") # Query to create an object + _constraints = None + _addresses = [] - mac = None # MAC address of the interface - name = None - system_name = None # Name of the system - comment = None # Comment on the system (or NULL for no comment) + mac = None # MAC address of the interface + name = None + system_name = None # Name of the system + comment = None # Comment on the system (or NULL for no comment) - @property - def constraints(self): - """ - Description: Return a dict of all the constraints for a given object - """ - classname = str(self.__class__).split("'")[1].split('.')[-1] - if not self._constraints: - self._constraints = ConstraintRetriever() - try: - return self._constraints[classname] - except KeyError: - pass - self._constraints[classname] = { - "system_name": reduce(lambda a, b: a + b, self._conn.execute( - "SELECT system_name FROM systems.systems;", results=True)), - } - return self._constraints[classname] + @property + def constraints(self): + """ + Description: Return a dict of all the constraints for a given object + """ + classname = str(self.__class__).split("'")[1].split('.')[-1] + if not self._constraints: + self._constraints = ConstraintRetriever() + try: + return self._constraints[classname] + except KeyError: + self._constraints[classname] = { + "system_name": reduce(lambda a, b: a + b, self._conn.execute( + "SELECT system_name FROM systems.systems;", results=True)), + } + return self._constraints[classname] - @property - def addresses(self): - if self._addresses: - return self._addresses - self._addresses = Address.search(mac=self.mac) - return self._addresses + @property + def addresses(self): + if self._addresses: + return self._addresses + self._addresses = Address.search(mac=self.mac) + return self._addresses - @addresses.setter - def addresses(self, value): - self.addresses - if not isinstance(value, list): - value = [value, ] - value = filter(lambda val: isinstance(val, Address), value) - for val in value: - setattr(val, 'mac', self.mac) - val.constraints - val.put() - if self._addresses: - self._addresses.extend(value) - else: - self.addresses - if not self._addresses: - self._addresses = [] - self._addresses.extend(value) + @addresses.setter + def addresses(self, value): + self.addresses + if not isinstance(value, list): + value = [value, ] + value = filter(lambda val: isinstance(val, Address), value) + for val in value: + setattr(val, 'mac', self.mac) + val.constraints + val.put() + if self._addresses: + self._addresses.extend(value) + else: + self.addresses + if not self._addresses: + self._addresses = [] + self._addresses.extend(value) - def __init__(self, mac=None, name=None, system_name=None, comment=None): - self.mac = mac - self.name = name - self.system_name = system_name - self.comment = comment - self.constraints - ImpulseObject.__init__(self) + def __init__(self, mac=None, name=None, system_name=None, comment=None): + self.mac = mac + self.name = name + self.system_name = system_name + self.comment = comment + self.constraints + ImpulseObject.__init__(self) - def put(self): - """ - Description: Save the System up to the Impulse database. - """ - try: - self.enforce_constraints() - except ValueError: - return False - if not self.comment: - self.comment = '' - query = self.creation_query.format(sys_name=self.system_name, mac=self.mac, - name=self.name, comment=self.comment) - try: - self._conn.execute(query) - except IntegrityError: - for key in self.optional_properties + self.required_properties: - query = self.modification_query.format(mac=self.mac, field=key, val=self.__dict__[key]) - self._conn.execute(query) - obj = self.find(self.mac) - self.__dict__ = obj.__dict__ - return obj + def put(self): + """ + Description: Save the System up to the Impulse database. + """ + try: + self.enforce_constraints() + except ValueError: + return False + if not self.comment: + self.comment = '' + query = self.creation_query.format(sys_name=self.system_name, mac=self.mac, + name=self.name, comment=self.comment) + try: + self._conn.execute(query) + except IntegrityError: + for key in self.optional_properties + self.required_properties: + query = self.modification_query.format(mac=self.mac, field=key, + val=self.__dict__[key]) + self._conn.execute(query) + obj = self.find(self.mac) + self.__dict__ = obj.__dict__ + return obj diff --git a/setup.py b/setup.py index 578d3d9..1f4337f 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. @@ -26,41 +26,42 @@ from ish import __version__ setup(name = "ish", - version = __version__, - description = "Impulse Shell", - long_description="A shell to access Cohoe's Impulse registration system ( https://github.com/cohoe/impulse )", - author = "Ryan Brown", - author_email = "ryansb@csh.rit.edu", - url = "https://github.com/ryansb/ish", - packages = find_packages(), - include_package_data = True, - package_data = { - '': ['conf/*.cfg'], - }, - scripts = [ - 'bin/ish', - 'bin/ipy-ish', - 'bin/impulse-quick-create', - 'bin/impulse-query', - ], - license = 'MIT', - platforms = 'Posix; MacOS X', - classifiers = [ 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - ], - keywords = [ - 'Impulse Shell', - 'ish', - 'impulse-shell', - 'system registration', - 'csh', - ], - dependency_links = [ - ], - install_requires = [ - 'psycopg2', - ], - ) + version = __version__, + description = "Impulse Shell", + long_description="A shell to access Cohoe's Impulse registration system" + + " ( https://github.com/cohoe/impulse )", + author = "Ryan Brown", + author_email = "ryansb@csh.rit.edu", + url = "https://github.com/ryansb/ish", + packages = find_packages(), + include_package_data = True, + package_data = { + '': ['conf/*.cfg'], + }, + scripts = [ + 'bin/ish', + 'bin/ipy-ish', + 'bin/impulse-quick-create', + 'bin/impulse-query', + ], + license = 'MIT', + platforms = 'Posix; MacOS X', + classifiers = [ 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + ], + keywords = [ + 'Impulse Shell', + 'ish', + 'impulse-shell', + 'system registration', + 'csh', + ], + dependency_links = [ + ], + install_requires = [ + 'psycopg2', + ], + ) diff --git a/tests/test_system.py b/tests/test_system.py index 4f25988..e5daf28 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -27,8 +27,8 @@ class TestSystemObject(unittest.TestCase): - def setUp(self): - pass + def setUp(self): + pass - def tearDown(self): - pass + def tearDown(self): + pass