add device-related DB storage & DB manager

ref #3069

Change-Id: I8a6c9f7c8c742b082ed92bf44a3afe002257084b
diff --git a/device_profile.py b/device_profile.py
index d6feec6..966886f 100644
--- a/device_profile.py
+++ b/device_profile.py
@@ -20,11 +20,20 @@
 """
 This module defines the DeviceProfile class with holds descriptors of device
 """
+from pyndn import Name
 
 class DeviceProfile(object):   
     
     def __init__(self, prefix = None, location = None, manufacturer = None, category = None, type_ = None, model = None, serialNumber = None, serviceProfileList = None):
-        '''initialize device profile.'''
+        '''initialize device profile.
+        param Name prefix: device's prefix
+        param str location: device's location 
+        param str manufacturer: device's manufacturer
+        param str category: device's category
+        param str type: device's type
+        param str model: device's model
+        parma str lsit serviceProfileList: the list of service profile names 
+        '''
         self._prefix = prefix
         self._location = location
         self._manufacturer = manufacturer
@@ -39,7 +48,7 @@
         self._metadata = ['prefix', 'location', 'manufacturer', 'category', 'type', 'model', 'serialNumber', 'serviceProfileList']
  
     def __str__(self):
-        info ='prefix: '+ self._prefix + '\nlocation: '+ self._location + '\nmanufacturer: ' + self._manufacturer + '\ncategory: ' + self._category + '\ntype: ' + self._type + '\nserial number: ' + self._serialNumber + '\nservice profile list: '
+        info ='prefix: '+ self._prefix.toUri() + '\nlocation: '+ str(self._location) + '\nmanufacturer: ' + str(self._manufacturer) + '\ncategory: ' + str(self._category) + '\ntype: ' + str(self._type) + '\nserial number: ' + str(self._serialNumber) + '\nservice profile list: '
         info += ', '.join(self._serviceProfileList)
         info += '\nmetadata: ' 
         info += ', '.join(self._metadata)
diff --git a/device_user_access_manager.py b/device_user_access_manager.py
new file mode 100644
index 0000000..fa989c9
--- /dev/null
+++ b/device_user_access_manager.py
@@ -0,0 +1,153 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014-2015 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU Lesser General Public License is in the file COPYING.
+
+"""
+This module defines the DeviceUserAccessManager class which is the interface of
+operations related to identity, keys, and certificates.
+"""
+
+import sys
+from pyndn.name import Name
+from device_profile import DeviceProfile
+from device_user_access_storage import DeviceUserAccessStorage
+
+class DeviceUserAccessManager(object):
+    """
+    Create a new DeviceUserAccessManager
+    """
+    def __init__(self, databaseFilePath = None):
+        deviceUserAccessStorage = DeviceUserAccessStorage(databaseFilePath)
+        self._deviceUserAccessStorage = deviceUserAccessStorage
+    
+    def createDevice(self, deviceProfile, seed, configurationToken, commandList = None):
+        """
+        Create a device in the database, including it's corresponding commands and serrvice profiles
+        @param DeviceProfile deviceProfile: the device profile
+        @param SymmetricKey seed: the seed of the device
+        @param SymmetricKey configurationToken: the configuration Token of the device
+        @param list commandList: the command list of the device, must have following structure: each element in the list should be a two-tuple (commandName, commandToken), the commandName should be a string, and commandToken should be a SymmetricKey. Here is an example of a valid commandList : [('turn_on', commandToken1), ('turn_off', commandToken2)]
+        return True if succeed, otherwise False
+        """
+        result = False
+
+        #add device
+        deviceId =  self._deviceUserAccessStorage.addDevice(deviceProfile, seed, configurationToken)  
+        if deviceId == 0:
+           raise RuntimeError("device already exists in database") 
+           return result
+        elif deviceId == -1:
+           raise RuntimeError("error occured during adding device into database") 
+           return result
+        
+        #add service profile
+        serviceProfileList = deviceProfile.getServiceProfileList()
+        for serviceProfileName in serviceProfileList:
+            serviceProfileId = self._deviceUserAccessStorage.addServiceProfile(deviceId, serviceProfileName)
+            if serviceProfileId == 0:
+                raise RuntimeError("service profile: " + serviceProfileName + " already exists in database")
+                return result
+            elif serviceProfileId == -1:
+                raise RuntimeError("error occured during adding service profile :" + serviceProfileName)
+                return result
+        #add command
+        for commandTuple in commandList:
+            commandId = self._deviceUserAccessStorage.addCommand(deviceId, commandTuple[0], commandTuple[1])
+            if commandId == 0:
+                raise RuntimeError("command: " + commandTuple[0] + " already exists in database")
+                return result
+            elif commandId == -1:
+                raise RuntimeError("error occured during adding command:" +commandTuple[0])
+                return result
+        result = True
+        return result
+
+    def getDeviceProfile(self, prefix):
+        """
+        get device profile of a specified device
+        :param Name prefix: the device prefix
+        :return device profile of the device if exists, otherwise return None
+        :rtype: DeviceProfile
+        """
+        deviceProfile = self._deviceUserAccessStorage.getDeviceProfileFromDevice(prefix)
+        if deviceProfile == None:
+            return deviceProfile
+        else:
+            # get device Id 
+            deviceId = self._deviceUserAccessStorage.getDeviceId(deviceProfile.getPrefix())
+            if deviceId == 0:
+                raise RuntimeError("device doesn't exist")
+             
+            serviceProfileList = self._deviceUserAccessStorage.getServiceProfilesOfDevice(deviceId)
+            deviceProfile.setServiceProfile(serviceProfileList)
+       
+        return deviceProfile
+
+    def getSeed(self, prefix):
+        """
+        get seed of a specified device
+        :param Name prefix: the device prefix
+        :return seed of the device if exists, otherwise return None
+        :rtype: SymmetricKey
+        """
+        return self._deviceUserAccessStorage.getSeed(prefix)
+
+    def getConfigurationToken(self, prefix):
+        """
+        get configuration token of a specified device
+        :param Name prefix: the device prefix
+        :return seed of the device if exists, otherwise return None
+        :rtype: SymmetricKey
+        """
+        return self._deviceUserAccessStorage.getConfigurationToken(prefix)
+
+    def getCommandToken(self, prefix, commandName):
+        """
+        get command token of a specified command 
+        :param Name prefix: the device prefix 
+        :param str commandName: device name of the command
+        :return command token if command existsm, otherwise None
+        :rtype SymmetricKey
+        """
+        deviceId = self._deviceUserAccessStorage.getDeviceId(prefix)
+        if deviceId == 0:
+            return None
+        return self._deviceUserAccessStorage.getCommandToken(deviceId, commandName)
+
+    def getCommandsOfDevice(self, prefix):
+        """
+        get all the commands of a specified device
+        :param Name prefix: the device prefix
+        :return command name list if any commands exist, otherwise None
+        """
+        deviceId = self._deviceUserAccessStorage.getDeviceId(prefix)
+        if deviceId == 0:
+            return None
+        return self._deviceUserAccessStorage.getCommandsOfDevice(deviceId)
+    
+    def getServiceProfilesOfDevice(self, prefix):
+        """
+        get all the service profiles of a specified device
+        :param Name prefix: the device prefix
+        :return service profiles name list if any service profiles exist, otherwise None
+        """
+        deviceId = self._deviceUserAccessStorage.getDeviceId(prefix)
+        if deviceId == 0:
+            return None
+        return self._deviceUserAccessStorage.getServiceProfilesOfDevice(deviceId)
+    
diff --git a/device_user_access_storage.py b/device_user_access_storage.py
new file mode 100644
index 0000000..fb0fd9e
--- /dev/null
+++ b/device_user_access_storage.py
@@ -0,0 +1,601 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Regents of the University of California.
+# Author: Teng Liang <philoliang2011@gmail.com> 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU General Public License is in the file COPYING.
+"""
+DeviceUserAccessStorage implements a basic storage of devices, users and access 
+"""
+
+import os
+import sqlite3
+from pyndn import Name
+from device_profile import DeviceProfile
+from hmac_key import HMACKey
+INIT_DEVICE_TABLE = ["""
+CREATE TABLE IF NOT EXISTS
+  Device(
+      id                             INTEGER,
+      prefix                         BLOB NOT NULL UNIQUE,
+      location                       BLOB,
+      category                       BLOB,
+      type                           BLOB,
+      model                          BLOB,
+      serial_number                  BLOB,
+      manufacturer                   BLOB,
+      seed_name                      BLOB NOT NULL,
+      seed_sequence                  BLOB NOT NULL,
+      seed_counter                   BLOB NOT NULL,
+      seed                           BLOB NOT NULL,
+      configuration_token            BLOB NOT NULL,
+      configuration_token_sequence   BLOB NOT NULL,
+      configuration_token_counter    BLOB NOT NULL,
+      
+      PRIMARY KEY (id)
+  );
+"""]
+
+INIT_COMMAND_TABLE = ["""
+CREATE TABLE IF NOT EXISTS
+  Command(
+      id                            INTEGER,
+      device_id                     INTEGER NOT NULL,
+      name                          BLOB NOT NULL,
+      command_token_name            BLOB NOT NULL,
+      command_token_sequence        BLOB NOT NULL,
+      command_token_counter         BLOB NOT NULL,
+      command_token                 BLOB NOT NULL,
+      
+      PRIMARY KEY(id)
+      FOREIGN KEY(device_id) REFERENCES Device(id)
+  );
+"""]
+
+INIT_SERVICE_PROFILE_TABLE = ["""
+CREATE TABLE IF NOT EXISTS
+  ServiceProfile(
+      id                            INTEGER,
+      device_id                     INTEGER NOT NULL,
+      name                          BLOB NOT NULL,
+      
+      PRIMARY KEY(id)
+      FOREIGN KEY(device_id) REFERENCES Device(id)
+  );
+"""]
+
+class DeviceUserAccessStorage(object):
+    """
+    Create a new DeviceUserStorage to work with an SQLite file.
+    :param str databaseFilePath: (optional) The path of the SQLite file. If ommitted, use the default path.
+    """
+    def __init__(self, databaseFilePath = None):
+        if databaseFilePath == None or databaseFilePath == "":
+            if not "HOME" in os.environ:
+                home = '.'
+            else:
+                home = os.environ["HOME"]
+            
+            dbDirectory = os.path.join(home, '.ndn')
+            if not os.path.exists(dbDirectory):
+                os.makedirs(dbDirectory)
+
+            databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+
+        self._database =  sqlite3.connect(databaseFilePath)
+        
+        #Check if the Device table exists
+        cursor = self._database.cursor()
+        cursor.execute("SELECT name FROM sqlite_master WHERE TYPE ='table' and NAME = 'Device'")
+        if cursor.fetchone() == None:
+            #no device table exists, create one
+            for command in INIT_DEVICE_TABLE:
+                self._database.execute(command)
+        cursor.close()
+        
+        #Check if the Command table exists
+        cursor = self._database.cursor()
+        cursor.execute("SELECT name FROM sqlite_master WHERE TYPE ='table' and NAME = 'Command'")
+        if cursor.fetchone() == None:
+            #no command table exists, create one
+            for command in INIT_COMMAND_TABLE:
+                self._database.execute(command)
+        cursor.close()
+
+        #Check if the ServiceProfile table exists
+        cursor = self._database.cursor()
+        cursor.execute("SELECT name FROM sqlite_master WHERE TYPE ='table' and NAME = 'ServiceProfile'")
+        if cursor.fetchone() == None:
+            #no ServiceProfile table exists, create one
+            for command in INIT_SERVICE_PROFILE_TABLE:
+                self._database.execute(command)
+        cursor.close()
+
+        self._database.commit()
+   
+    def doesTableExist(self, tableName):
+        """
+         check if table exists
+         :param str tableName
+         :return True if exists, otherwise False
+        """
+        selectOperation = "SELECT name FROM sqlite_master WHERE TYPE='table' and NAME='" + tableName +"'"
+        #print selectOperation
+        cursor = self._database.cursor()
+        cursor.execute(selectOperation)
+        if cursor.fetchone() == None:
+            result = False
+        else: 
+            result = True
+        cursor.close()
+        return result
+
+    def doesDeviceExist(self, prefix):
+        """
+        Check if the specified device already exists.
+       
+        :param Name prefix: the prefix of device
+        :return True if the device exists, otherwise False
+        :rtype: bool
+        """
+        result = False
+        #print prefix        
+        cursor = self._database.cursor()
+        cursor.execute("SELECT count(*) FROM Device WHERE prefix =?", (prefix.toUri(),))
+        (count,) = cursor.fetchone()
+        if count > 0:
+            result = True
+            #print 'device with %s is founnd, count %d' %(prefix, count)
+
+        cursor.close()
+        return result
+
+    def addDevice(self, deviceProfile, seed, configurationToken):
+        """ 
+        Add a new device to the Device table, do nothing if the device already exists
+        
+        :param DeviceProfile devicePorfile: the deviceProfile of the device
+        :param Key seed: the seed of the device
+        :param Key configurationToken: the configurationToken of the device
+        :return the device id if it's added successfully, 0 if the device already exists, otherwise -1
+        :rtype: INTEGER
+        """
+        result = -1
+        data = (deviceProfile.getPrefix().toUri(),
+               deviceProfile.getLocation(),
+               deviceProfile.getCategory(),
+               deviceProfile.getType(),
+               deviceProfile.getModel(),
+               deviceProfile.getManufacturer(),
+               deviceProfile.getSerialNumber(),
+               seed.getName(),
+               seed.getSequence(),
+               seed.getCounter(),
+               seed.getKey(),
+               configurationToken.getKey(),
+               configurationToken.getSequence(),
+               configurationToken.getCounter()
+              )     
+
+        #check if the device already exists, if yes return 0
+        prefixName = deviceProfile.getPrefix()
+        if self.doesDeviceExist(prefixName):
+            return 0
+
+        insertDevice = (
+            "INSERT INTO Device(prefix, location, category, type, model, manufacturer, serial_number, seed_name, seed_sequence, seed_counter,seed, configuration_token, configuration_token_sequence, configuration_token_counter)"
+            "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
+        )
+        cursor = self._database.cursor()
+        cursor.execute(insertDevice, data)
+        self._database.commit()
+        result = cursor.lastrowid
+        cursor.close()
+        return result
+
+    def getDeviceProfileFromDevice(self, prefix):
+        """
+        get the specified device profile
+        :param Name prefix: the device prefix
+        :return device profile of the device if exists, otherwise return None
+        :rtype: DeviceProfile
+        """   
+        operation = (
+            "Select location, category, type, model, manufacturer, serial_number FROM Device "
+            "WHERE prefix = ?"
+        )
+    
+        cursor = self._database.cursor() 
+        cursor.execute(operation, (prefix.toUri(),))
+        result = cursor.fetchone() 
+        cursor.close()
+        # print result
+        if result == None:
+            return None
+        else:               
+            deviceProfile = DeviceProfile(prefix)
+            deviceProfile.setLocation(result[0])
+            deviceProfile.setCategory(result[1])
+            deviceProfile.setType(result[2])
+            deviceProfile.setModel(result[3])
+            deviceProfile.setManufacturer(result[4])
+            deviceProfile.setSerialNumber(result[5])
+        
+        return deviceProfile
+               
+    def getSeed(self, prefix):
+        """
+        get the seed of the specified device        
+        :param Name prefix: the device prefix
+        :return seed of the device if exists, otherwise return None
+        :rtype: HMACKey
+
+        """
+        operation = (
+            "Select seed_name, seed_sequence, seed_counter, seed FROM Device "
+            "WHERE prefix = ?"
+        )
+
+        cursor = self._database.cursor()
+        cursor.execute(operation, (prefix.toUri(),))
+        result = cursor.fetchone()
+        cursor.close()
+        print result
+        if result == None:
+            return None
+        else:
+            seed = HMACKey(result[1], result[2], result[3], result[0]) 
+        return seed
+
+        # raise RuntimeError("getSeed is not implemented")
+
+    def getConfigurationToken(self, prefix):
+        """
+        get the seed of the specified device        
+        :param Name prefix: the device prefix
+        :return seed of the device if exists, otherwise return None
+        :rtype: HMACKey
+
+        """
+        operation = (
+            "Select configuration_token_sequence, configuration_token_counter, configuration_token FROM Device "
+            "WHERE prefix = ?"
+        )
+
+        cursor = self._database.cursor()
+        cursor.execute(operation, (prefix.toUri(),))
+        result = cursor.fetchone()
+        cursor.close()
+        print result
+        if result == None:
+            return None
+        else:
+            configurationToken = HMACKey(result[0], result[1], result[2])
+        return configurationToken
+        # raise RuntimeError("getConfigurationToken is not implemented")
+
+    def getDeviceEntry(self, prefix):
+        """
+        get the specified device entry
+        :param Name prefix: the device prefix
+        :return '' return the corresponding row of the device
+        :rtype: str
+        """        
+        cursor = self._database.cursor()
+        cursor.execute("SELECT * FROM Device WHERE prefix =?", (prefix.toUri(),))
+        row = cursor.fetchone()
+        cursor.close()
+        return row
+
+    def deleteDevice(self, prefix):
+        """
+        delete specified device.
+        :param Name prefix: The device prefix
+        :return: 1 if successful, 0 if no device to delete, otherwise -1.
+        :rtype: INTEGER	
+        """
+        result = -1
+        if not self.doesDeviceExist(prefix):
+            return 0
+        cursor = self._database.cursor()
+        deleteDevice = "DELETE FROM Device WHERE prefix=?"
+        cursor.execute(deleteDevice, (prefix.toUri(),))
+        self._database.commit()
+        cursor.close()
+        return 1
+   
+    def getDeviceId(self, prefix):
+        """
+        get the device id of some specified device
+        :param Name prefix: the device prefix
+        :return id of the device, 0 if the device doesn't exist
+        :rtype: INTEGER
+        """
+        if not self.doesDeviceExist(prefix):
+            return 0
+        cursor = self._database.cursor()
+        operation = "SELECT id FROM Device WHERE prefix=?"
+        cursor.execute(operation, (prefix.toUri(),))
+        result = cursor.fetchone()
+        deviceId = result[0]
+        self._database.commit()
+        cursor.close()
+        return deviceId
+    
+    def updateDevice(self, prefix, newDeviceProfile = None , newSeed = None , newcConfigurationToken = None ):
+        """
+        update specifided device 
+        :param Name prefix 
+        :param DeviceProfile newDeviceProfile
+        :param HMACKey newSeed
+        :param newConfigurationToken
+        :return id of the device if successful, 0 if device not found, otherwise -1
+        :rtype int
+        """
+        raise RuntimeError("getConfigurationToken is not implemented")
+
+    def updateOneColumnOfDevice(self, prefix, columnName, newColumnValue):
+        """
+        update the value of a specified column of a specified device
+        :param Name prefix: the device prefix
+        :param str columnName: column to be updated 
+        :param newColumnValue: new column value
+        :return id of the device if successful. 0 if device not found, otherwise -1
+        :rtype int
+        """
+        result  = -1
+        if not self.doesDeviceExist(prefix):
+            return 0
+        
+        updateDevice = "UPDATE Device Set " + columnName + "=? WHERE prefix=?"
+        #print updateDevice       
+        cursor = self._database.cursor()
+        #print  newColumnValue
+        cursor.execute(updateDevice, (newColumnValue,prefix.toUri()))
+        self._database.commit()
+        result = cursor.lastrowid
+        cursor.close()
+        return result
+
+    def doesCommandExist(self, deviceId, commandName):
+        """
+        check if the specified command already exists.
+       
+        :param Integer device_id: the device id
+        :return True if the command exists, otherwise False
+        :rtype: bool
+        """
+        result = False
+
+        cursor = self._database.cursor()
+        cursor.execute("SELECT count(*) FROM Command WHERE device_id =? AND name = ?", (deviceId, commandName))
+        (count,) = cursor.fetchone()
+        if count > 0:
+            result = True
+        
+        cursor.close()
+        return result
+    
+    def addCommand(self, deviceId, name, commandToken):
+        """ 
+        Add a new command to the Command table, do nothing if the device already exists
+        
+        :param int deviceId: the device id to which the command belongs to
+        :param str name: the command name
+        :param HMACKey commandToken: the command token
+        :return the command id if it's added successfully, 0 if the command already exists, otherwise -1
+        :rtype: int
+        """
+        result = -1
+        data = (deviceId,
+               name,
+               commandToken.getName(),
+               commandToken.getSequence(),
+               commandToken.getCounter(),
+               commandToken.getKey()
+               )
+
+        #check if the command already exists, if yes return 0
+        if self.doesCommandExist(deviceId, name):
+            return 0
+
+        insertCommand = (
+            "INSERT INTO Command(device_id, name, command_token_name, command_token_sequence, command_token_counter, command_token)"
+            "VALUES(?,?,?,?,?,?)"
+        )
+        cursor = self._database.cursor()
+        cursor.execute(insertCommand, data)
+        self._database.commit()
+        result = cursor.lastrowid
+        cursor.close()
+        return result
+    
+    def deleteCommand(self, deviceId, name = None):
+        """
+        delete specified commands.
+        :param str name: The command name, if None, delete all commands of the device
+        :param int deviceId: device id of the command
+        :return: 1 if successful, 0 if no command to delete, otherwise -1.
+        :rtype: int
+        """
+        result = -1
+        if not name == None: 
+            if not self.doesCommandExist(deviceId, name):
+                return 0
+            cursor = self._database.cursor()
+            deleteCommand = "DELETE FROM Command WHERE device_id=? AND name=?"
+            cursor.execute(deleteCommand, (deviceId, name))
+            self._database.commit()
+            cursor.close()
+            return 1
+        else: 
+            selectOperation = "SELECT count(*) FROM Command WHERE deviceId=?"
+            cursor = self._database.cursor()
+            cursor.execute(selectOperation, (deviceId,))
+            (count,) = cursor.fetchone()
+            if not count > 0:
+                return 0
+            deleteCommand = "DELETE FROM Command WHERE deviceId=?"
+            cursor.execute(deleteCommand, (deviceId))
+            self._database.commit()
+            cursor.close()
+            return 1
+        
+        return result
+      
+    def getCommandsOfDevice(self, deviceId):
+        """
+        get all the commands of a specified device
+        :param int deviceId: device id of the command
+        :return command name list if any commands exist, otherwise None
+
+        """
+        operation = "SELECT name FROM Command WHERE device_id = ?"
+        #operation2 = "SELECT count(*) FROM Command WHERE device_id = ? "
+        cursor = self._database.cursor()
+        #cursor.execute(operation2,(deviceId,))
+        #(count,) = cursor.fetchone
+
+        #if not count > 0:
+            #return None
+        cursor.execute(operation, (deviceId,))
+        result = cursor.fetchall()                    
+        print result
+        commandList = []
+        if result == None:
+           return commandList 
+        else:  
+           for row in result:
+               commandList.append(row[0]) 
+    
+        return commandList
+
+    def getCommandToken(self, deviceId, commandName):
+        """
+        get command token of a specified command 
+        :param int deviceId: device id of the command
+        :param str commandName: device name of the command
+        :return command token if command existsm, otherwise None
+        :rtype HMACKey
+        """
+        operation = "SELECT command_token_sequence, command_token_counter,command_token, command_token_name FROM Command WHERE device_id = ? AND name = ?"
+        cursor = self._database.cursor()
+        cursor.execute(operation, (deviceId, commandName))
+        result = cursor.fetchone()
+        if result == None:
+            return None
+        else:
+            commandToken = HMACKey(result[0], result[1], result[2], result[3])
+        return commandToken
+  
+    def doesServiceProfileExist(self, deviceId, serviceProfileName):
+        """
+        check if the specified service profile already exists.
+       
+        :param Integer device_id: the device id
+        :param str serviceProfileName: name of the service profile 
+        :return True if the service profile exists, otherwise False
+        :rtype: bool
+        """
+        result = False
+
+        cursor = self._database.cursor()
+        cursor.execute("SELECT count(*) FROM ServiceProfile WHERE device_id =? AND name = ?", (deviceId, serviceProfileName))
+        (count,) = cursor.fetchone()
+        if count > 0:
+            result = True
+
+        cursor.close()
+        return result
+
+    def addServiceProfile(self, deviceId, name):
+        """ 
+        Add a new command to the ServiceProfile table, do nothing if the device already exists
+        
+        :param int deviceId: the device id to which the command belongs to
+        :param str name: the service profile name
+        :return the service profile id if it's added successfully, 0 if the service profile already exists, otherwise -1
+        :rtype: int
+        """
+        result = -1
+        data = (deviceId,
+               name,
+               )
+
+        #check if the service profile already exists, if yes return 0
+        if self.doesServiceProfileExist(deviceId, name):
+            return 0
+
+        insertCommand = (
+            "INSERT INTO ServiceProfile(device_id, name)"
+            "VALUES(?,?)"
+        )
+        cursor = self._database.cursor()
+        cursor.execute(insertCommand, data)
+        self._database.commit()
+        result = cursor.lastrowid
+        cursor.close()
+        return result
+
+    def deleteServiceProfile(self, deviceId, name = None):
+        """
+        delete specified service profile.
+        :param str name: The command name, if None, delete all profiless of the device
+        :param int deviceId: device id 
+        :return: 1 if successful, 0 if no service profile to delete, otherwise -1.
+        :rtype: int
+        """
+        result = -1
+        if not name == None:
+            if not self.doesServiceProfileExist(deviceId, name):
+                return 0
+            cursor = self._database.cursor()
+            deleteProfile = "DELETE FROM ServiceProfile WHERE device_id=? AND name=?"
+            cursor.execute(deleteProfile, (deviceId, name))
+            self._database.commit()
+            cursor.close()
+            return 1
+        else:
+            selectOperation = "SELECT count(*) FROM ServiceProfile WHERE deviceId=?"
+            cursor = self._database.cursor()
+            cursor.execute(selectOperation, (deviceId,))
+            (count,) = cursor.fetchone()
+            if not count > 0:
+                return 0
+            deleteProfile = "DELETE FROM ServiceProfile WHERE deviceId=?"
+            cursor.execute(deleteProfile, (deviceId))
+            self._database.commit()
+            cursor.close()
+            return 1
+
+        return result
+
+    def getServiceProfilesOfDevice(self, deviceId):
+        """
+        get all the service profiles of a specified device
+        :param int deviceId: device id of the command
+        :return service profiles name list if any service profiles exist, otherwise None
+        """
+        operation = "SELECT name FROM ServiceProfile WHERE device_id = ?"
+        cursor = self._database.cursor()
+        cursor.execute(operation, (deviceId,))
+        result = cursor.fetchall()
+        #print result
+        serviceProfileList = []
+        if not result == None:
+            for row in result:
+                serviceProfileList.append(row[0])
+        return serviceProfileList
+
+
diff --git a/fill_database_for_test.py b/fill_database_for_test.py
new file mode 100644
index 0000000..6ffc096
--- /dev/null
+++ b/fill_database_for_test.py
@@ -0,0 +1,81 @@
+
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU General Public License is in the file COPYING.
+
+import os.path
+from pyndn import Name
+from device_user_access_storage import DeviceUserAccessStorage
+from device_profile import DeviceProfile
+from hmac_key import HMACKey
+
+
+class FillDatabaseForTest(object):
+    def __init__(self, count = None):
+        if count == None:
+            self.count = 1
+        else:
+            self.count = count 
+        if not "HOME" in os.environ:
+            home = '.'
+        else:
+            home = os.environ["HOME"]
+
+        dbDirectory = os.path.join(home, '.ndn')
+        self.databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+
+        if os.path.isfile(self.databaseFilePath):
+            os.remove(self.databaseFilePath)
+
+        self.storage = DeviceUserAccessStorage()
+
+    def fillDatabase(self):
+        count = self.count
+        prefixStrBase = 'home/sensor/LED/'
+        serviceProfileNameBase = '/standard/sensor/simple-LED-control/v'
+        commandNameBase = 'turn_on'
+        commandNameBase2 = 'turn_off'
+        for i in range(1, count+1):
+             prefixStr = prefixStrBase + str(i)
+             self.add_a_default_device(prefixStr)
+             name = Name(prefixStr)
+             deviceId = self.storage.getDeviceId(name)
+             serviceProfileName = serviceProfileNameBase + str(i)
+             self.storage.addServiceProfile(deviceId, serviceProfileName)
+
+             commandName = commandNameBase + str(i)
+             commandName2 = commandNameBase2 + str(i)
+             commandToken = self.create_a_default_key(commandName)
+             commandToken2 = self.create_a_default_key(commandName2)
+             self.storage.addCommand(deviceId, commandName, commandToken)
+             self.storage.addCommand(deviceId,commandName2,commandToken2)
+
+    def add_a_default_device(self, prefixStr):
+        name = Name(prefixStr)
+        profile = DeviceProfile(prefix = name)
+        seed = self.create_a_default_key('')
+        configurationToken = self.create_a_default_key()
+        self.storage.addDevice(profile, seed, configurationToken)
+
+    def create_a_default_key(self, keyName = None):
+        keyContent = 'this is key content'
+        seed = HMACKey(0,0, keyContent, keyName)
+        return seed
+
+test = FillDatabaseForTest(8)
+test.fillDatabase()
diff --git a/hmac_key.py b/hmac_key.py
new file mode 100644
index 0000000..98d1b51
--- /dev/null
+++ b/hmac_key.py
@@ -0,0 +1,40 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com> 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU General Public License is in the file COPYING.
+
+class HMACKey(object):
+    """
+    HMACKey implements the  HMAC symmetric key
+    """
+    def __init__(self, sequence, counter, key, name = None):
+        self._name = name
+        self._sequence = sequence
+        self._counter = counter
+        self._key = key
+
+    def getName(self):
+        return self._name
+  
+    def getSequence(self):
+        return self._sequence
+ 
+    def getCounter(self):
+        return self._counter
+   
+    def getKey(self):
+        return self._key
+        
diff --git a/show_devices.py b/show_devices.py
new file mode 100644
index 0000000..924ba3a
--- /dev/null
+++ b/show_devices.py
@@ -0,0 +1,63 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014-2015 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU Lesser General Public License is in the file COPYING.
+
+import sys
+import os
+import sqlite3
+from device_user_access_storage import DeviceUserAccessStorage
+def showDefault(databaseFilePath = None):
+    if databaseFilePath == None or databaseFilePath == "":
+        if not "HOME" in os.environ:
+            home = '.'
+        else:
+            home = os.environ["HOME"]
+
+        dbDirectory = os.path.join(home, '.ndn')
+        if not os.path.exists(dbDirectory):
+            os.makedirs(dbDirectory)
+
+        databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+
+        database =  sqlite3.connect(databaseFilePath)
+        storage = DeviceUserAccessStorage(databaseFilePath)
+        cursor = database.cursor()
+        cursor.execute("SELECT id, prefix  FROM Device")
+        print 'id:     prefix:                  Commands:                   ServiceProfile:'
+        for row in cursor.fetchall():
+            commandResult = storage.getCommandsOfDevice(row[0])
+            commandStr =''
+            for command in commandResult:
+                commandStr += command[0] + ';'
+            profileResult = storage.getServiceProfilesOfDevice(row[0])
+            profileStr=''
+            for profile in profileResult:
+                profileStr = profile[0] +';'
+            print '%d    %s    %s    %s' %(row[0], row[1], commandStr, profileStr ) 
+            
+        cursor.close()
+
+
+
+if len(sys.argv) < 2:
+    showDefault()
+     
+else:
+    raise RuntimeError("options is not implemented yet") 
+    
+
diff --git a/tests/test_device_profile.py b/tests/test_device_profile.py
index 52986ce..906d0d6 100644
--- a/tests/test_device_profile.py
+++ b/tests/test_device_profile.py
@@ -1,4 +1,5 @@
 from device_profile import DeviceProfile
+from pyndn import Name
 
 def readProfile(profile):
     print '   prefix: ', profile.getPrefix()
@@ -19,7 +20,7 @@
     readProfile(profile)
     
     print "set prefix to '/home/sensor/LED/23'..."
-    profile.setPrefix('/home/sensor/LED/23')
+    profile.setPrefix(Name('/home/sensor/LED/23'))
 
     print "set location to 'Alice's bedroom'..."
     profile.setLocation('Alice\'s bedroom')
diff --git a/tests/test_manager.py b/tests/test_manager.py
new file mode 100644
index 0000000..710aa79
--- /dev/null
+++ b/tests/test_manager.py
@@ -0,0 +1,113 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU General Public License is in the file COPYING.
+
+import unittest as ut
+import os.path
+from pyndn import Name
+from device_user_access_manager import DeviceUserAccessManager
+from device_profile import DeviceProfile
+from hmac_key import HMACKey
+
+class TestManagerMethods(ut.TestCase):
+    def setUp(self):
+        if not "HOME" in os.environ:
+            home = '.'
+        else:
+            home = os.environ["HOME"]
+
+        dbDirectory = os.path.join(home, '.ndn')
+        self.databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+                
+        if os.path.isfile(self.databaseFilePath):
+            os.remove(self.databaseFilePath)
+        self.manager = DeviceUserAccessManager()
+
+    def tearDown(self):
+        pass
+
+    def test_01_manager_constructor(self):
+        print("test")
+        if not "HOME" in os.environ:
+            home = '.'
+        else:
+            home = os.environ["HOME"]
+
+        dbDirectory = os.path.join(home, '.ndn')
+        self.databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+        self.manager = DeviceUserAccessManager()
+        self.assertTrue(os.path.isfile(self.databaseFilePath), 'fail to create database file')
+
+    def test_02_methods(self):
+        #create device
+        prefixStr = '/home/sensor/LED/1'
+        name = Name(prefixStr)
+        profile = DeviceProfile(prefix = name)
+        location = 'living_room'
+        profile.setLocation(location)
+        serviceProfileList  = ['/standard/sensor/simple-camera-control/v0', '/standard/sensor/simple-motionsensor-control/v0']
+        profile.setServiceProfile(serviceProfileList)
+        keyContent = 'this is key content'
+        seedName = 'led1'
+        seed =  HMACKey( 0, 0 ,keyContent, seedName)
+        configurationToken =  HMACKey(0, 0, keyContent)
+        commandTokenName1 = 'commandToken1'
+        commandTokenName2 = 'commandToken2'
+        commandToken = HMACKey(0,0, keyContent, commandTokenName1)
+        commandToken2 = HMACKey(0,0,keyContent, commandTokenName2)
+        commandName1 = 'turn_on'
+        commandName2 = 'turn_off'
+        commandTuple = (commandName1, commandToken)
+        commandTuple2 = (commandName2, commandToken2)
+        commandList =  [commandTuple, commandTuple2]
+        result = self.manager.createDevice(profile, seed, configurationToken, commandList)
+        self.assertTrue(result, 'fail to create device')
+       
+        #getDeviceProfile()
+        deviceProfile = self.manager.getDeviceProfile(name)
+        self.assertTrue(deviceProfile.getLocation() == location, 'wrong location in device profile ')
+        self.assertTrue(deviceProfile.getServiceProfileList() == serviceProfileList, 'wrong service profile list in device profile' )
+     
+        #getSeed()
+        seed = self.manager.getSeed(name)
+        self.assertTrue(seed.getName() == seedName, 'wrong seed name')
+        
+        #getConfigurationToken()
+        configurationToken = self.manager.getConfigurationToken(name)
+        self.assertTrue(configurationToken.getKey()== keyContent, 'wrong configration token')
+       
+        #getCommandToken()
+        commandToken1 = self.manager.getCommandToken(name, commandName1 )
+        commandToken2 = self.manager.getCommandToken(name, commandName2)
+        self.assertTrue(commandToken1.getName() == commandTokenName1, 'wrong commandToken')
+        self.assertTrue(commandToken2.getName() == commandTokenName2, 'wrong commandToken')
+ 
+        #getCommandsOfDevice()
+        commandNameList = self.manager.getCommandsOfDevice(name)
+        self.assertTrue(commandName1 in commandNameList, 'command:' + commandName1 + ' not found')
+        self.assertTrue(commandName2 in commandNameList, 'command:' + commandName2 + ' not found')
+
+        #getServiceProfilesOfDevice()
+        serviceProfileListReturned = self.manager.getServiceProfilesOfDevice(name)
+        self.assertTrue(serviceProfileList[0] in serviceProfileListReturned, 'service profile:' + serviceProfileList[0] + ' not found')
+        self.assertTrue(serviceProfileList[1] in serviceProfileListReturned, 'service profile:' + serviceProfileList[1] + ' not found')
+
+
+if __name__ == '__main__':
+    ut.main(verbosity=2)
+
diff --git a/tests/test_storage.py b/tests/test_storage.py
new file mode 100644
index 0000000..dbdce7f
--- /dev/null
+++ b/tests/test_storage.py
@@ -0,0 +1,338 @@
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Regents of the University of California.
+# Author: Weiwei Liu <summerwing10@gmail.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# A copy of the GNU General Public License is in the file COPYING.
+
+import unittest as ut
+import os.path
+from pyndn import Name
+from device_user_access_storage import DeviceUserAccessStorage
+from device_profile import DeviceProfile
+from hmac_key import HMACKey
+
+class TestStorageMethods(ut.TestCase):
+    def setUp(self):
+        if not "HOME" in os.environ:
+            home = '.'
+        else:
+            home = os.environ["HOME"]
+
+        dbDirectory = os.path.join(home, '.ndn')
+        self.databaseFilePath = os.path.join(dbDirectory, 'ndnhome-controller.db')
+
+        if os.path.isfile(self.databaseFilePath):
+            os.remove(self.databaseFilePath)
+        
+        self.storage = DeviceUserAccessStorage()
+
+    def tearDown(self):
+        pass
+    
+    def test_01_storage_constructor(self):
+        
+        self.assertTrue(os.path.isfile(self.databaseFilePath), 'fail to create database file')        
+        self.assertTrue(self.storage.doesTableExist("Device"), "Device table doesn't exist")  
+        self.assertTrue(self.storage.doesTableExist("Command"), "Command table doesn't exist")
+        self.assertTrue(self.storage.doesTableExist("ServiceProfile"), "ServiceProfile table doesn't exist") 
+        #test constructor with file path HOME/.ndn/homedb 
+        home = os.environ["HOME"]
+        dir = os.path.join(home, '.ndn')
+        dbdir = os.path.join(dir, 'homedb')
+        
+        if not os.path.exists(dbdir):
+                os.makedirs(dbdir)
+       
+        filePath = os.path.join(dbdir, 'ndnhome-controller.db')
+        storage2 = DeviceUserAccessStorage(filePath)
+        self.assertTrue(os.path.isfile(filePath), 'fail to create database file' )
+        os.remove(filePath)
+    
+    def test_02_entry_existence_methods(self):
+        # test the existence of non-existed device (prefix = /home/sensor/LED/1)
+        prefixStr = '/home/sensor/LED/1'
+        name = Name(prefixStr)        
+        profile = DeviceProfile(prefix = name)
+        keyContent = 'this is key content'
+        seed =  HMACKey( 0, 0 ,keyContent, 'led1')
+        configurationToken =  HMACKey(0, 0, keyContent)
+        result = self.storage.doesDeviceExist(name)
+        self.assertFalse(result, "device with prefix '" + prefixStr +  "' shouldn't exist")
+        
+        #test existence of an existed device 
+        self.storage.addDevice(profile, seed, configurationToken)
+        result = self.storage.doesDeviceExist(name)
+        self.assertTrue(result, "device with prefix '" + prefixStr +"' should exist")
+        
+        #test existence of a non-existed command
+        deviceId = 1 
+        commandName = 'turn_on'
+        result = self.storage.doesCommandExist(deviceId, commandName)
+        self.assertFalse(result, "Command : 'device_id =" + str(deviceId) + ", name = " + commandName + "' shouldn't exist" ) 
+       
+        #test existence of a existed command
+        deviceId = self.storage.getDeviceId(name)
+        commandName = 'turn_on'
+        commandToken = self.create_a_default_key('turn_on')
+        self.storage.addCommand(deviceId, commandName, commandToken)
+        result = self.storage.doesCommandExist(deviceId, commandName)
+        self.assertTrue(result, "Command : 'device_id =" + str(deviceId) + ", name = " + commandName + "' should exist" )
+           
+        #test existence of a non-existed service profile
+        deviceId = 1
+        serviceProfileName = '/standard/sensor/simple-LED-control/v0'
+        
+        result = self.storage.doesServiceProfileExist(deviceId, serviceProfileName)
+        self.assertFalse(result, "service profile : 'device_id =" + str(deviceId) + ", name = " + serviceProfileName + "' shouldn't exist" )
+
+        #test existence of a existed service profile
+        deviceId = self.storage.getDeviceId(name)
+        serviceProfileName = '/standard/sensor/simple-LED-control/v0'
+
+        self.storage.addServiceProfile(deviceId, serviceProfileName)
+        result = self.storage.doesServiceProfileExist(deviceId, serviceProfileName)
+        self.assertTrue(result, "service profile : 'device_id =" + str(deviceId) + ", name = " + serviceProfileName + "' should exist" )
+ 
+
+    def test_03_add_device(self):
+        prefixStr = '/home/sensor/LED/1'
+        name = Name(prefixStr)
+        profile = DeviceProfile(prefix = name)
+        keyContent = 'this is key content'
+        seed =  HMACKey( 0, 0 ,keyContent, 'led1')
+        configurationToken =  HMACKey(0, 0, keyContent)
+        
+        result = self.storage.addDevice(profile, seed, configurationToken)
+
+        self.assertTrue(result > 0, 'fail to add device entry')             
+        self.assertTrue(self.storage.doesDeviceExist(name), "device doesn't exist in table")        
+        row = self.storage.getDeviceEntry(name)
+        self.assertTrue(row[1] == prefixStr, "column prefix has incorrect value")
+        #print row[2:7]
+        self.assertTrue(row[2:8] == (None, None, None, None, None, None), "column 2-7 have incorrect values ")
+        self.assertTrue(row[8] == 'led1', 'colum seed_name has incorrect values')
+        self.assertTrue(row[9:12] == (0,0,keyContent), 'colum seed_sequence or seed_counter has incorrect value')
+        self.assertTrue(row[12:] == (keyContent,0,0), 'one or more configuration token columns have incoorect value')
+        #try to insert the same device
+        result = self.storage.addDevice(profile, seed, configurationToken)
+        self.assertTrue(result == 0, 'unexpected result when trying to insert an already existed device')
+
+    def test_04_delete_device(self):
+        name = Name('/home/sensor/LED/1')
+        profile = DeviceProfile(prefix = name)
+        seed =  HMACKey( 0, 0 ,'this is key content', 'led1')
+        configurationToken =  HMACKey(0, 0, 'this is key content')
+        self.storage.addDevice(profile, seed, configurationToken)
+        self.assertTrue(self.storage.doesDeviceExist(name), "device doesn't exist in table")
+        result = self.storage.deleteDevice(name)
+        #print 'result : %d' %(result)
+        self.assertTrue(result == 1, 'fail to delete device')
+
+    def test_05_get_deviceid(self):
+        name = Name('/home/sensor/LED/1')
+        profile = DeviceProfile(prefix = name)
+        seed =  HMACKey( 0, 0 ,'this is key content', 'led1')
+        configurationToken =  HMACKey(0, 0, 'this is key content')
+        self.storage.addDevice(profile, seed, configurationToken)
+        deviceId = self.storage.getDeviceId(name)
+        self.assertTrue(deviceId==1, "get a wrong device id")
+        
+        name2 = Name('/home/sensor/LED/2')
+        profile = DeviceProfile(prefix = name2)
+        seed =  HMACKey( 0, 0 ,'this is key content', 'led1')
+        configurationToken =  HMACKey(0, 0, 'this is key content')
+        self.storage.addDevice(profile, seed, configurationToken)
+        deviceId2 = self.storage.getDeviceId(name2)
+        self.assertTrue(deviceId2 == 2, "get a wrong device id")
+     
+    def test_06_update_device(self):
+        prefixStr = '/home/sensor/LED/1' 
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+
+        #update column prefix of device 
+        newPrefixStr = '/home/sensor/LED/2'
+        newSeedName = 'led2'
+        self.storage.updateOneColumnOfDevice(name, 'seed_name', newSeedName)
+        row = self.storage.getDeviceEntry(name)
+        self.assertTrue(row[8] == newSeedName, "fail to update column: seed_name")
+
+        self.storage.updateOneColumnOfDevice(name, 'prefix', newPrefixStr) 
+        row = self.storage.getDeviceEntry(Name(newPrefixStr))
+       
+        self.assertTrue(row[1] == newPrefixStr, "fail to update coumn: prefix")     
+  
+    def test_07_add_command(self):
+        prefixStr = '/home/sensor/LED/2'
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+        deviceId = self.storage.getDeviceId(name)
+        commandName = 'turn_on'
+        commandToken = self.create_a_default_key('turn_on')
+        self.storage.addCommand(deviceId, commandName, commandToken)        
+        result = self.storage.doesCommandExist(deviceId, commandName)
+        self.assertTrue(result == True, "fail to add command")
+
+    def test_08_delete_command(self):
+        prefixStr1 = 'home/sensor/LED/1'
+        name1 = Name(prefixStr1)
+        self.add_a_default_device(prefixStr1)     
+        deviceId = self.storage.getDeviceId(name1)
+        commandName = 'turn_on'
+        commandToken = self.create_a_default_key(commandName)
+        self.storage.addCommand(deviceId, commandName, commandToken)
+        self.assertTrue(self.storage.doesCommandExist(deviceId, commandName), 'before delete, the command should exist')
+        self.storage.deleteCommand(deviceId, commandName)
+        self.assertFalse(self.storage.doesCommandExist(deviceId, commandName), "after delete, the command shouldn't exist")
+        
+    def test_09_get_commands_of_device(self):
+        #test with an empty table
+        deviceId = 2
+        result = self.storage.getCommandsOfDevice(deviceId) 
+        print result
+        self.assertTrue(not result, "there should be no command found")
+      
+        #test with a device has two commands
+        prefixStr = 'home/sensor/LED/1' 
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+        deviceId = self.storage.getDeviceId(name)
+        commandName = 'turn_on'
+        commandName2 = 'turn_off'
+        commandToken = self.create_a_default_key(commandName)
+        commandToken2 = self.create_a_default_key(commandName2)
+        self.storage.addCommand(deviceId, commandName, commandToken)
+        self.storage.addCommand(deviceId, commandName2, commandToken)
+        result = self.storage.getCommandsOfDevice(deviceId)
+        print 'result %s' %(result)
+       
+    def test_10_add_service_profile(self):
+        prefixStr = '/home/sensor/LED/2'
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+        deviceId = self.storage.getDeviceId(name)
+
+        serviceProfileName = '/standard/sensor/simple-LED-control/v0'
+        self.storage.addServiceProfile(deviceId, serviceProfileName)
+        result = self.storage.doesServiceProfileExist(deviceId, serviceProfileName)
+        self.assertTrue(result == True, "fail to add service profile")
+
+    def test_11_delete_service_profile(self):
+        prefixStr1 = 'home/sensor/LED/1'
+        name1 = Name(prefixStr1)
+        self.add_a_default_device(prefixStr1)
+        deviceId = self.storage.getDeviceId(name1)
+        serviceProfileName = '/standard/sensor/simple-LED-control/v0'        
+
+        self.storage.addServiceProfile(deviceId, serviceProfileName)
+        self.assertTrue(self.storage.doesServiceProfileExist(deviceId, serviceProfileName), 'before delete, the service profile should exist')
+        self.storage.deleteServiceProfile(deviceId, serviceProfileName)
+        self.assertFalse(self.storage.doesServiceProfileExist(deviceId, serviceProfileName), "after delete, the service profile shouldn't exist")
+
+    def test_12_get_service_profiles_of_device(self):
+        #test with an empty table
+        deviceId = 2
+        result = self.storage.getServiceProfilesOfDevice(deviceId)
+        print result
+        self.assertTrue(not result, "there should be no command found")
+
+        #test with a device has two commands
+        prefixStr = 'home/sensor/LED/1'
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+        deviceId = self.storage.getDeviceId(name)
+        serviceProfileName = '/standard/sensor/simple-LED-control/v0'
+        serviceProfileName2 = '/standard/sensor/simple-LED-control/v2'
+        self.storage.addServiceProfile(deviceId, serviceProfileName)
+        self.storage.addServiceProfile(deviceId, serviceProfileName2)
+        result = self.storage.getServiceProfilesOfDevice(deviceId)
+        print 'result %s' %(result)
+
+    def test_13_get_device_profile_from_device(self):
+        #test with a non exisited device prefix
+        prefixStr = '/home/sensor/LED/2'
+        name = Name(prefixStr)
+        deviceProfile = self.storage.getDeviceProfileFromDevice(name)
+        self.assertTrue(deviceProfile == None, "no device profile should be return with an non-existed prefix ")
+       
+        #test with a existed device prefix
+        profile = DeviceProfile(prefix = name, location = 'bedroom', category = 'sensors')
+        seed = self.create_a_default_key('led1')
+        configurationToken = self.create_a_default_key()
+        self.storage.addDevice(profile, seed, configurationToken)
+
+        deviceProfile  = self.storage.getDeviceProfileFromDevice(name)
+        
+        self.assertTrue(deviceProfile.getLocation() == 'bedroom', 'wrong field: location')
+        self.assertTrue(deviceProfile.getCategory() == 'sensors', 'wrong field: category')
+
+    def test_14_get_seed_and_get_configuration_token(self):
+        #test with a non existed device prefix
+        prefixStr = '/home/sensor/LED/2'
+        name = Name(prefixStr)
+        seed = self.storage.getSeed(name)
+        configurationToken = self.storage.getConfigurationToken(name)
+        self.assertTrue(seed == None, "no seed should be returned with an non-existed prefix ")
+        self.assertTrue(configurationToken == None, "no configuration token should be returned with an non-existed prefix ")
+        
+        #test with an existed device prefix
+        profile= DeviceProfile(prefix = name)
+        seed = self.create_a_default_key('led2')
+        configurationToken = self.create_a_default_key()
+        self.storage.addDevice(profile, seed, configurationToken)
+        seed = self.storage.getSeed(name)
+        configurationToken = self.storage.getConfigurationToken(name)
+        keyContent = 'this is key content'
+        self.assertTrue(seed.getKey() == keyContent, 'key content of seed  is incorrect')
+        self.assertTrue(configurationToken.getKey() == keyContent, 'key content of configuration token is incorrect')
+        self.assertTrue(seed.getName() == 'led2', 'name of seed is incorrect')
+        self.assertTrue(configurationToken.getName() == None, 'name of configuration token is incorrect')
+  
+    def test_15_get_Command_Token(self):
+        #test with a non existed command
+        deviceId = 5
+        commandName = "turn_on"
+        commandToken =  self.storage.getCommandToken(deviceId, commandName)
+        self.assertTrue(commandToken == None,  'no commandToken should be returned with an non-existed command')
+        
+        #test with an existed command
+        prefixStr = 'home/sensor/LED/1'
+        name = Name(prefixStr)
+        self.add_a_default_device(prefixStr)
+        deviceId = self.storage.getDeviceId(name)
+        commandToken = self.create_a_default_key(commandName)
+        self.storage.addCommand(deviceId, commandName, commandToken)
+
+        commandTokenReturned =  self.storage.getCommandToken(deviceId, commandName)
+        #print commandTokenReturned.getName()
+        self.assertTrue(commandTokenReturned.getName() == commandName,  'wrong command token')
+
+    def add_a_default_device(self, prefixStr):
+        name = Name(prefixStr)
+        profile = DeviceProfile(prefix = name)
+        seed = self.create_a_default_key('led1')
+        configurationToken = self.create_a_default_key()
+        self.storage.addDevice(profile, seed, configurationToken)
+    
+    def create_a_default_key(self, keyName = None):
+        keyContent = 'this is key content'
+        seed = HMACKey(0,0, keyContent, keyName)
+        return seed
+         
+if __name__ == '__main__':
+    ut.main(verbosity=2)
+
+