blob: b93bc5d3bbed1f42d0055bcf222272654a096eb7 [file] [log] [blame]
philoL5ddbce12015-04-20 23:35:34 -07001# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2#
3# Copyright (C) 2014 Regents of the University of California.
Teng Liang29aa6012015-06-28 15:31:01 -07004# Author: Teng Liang <philoliang2011@gmail.com>
philoL5ddbce12015-04-20 23:35:34 -07005#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Lesser General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18# A copy of the GNU General Public License is in the file COPYING.
Teng Liang29aa6012015-06-28 15:31:01 -070019
Teng Liang938be582015-07-15 16:25:26 -070020"""
21This module defines BaseNode class, which contains methods/attributes common to both end device and controller.
22"""
23
philoL5ddbce12015-04-20 23:35:34 -070024import logging
25import time
26import sys
27import random
28
Teng Liang938be582015-07-15 16:25:26 -070029from pyndn import Name, Face, Interest, Data
30from pyndn.threadsafe_face import ThreadsafeFace
philoL5ddbce12015-04-20 23:35:34 -070031from pyndn.security import KeyChain
32from pyndn.security.identity import IdentityManager, BasicIdentityStorage
Teng Liang938be582015-07-15 16:25:26 -070033from pyndn.security.policy import NoVerifyPolicyManager, ConfigPolicyManager
philoL5ddbce12015-04-20 23:35:34 -070034from pyndn.security.security_exception import SecurityException
philoL5ddbce12015-04-20 23:35:34 -070035
Teng Liang938be582015-07-15 16:25:26 -070036from access_control_manager import AccessControlManager
philoL5ddbce12015-04-20 23:35:34 -070037
38try:
39 import asyncio
40except ImportError:
41 import trollius as asyncio
42
philoL5ddbce12015-04-20 23:35:34 -070043
44class BaseNode(object):
Teng Liang938be582015-07-15 16:25:26 -070045
Teng Lianga0b49372015-05-15 05:30:27 -070046 def __init__(self,configFileName):
philoL5ddbce12015-04-20 23:35:34 -070047 """
48 Initialize the network and security classes for the node
49 """
50 super(BaseNode, self).__init__()
51
Teng Lianga0b49372015-05-15 05:30:27 -070052
philoL5ddbce12015-04-20 23:35:34 -070053 self._identityStorage = BasicIdentityStorage()
54 self._identityManager = IdentityManager(self._identityStorage)
philoL5ddbce12015-04-20 23:35:34 -070055
Teng Liang938be582015-07-15 16:25:26 -070056 if configFileName:
57 self._policyManager = ConfigPolicyManager(configFileName)
58 else:
59 self._policyManager = NoVerifyPolicyManager()
60
Teng Lianga0b49372015-05-15 05:30:27 -070061 self._keyChain = KeyChain(self._identityManager,self._policyManager)
philoL5ddbce12015-04-20 23:35:34 -070062
63 self._registrationFailures = 0
64 self._prepareLogging()
65
66 self._setupComplete = False
67 self._instanceSerial = None
68
Teng Liang50429402015-05-22 16:01:17 -070069 self._bootstrapPrefix = '/home/controller/bootstrap'
Teng Liang938be582015-07-15 16:25:26 -070070 self._serviceProfileList = []
71 self._commmandList = []
72 self._protocolList = []
73
philoL5ddbce12015-04-20 23:35:34 -070074 def getSerial(self):
75 """
76 Since you may wish to run two nodes on a Raspberry Pi, each
77 node will generate a unique serial number each time it starts up.
78 """
79 if self._instanceSerial is None:
80 prefixLen = 4
81 prefix = ''
82 for i in range(prefixLen):
83 prefix += (chr(random.randint(0,0xff)))
Teng Liang4662b372015-05-27 15:48:36 -070084
85 self._instanceSerial = prefix.encode('hex')
philoL5ddbce12015-04-20 23:35:34 -070086 return self._instanceSerial
87
Teng Liang938be582015-07-15 16:25:26 -070088 def addServiceProfiles(self,profiles):
89 pass
90
91 def addServices(self,services):
92 pass
93
94 def addProtocols(self,protocols):
95 pass
96
97
98 def beforeLoopStart(self):
99 """
100 Called before the event loop starts.
101 """
102 pass
103
104
105 def start(self):
106 """
107 Begins the event loop. After this, the node's Face is set up and it can
108 send/receive interests+data
109 """
110 self.log.info("Starting up")
111 self.loop = asyncio.get_event_loop()
112 self.face = ThreadsafeFace(self.loop, 'localhost')
113
114 self._keyChain.setFace(self.face)
115 self.beforeLoopStart()
116
117 self.face.setCommandSigningInfo(self._keyChain, self._keyChain.getDefaultCertificateName())
118
119 try:
120 self.loop.run_forever()
121 except Exception as e:
122 self.log.exception(e, exc_info=True)
123
124 def createACK(self):
125 pass
126
127 def createNACK(self):
128 pass
129
130 def signData(self, data):
131 """
132 Sign the data with our network certificate
133 :param pyndn.Data data: The data to sign
134 """
135 pass
136
137 def sendData(self, data, transport, sign=True):
138 """
139 Reply to an interest with a data packet, optionally signing it.
140 :param pyndn.Data data: The response data packet
141 :param pyndn.Transport transport: The transport to send the data through. This is
142 obtained from an incoming interest handler
143 :param boolean sign: (optional, default=True) Whether the response must be signed.
144 """
145 if sign:
146 self.signData(data)
147 transport.send(data.wireEncode().buf())
148
149 """
150 Failure
151
152 """
153
154 def onRegisterFailed(self, prefix):
155 """
156 Called when the node cannot register its name with the forwarder
157 :param pyndn.Name prefix: The network name that failed registration
158 """
159 if self._registrationFailures < 5:
160 self._registrationFailures += 1
161 self.log.warn("Could not register {}, retry: {}/{}".format(prefix.toUri(), self._registrationFailures, 5))
162 self.face.registerPrefix(self.prefix, self._onCommandReceived, self.onRegisterFailed)
163 else:
164 self.log.critical("Could not register device prefix, ABORTING")
165 self._isStopped = True
166
167 def verificationFailed(self, dataOrInterest):
168 """
169 Called when verification of a data packet or command interest fails.
170 :param pyndn.Data or pyndn.Interest: The packet that could not be verified
171 """
172 self.log.info("Received invalid" + dataOrInterest.getName().toUri())
173
174
175
176
177 """
178 Logging
179 """
180
philoL5ddbce12015-04-20 23:35:34 -0700181 def _prepareLogging(self):
182 self.log = logging.getLogger(str(self.__class__))
183 self.log.setLevel(logging.DEBUG)
Teng Liang4662b372015-05-27 15:48:36 -0700184 logFormat = "%(asctime)-15s %(name)-20s %(funcName)-2s (%(levelname)-2s):\t%(message)s"
philoL5ddbce12015-04-20 23:35:34 -0700185 self._console = logging.StreamHandler()
186 self._console.setFormatter(logging.Formatter(logFormat))
187 self._console.setLevel(logging.INFO)
188 # without this, a lot of ThreadsafeFace errors get swallowed up
189 logging.getLogger("trollius").addHandler(self._console)
190 self.log.addHandler(self._console)
191
192 def setLogLevel(self, level):
193 """
194 Set the log level that will be output to standard error
195 :param level: A log level constant defined in the logging module (e.g. logging.INFO)
196 """
197 self._console.setLevel(level)
198
199 def getLogger(self):
200 """
201 :return: The logger associated with this node
202 :rtype: logging.Logger
203 """
204 return self.log
205
philoL5ddbce12015-04-20 23:35:34 -0700206
philoL5ddbce12015-04-20 23:35:34 -0700207
208 @staticmethod
209 def getDeviceSerial():
210 """
211 Find and return the serial number of the Raspberry Pi. Provided in case
212 you wish to distinguish data from nodes with the same name by serial.
213 :return: The serial number extracted from device information in /proc/cpuinfo
214 :rtype: str
215 """
216 with open('/proc/cpuinfo') as f:
217 for line in f:
218 if line.startswith('Serial'):
Teng Liang938be582015-07-15 16:25:26 -0700219 return line.split(':')[1].strip()